diff --git a/config.yml b/config.yml index e8da1f7..24f3058 100644 --- a/config.yml +++ b/config.yml @@ -69,8 +69,8 @@ theme: - {stop: 1, color: 0x1e90ff} fill: gradient_alpha: - - {stop: 0.2, color: 0x003f7c, alpha: 0.5} - - {stop: 1, color: 0x1e90ff, alpha: 1.0} + - {stop: 0.2, color: 0x316ece, alpha: 0.5} + - {stop: 1, color: 0x8cc7ff, alpha: 1.0} indicator: bg: gradient: diff --git a/drawing/common.lua b/drawing/common.lua index 12364b6..eeb6486 100644 --- a/drawing/common.lua +++ b/drawing/common.lua @@ -1,8 +1,7 @@ -local M = {} - local geom = require 'geom' local format = require 'format' -local theme = require 'theme' +local color = require 'color' +-- local theme = require 'theme' local dial = require 'dial' local rect = require 'rect' local fill_rect = require 'fill_rect' @@ -21,722 +20,672 @@ local style = require 'style' local source = require 'source' local pure = require 'pure' --------------------------------------------------------------------------------- --- constants +local compile_patterns -local FONT = 'Neuropolitical' +compile_patterns = function(patterns) + local r = {} + for k, v in pairs(patterns) do + if type(v) == "number" then + r[k] = color.rgb(v) + elseif v.color ~= nil then + r[k] = color.rgba(v.color, v.alpha) + elseif v.gradient ~= nil then + local p = {} + local g = v.gradient + for i = 1, #g do + local _g = g[i] + p[_g.stop] = _g.color + end + r[k] = color.gradient_rgb(p) + elseif v.gradient_alpha ~= nil then + local p = {} + local g = v.gradient_alpha + for i = 1, #g do + local _g = g[i] + p[_g.stop] = {_g.color, _g.alpha} + end + r[k] = color.gradient_rgba(p) + else + r[k] = compile_patterns(v) + end + end + return r +end -local NORMAL_FONT_SIZE = 13 -local PLOT_LABEL_FONT_SIZE = 8 -local TABLE_FONT_SIZE = 11 -local HEADER_FONT_SIZE = 15 +return function(config) + local M = {} -local HEADER_HEIGHT = 45 -local HEADER_UNDERLINE_CAP = CAIRO_LINE_CAP_ROUND -local HEADER_UNDERLINE_OFFSET = 26 -local HEADER_UNDERLINE_THICKNESS = 3 + local patterns = compile_patterns(config.theme.patterns) -local SEPARATOR_THICKNESS = 1 + ----------------------------------------------------------------------------- + -- constants -local TABLE_BODY_FORMAT = 8 -local TABLE_VERT_PAD = 15 -local TABLE_HORZ_PAD = 5 -local TABLE_HEADER_PAD = 20 -local TABLE_LINE_THICKNESS = 1 + local FONT = config.theme.font -local PLOT_NUM_POINTS = 90 -local PLOT_GRID_X_N = 9 -local PLOT_GRID_Y_N = 4 + local NORMAL_FONT_SIZE = 13 + local PLOT_LABEL_FONT_SIZE = 8 + local TABLE_FONT_SIZE = 11 + local HEADER_FONT_SIZE = 15 -local ARC_WIDTH = 2 + local HEADER_HEIGHT = 45 + local HEADER_UNDERLINE_CAP = CAIRO_LINE_CAP_ROUND + local HEADER_UNDERLINE_OFFSET = 26 + local HEADER_UNDERLINE_THICKNESS = 3 -local DIAL_THETA0 = 90 -local DIAL_THETA1 = 360 + local SEPARATOR_THICKNESS = 1 --------------------------------------------------------------------------------- --- line helper functions + local TABLE_BODY_FORMAT = 8 + local TABLE_VERT_PAD = 15 + local TABLE_HORZ_PAD = 5 + local TABLE_HEADER_PAD = 20 + local TABLE_LINE_THICKNESS = 1 -local _make_horizontal_line = function(x, y, w) + local PLOT_NUM_POINTS = 90 + local PLOT_GRID_X_N = 9 + local PLOT_GRID_Y_N = 4 + + local ARC_WIDTH = 2 + + local DIAL_THETA0 = 90 + local DIAL_THETA1 = 360 + + ----------------------------------------------------------------------------- + -- line helper functions + + local _make_horizontal_line = function(x, y, w) return geom.make_line(geom.make_point(x, y), geom.make_point(x + w, y)) -end + end --------------------------------------------------------------------------------- --- text helper functions + ----------------------------------------------------------------------------- + -- text helper functions -local make_font_spec = function(f, size, bold) + local make_font_spec = function(f, size, bold) return { - family = f, - size = size, - weight = bold and CAIRO_FONT_WEIGHT_BOLD or CAIRO_FONT_WEIGHT_NORMAL, - slant = CAIRO_FONT_WEIGHT_NORMAL, + family = f, + size = size, + weight = bold and CAIRO_FONT_WEIGHT_BOLD or CAIRO_FONT_WEIGHT_NORMAL, + slant = CAIRO_FONT_WEIGHT_NORMAL, } -end + end -local normal_font_spec = make_font_spec(FONT, NORMAL_FONT_SIZE, false) -local label_font_spec = make_font_spec(FONT, PLOT_LABEL_FONT_SIZE, false) + local normal_font_spec = make_font_spec(FONT, NORMAL_FONT_SIZE, false) + local label_font_spec = make_font_spec(FONT, PLOT_LABEL_FONT_SIZE, false) -local _text_row_style = function(x_align, color) - return text.config(normal_font_spec, color, x_align, 'center') -end + local _text_row_style = function(x_align, color) + return text.config(normal_font_spec, color, x_align, 'center') + end -local _left_text_style = _text_row_style('left', theme.INACTIVE_TEXT_FG) -local _right_text_style = _text_row_style('right', theme.PRIMARY_FG) + local _left_text_style = _text_row_style('left', patterns.text.inactive) + local _right_text_style = _text_row_style('right', patterns.text.active) -local _bare_text = function(pt, _text, _style) + local _bare_text = function(pt, _text, _style) return text.make_plain(pt, _text, _style) -end + end -local _left_text = function(pt, _text) + local _left_text = function(pt, _text) return _bare_text(pt, _text, _left_text_style) -end + end -local _right_text = function(pt, _text) + local _right_text = function(pt, _text) return _bare_text(pt, _text, _right_text_style) -end + end --------------------------------------------------------------------------------- --- timeseries helper functions + ----------------------------------------------------------------------------- + -- timeseries helper functions -local _default_grid_config = timeseries.grid_config( + local _default_grid_config = timeseries.grid_config( PLOT_GRID_X_N, PLOT_GRID_Y_N, - theme.PLOT_GRID_FG -) + patterns.plot.grid + ) -local _default_plot_config = timeseries.config( + local _default_plot_config = timeseries.config( PLOT_NUM_POINTS, - theme.PLOT_OUTLINE_FG, - theme.PLOT_FILL_BORDER_PRIMARY, - theme.PLOT_FILL_BG_PRIMARY, + patterns.plot.outline, + patterns.plot.data.border, + patterns.plot.data.fill, _default_grid_config -) + ) -local _format_percent_label = function(_) + local _format_percent_label = function(_) return function(z) return string.format('%i%%', z * 100) end -end + end -local _format_percent_maybe = function(z) + local _format_percent_maybe = function(z) if z == -1 then return 'N/A' else return string.format('%s%%', z) end -end + end -local _percent_label_config = timeseries.label_config( - theme.INACTIVE_TEXT_FG, + local _percent_label_config = timeseries.label_config( + patterns.text.inactive, label_font_spec, _format_percent_label -) + ) -local _make_timeseries = function(x, y, w, h, label_config, update_freq) + local _make_timeseries = function(x, y, w, h, label_config, update_freq) return timeseries.make( - geom.make_box(x, y, w, h), - update_freq, - _default_plot_config, - label_config + geom.make_box(x, y, w, h), + update_freq, + _default_plot_config, + label_config ) -end + end -local _make_tagged_percent_timeseries = function(x, y, w, h, spacing, label, update_freq, _format) + local _make_tagged_percent_timeseries = function(x, y, w, h, spacing, label, update_freq, _format) return { - label = _left_text(geom.make_point(x, y), label), - value = text_threshold.make_formatted( - geom.make_point(x + w, y), - nil, - _right_text_style, - _format, - text_threshold.config(theme.CRITICAL_FG, 80, false) - ), - plot = M.make_percent_timeseries( - x, - y + spacing, - w, - h, - update_freq - ), + label = _left_text(geom.make_point(x, y), label), + value = text_threshold.make_formatted( + geom.make_point(x + w, y), + nil, + _right_text_style, + _format, + text_threshold.config(patterns.text.critical, 80, false) + ), + plot = M.make_percent_timeseries( + x, + y + spacing, + w, + h, + update_freq + ), } -end + end --------------------------------------------------------------------------------- --- scaled timeseries helper functions + ----------------------------------------------------------------------------- + -- scaled timeseries helper functions -local _base_2_scale_data = function(m) + local _base_2_scale_data = function(m) return scaled_timeseries.scaling_parameters(2, m, 0.9) -end + end -local _make_scaled_timeseries = function(x, y, w, h, f, min_domain, update_freq) + local _make_scaled_timeseries = function(x, y, w, h, f, min_domain, update_freq) return scaled_timeseries.make( - geom.make_box(x, y, w, h), - update_freq, - _default_plot_config, - timeseries.label_config(theme.INACTIVE_TEXT_FG, label_font_spec, f), - _base_2_scale_data(min_domain) + geom.make_box(x, y, w, h), + update_freq, + _default_plot_config, + timeseries.label_config(patterns.text.inactive, label_font_spec, f), + _base_2_scale_data(min_domain) ) -end + end --------------------------------------------------------------------------------- --- header + ----------------------------------------------------------------------------- + -- header -M.make_header = function(x, y, w, _text) + M.make_header = function(x, y, w, _text) local bottom_y = y + HEADER_HEIGHT local underline_y = y + HEADER_UNDERLINE_OFFSET return { - text = text.make_plain( - geom.make_point(x, y), - _text, - text.config( - make_font_spec(FONT, HEADER_FONT_SIZE, true), - theme.HEADER_FG, - 'left', - 'top' - ) - ), - bottom_y = bottom_y, - underline = line.make( - _make_horizontal_line(x, underline_y, w), - line.config( - style.line(HEADER_UNDERLINE_THICKNESS, HEADER_UNDERLINE_CAP), - theme.HEADER_FG, - true - ) - ) + text = text.make_plain( + geom.make_point(x, y), + _text, + text.config( + make_font_spec(FONT, HEADER_FONT_SIZE, true), + patterns.header, + 'left', + 'top' + ) + ), + bottom_y = bottom_y, + underline = line.make( + _make_horizontal_line(x, underline_y, w), + line.config( + style.line(HEADER_UNDERLINE_THICKNESS, HEADER_UNDERLINE_CAP), + patterns.header, + true + ) + ) } -end + end -M.draw_header = function(cr, header) + M.draw_header = function(cr, header) text.draw(header.text, cr) line.draw(header.underline, cr) -end + end --------------------------------------------------------------------------------- --- percent timeseries + ----------------------------------------------------------------------------- + -- percent timeseries -M.make_percent_timeseries = function(x, y, w, h, update_freq) + M.make_percent_timeseries = function(x, y, w, h, update_freq) return _make_timeseries(x, y, w, h, _percent_label_config, update_freq) -end + end --------------------------------------------------------------------------------- --- tagged percent timeseries + ----------------------------------------------------------------------------- + -- tagged percent timeseries -M.make_tagged_percent_timeseries = function(x, y, w, h, spacing, label, update_freq) + M.make_tagged_percent_timeseries = function(x, y, w, h, spacing, label, update_freq) return _make_tagged_percent_timeseries( - x, y, w, h, spacing, label, update_freq, '%s%%' + x, y, w, h, spacing, label, update_freq, '%s%%' ) -end + end -M.make_tagged_maybe_percent_timeseries = function(x, y, w, h, spacing, label, update_freq) + M.make_tagged_maybe_percent_timeseries = function(x, y, w, h, spacing, label, update_freq) return _make_tagged_percent_timeseries( - x, y, w, h, spacing, label, update_freq, _format_percent_maybe + x, y, w, h, spacing, label, update_freq, _format_percent_maybe ) -end + end -M.tagged_percent_timeseries_draw_static = function(pp, cr) + M.tagged_percent_timeseries_draw_static = function(pp, cr) text.draw(pp.label, cr) timeseries.draw_static(pp.plot, cr) -end + end -M.tagged_percent_timeseries_draw_dynamic = function(obj, cr) + M.tagged_percent_timeseries_draw_dynamic = function(obj, cr) text_threshold.draw(obj.value, cr) timeseries.draw_dynamic(obj.plot, cr) -end + end -M.tagged_percent_timeseries_set = function(obj, percent) + M.tagged_percent_timeseries_set = function(obj, percent) local _percent = pure.round_percent(percent) text.set(obj.value, _percent) timeseries.update(obj.plot, _percent / 100) -end - -M.tagged_maybe_percent_timeseries_set = function(obj, percent) - if percent == false then - text.set(obj.value, -1) - timeseries.update(obj.plot, 0) - else - M.tagged_percent_timeseries_set(obj, percent) end -end --------------------------------------------------------------------------------- --- scaled plot + M.tagged_maybe_percent_timeseries_set = function(obj, percent) + if percent == false then + text.set(obj.value, -1) + timeseries.update(obj.plot, 0) + else + M.tagged_percent_timeseries_set(obj, percent) + end + end --- Generate a format string for labels on y axis of plots. If the max of the --- plot if numerically less than the number of grid lines, this means that --- some number of decimal places are necessary to accurately display the number. --- Note that this for now only works when the number of y grid lines if 4, as --- it gives enough resolution for 1, 0.75, 0.5, and 0.25 but no more -M.y_label_format_string = function(plot_max, unit) + ----------------------------------------------------------------------------- + -- scaled plot + + -- Generate a format string for labels on y axis of plots. If the max of the + -- plot if numerically less than the number of grid lines, this means that + -- some number of decimal places are necessary to accurately display the number. + -- Note that this for now only works when the number of y grid lines if 4, as + -- it gives enough resolution for 1, 0.75, 0.5, and 0.25 but no more + M.y_label_format_string = function(plot_max, unit) local num_fmt if plot_max < 2 then - num_fmt = '%.2f' + num_fmt = '%.2f' elseif plot_max < 4 then - num_fmt = '%.1f' + num_fmt = '%.1f' else - num_fmt = '%.0f' + num_fmt = '%.0f' end return string.format('%s %s', num_fmt, unit) -end - -M.converted_y_label_format_generator = function(unit) - return function(plot_max) - local new_prefix, new_max = format.convert_data_val(plot_max) - local conversion_factor = plot_max / new_max - local fmt = M.y_label_format_string(new_max, new_prefix..unit..'/s') - return function(bytes) - return string.format(fmt, bytes / conversion_factor) - end end -end --------------------------------------------------------------------------------- --- tagged scaled plot + M.converted_y_label_format_generator = function(unit) + return function(plot_max) + local new_prefix, new_max = format.convert_data_val(plot_max) + local conversion_factor = plot_max / new_max + local fmt = M.y_label_format_string(new_max, new_prefix..unit..'/s') + return function(bytes) + return string.format(fmt, bytes / conversion_factor) + end + end + end -M.make_tagged_scaled_timeseries = function(x, y, w, h, format_fun, label_fun, + ----------------------------------------------------------------------------- + -- tagged scaled plot + + M.make_tagged_scaled_timeseries = function(x, y, w, h, format_fun, label_fun, spacing, label, min_domain, update_freq) return { - label = _left_text(geom.make_point(x, y), label), - value = text.make_formatted( - geom.make_point(x + w, y), - 0, - _right_text_style, - format_fun - ), - plot = _make_scaled_timeseries(x, y + spacing, w, h, label_fun, min_domain, update_freq), + label = _left_text(geom.make_point(x, y), label), + value = text.make_formatted( + geom.make_point(x + w, y), + 0, + _right_text_style, + format_fun + ), + plot = _make_scaled_timeseries(x, y + spacing, w, h, label_fun, min_domain, update_freq), } -end + end -M.tagged_scaled_timeseries_draw_static = function(asp, cr) + M.tagged_scaled_timeseries_draw_static = function(asp, cr) text.draw(asp.label, cr) -end + end -M.tagged_scaled_timeseries_draw_dynamic = function(asp, cr) + M.tagged_scaled_timeseries_draw_dynamic = function(asp, cr) text.draw(asp.value, cr) scaled_timeseries.draw_dynamic(asp.plot, cr) -end + end -M.tagged_scaled_timeseries_set = function(asp, value) + M.tagged_scaled_timeseries_set = function(asp, value) text.set(asp.value, value) scaled_timeseries.update(asp.plot, value) -end - --------------------------------------------------------------------------------- --- rate timecourse plots - -local make_differential = function(update_frequency) - return function(x0, x1) - -- mask overflow - if x1 > x0 then - return (x1 - x0) * update_frequency - else - return 0 - end end -end -M.make_rate_timeseries = function(x, y, w, h, format_fun, label_fun, spacing, + ----------------------------------------------------------------------------- + -- rate timecourse plots + + local make_differential = function(update_frequency) + return function(x0, x1) + -- mask overflow + if x1 > x0 then + return (x1 - x0) * update_frequency + else + return 0 + end + end + end + + M.make_rate_timeseries = function(x, y, w, h, format_fun, label_fun, spacing, label, min_domain, update_freq, init) return { - label = _left_text(geom.make_point(x, y), label), - value = text.make_formatted( - geom.make_point(x + w, y), - 0, - _right_text_style, - format_fun - ), - plot = _make_scaled_timeseries(x, y + spacing, w, h, label_fun, min_domain, update_freq), - prev_value = init, - derive = make_differential(update_freq), + label = _left_text(geom.make_point(x, y), label), + value = text.make_formatted( + geom.make_point(x + w, y), + 0, + _right_text_style, + format_fun + ), + plot = _make_scaled_timeseries(x, y + spacing, w, h, label_fun, min_domain, update_freq), + prev_value = init, + derive = make_differential(update_freq), } -end + end -M.update_rate_timeseries = function(obj, value) + M.update_rate_timeseries = function(obj, value) local rate = obj.derive(obj.prev_value, value) text.set(obj.value, rate) scaled_timeseries.update(obj.plot, rate) obj.prev_value = value -end + end --------------------------------------------------------------------------------- --- circle + ----------------------------------------------------------------------------- + -- circle -M.make_circle = function(x, y, r) + M.make_circle = function(x, y, r) return circle.make( - geom.make_circle(x, y, r), - circle.config(style.line(ARC_WIDTH, CAIRO_LINE_CAP_BUTT), theme.BORDER_FG) + geom.make_circle(x, y, r), + circle.config(style.line(ARC_WIDTH, CAIRO_LINE_CAP_BUTT), patterns.border) ) -end + end --------------------------------------------------------------------------------- --- ring with text data in the center + ----------------------------------------------------------------------------- + -- ring with text data in the center -M.make_text_circle = function(x, y, r, fmt, threshhold, pre_function) + M.make_text_circle = function(x, y, r, fmt, threshhold, pre_function) return { - ring = M.make_circle(x, y, r), - value = text_threshold.make_formatted( - geom.make_point(x, y), - 0, - text.config(normal_font_spec, theme.PRIMARY_FG, 'center', 'center'), - fmt, - text_threshold.config(theme.CRITICAL_FG, threshhold, pre_function) - ), + ring = M.make_circle(x, y, r), + value = text_threshold.make_formatted( + geom.make_point(x, y), + 0, + text.config(normal_font_spec, patterns.text.active, 'center', 'center'), + fmt, + text_threshold.config(patterns.text.critical, threshhold, pre_function) + ), } -end + end -M.text_circle_draw_static = function(tr, cr) + M.text_circle_draw_static = function(tr, cr) arc.draw(tr.ring, cr) -end + end -M.text_circle_draw_dynamic = function(tr, cr) + M.text_circle_draw_dynamic = function(tr, cr) text_threshold.draw(tr.value, cr) -end + end -M.text_circle_set = function(tr, value) + M.text_circle_set = function(tr, value) text_threshold.set(tr.value, value) -end + end --------------------------------------------------------------------------------- --- dial + ----------------------------------------------------------------------------- + -- dial -local threshold_indicator = function(threshold) + local threshold_indicator = function(threshold) return source.threshold_config( - theme.INDICATOR_FG_PRIMARY, - theme.INDICATOR_FG_CRITICAL, - threshold + patterns.indicator.fg.active, + patterns.indicator.fg.critical, + threshold ) -end + end -M.make_dial = function(x, y, radius, thickness, threshold, _format, pre_function) + M.make_dial = function(x, y, radius, thickness, threshold, _format, pre_function) return { - dial = dial.make( - geom.make_arc(x, y, radius, DIAL_THETA0, DIAL_THETA1), - arc.config(style.line(thickness, CAIRO_LINE_CAP_BUTT), theme.INDICATOR_BG), - threshold_indicator(threshold) - ), - text_circle = M.make_text_circle(x, y, radius - thickness / 2 - 2, _format, threshold, pre_function), + dial = dial.make( + geom.make_arc(x, y, radius, DIAL_THETA0, DIAL_THETA1), + arc.config(style.line(thickness, CAIRO_LINE_CAP_BUTT), patterns.indicator.bg), + threshold_indicator(threshold) + ), + text_circle = M.make_text_circle(x, y, radius - thickness / 2 - 2, _format, threshold, pre_function), } -end + end -M.dial_set = function(dl, value) + M.dial_set = function(dl, value) dial.set(dl.dial, value) M.text_circle_set(dl.text_circle, value) -end + end -M.dial_draw_static = function(dl, cr) + M.dial_draw_static = function(dl, cr) dial.draw_static(dl.dial, cr) M.text_circle_draw_static(dl.text_circle, cr) -end + end -M.dial_draw_dynamic = function(dl, cr) + M.dial_draw_dynamic = function(dl, cr) dial.draw_dynamic(dl.dial, cr) M.text_circle_draw_dynamic(dl.text_circle, cr) -end + end --------------------------------------------------------------------------------- --- compound dial + ----------------------------------------------------------------------------- + -- compound dial -M.make_compound_dial = function(x, y, outer_radius, inner_radius, thickness, + M.make_compound_dial = function(x, y, outer_radius, inner_radius, thickness, threshold, num_dials) return compound_dial.make( - geom.make_arc(x, y, outer_radius, DIAL_THETA0, DIAL_THETA1), - arc.config(style.line(thickness, CAIRO_LINE_CAP_BUTT), theme.INDICATOR_BG), - threshold_indicator(threshold), - inner_radius, - num_dials + geom.make_arc(x, y, outer_radius, DIAL_THETA0, DIAL_THETA1), + arc.config(style.line(thickness, CAIRO_LINE_CAP_BUTT), patterns.indicator.bg), + threshold_indicator(threshold), + inner_radius, + num_dials ) -end + end --------------------------------------------------------------------------------- --- annotated compound bar + ----------------------------------------------------------------------------- + -- annotated compound bar -M.make_compound_bar = function(x, y, w, pad, labels, spacing, thickness, threshold) + M.make_compound_bar = function(x, y, w, pad, labels, spacing, thickness, threshold) return { - labels = text_column.make( - geom.make_point(x, y), - labels, - _left_text_style, - nil, - spacing - ), - bars = compound_bar.make( - geom.make_point(x + pad, y), - w - pad, - line.config( - style.line(thickness, CAIRO_LINE_CAP_BUTT), - theme.INDICATOR_BG, - true - ), - threshold_indicator(threshold), - spacing, - #labels, - false - ) + labels = text_column.make( + geom.make_point(x, y), + labels, + _left_text_style, + nil, + spacing + ), + bars = compound_bar.make( + geom.make_point(x + pad, y), + w - pad, + line.config( + style.line(thickness, CAIRO_LINE_CAP_BUTT), + patterns.indicator.bg, + true + ), + threshold_indicator(threshold), + spacing, + #labels, + false + ) } -end + end -M.compound_bar_draw_static = function(cb, cr) + M.compound_bar_draw_static = function(cb, cr) text_column.draw(cb.labels, cr) compound_bar.draw_static(cb.bars, cr) -end + end -M.compound_bar_draw_dynamic = function(cb, cr) + M.compound_bar_draw_dynamic = function(cb, cr) compound_bar.draw_dynamic(cb.bars, cr) -end + end -M.compound_bar_set = function(cb, i, value) + M.compound_bar_set = function(cb, i, value) compound_bar.set(cb.bars, i, value) -end + end --------------------------------------------------------------------------------- --- separator (eg a horizontal line) + ----------------------------------------------------------------------------- + -- separator (eg a horizontal line) -M.make_separator = function(x, y, w) + M.make_separator = function(x, y, w) return line.make( - _make_horizontal_line(x, y, w), - line.config( - style.line(SEPARATOR_THICKNESS, CAIRO_LINE_CAP_BUTT), - theme.BORDER_FG, - true - ) + _make_horizontal_line(x, y, w), + line.config( + style.line(SEPARATOR_THICKNESS, CAIRO_LINE_CAP_BUTT), + patterns.border, + true + ) ) -end + end --------------------------------------------------------------------------------- --- text row (label with a value, aligned as far apart as possible) + ----------------------------------------------------------------------------- + -- text row (label with a value, aligned as far apart as possible) -M.make_text_row = function(x, y, w, label) + M.make_text_row = function(x, y, w, label) return { - label = _left_text(geom.make_point(x, y), label), - value = _right_text(geom.make_point(x + w, y), nil), + label = _left_text(geom.make_point(x, y), label), + value = _right_text(geom.make_point(x + w, y), nil), } -end + end -M.text_row_draw_static = function(row, cr) + M.text_row_draw_static = function(row, cr) text.draw(row.label, cr) -end + end -M.text_row_draw_dynamic = function(row, cr) + M.text_row_draw_dynamic = function(row, cr) text.draw(row.value, cr) -end + end -M.text_row_set = function(row, value) + M.text_row_set = function(row, value) text.set(row.value, value) -end + end --------------------------------------------------------------------------------- --- text row with critical indicator + ----------------------------------------------------------------------------- + -- text row with critical indicator -M.make_threshold_text_row = function(x, y, w, label, append_end, limit) + M.make_threshold_text_row = function(x, y, w, label, append_end, limit) return{ - label = _left_text(geom.make_point(x, y), label), - value = text_threshold.make_formatted( - geom.make_point(x + w, y), - nil, - _right_text_style, - append_end, - text_threshold.config(theme.CRITICAL_FG, limit, false) - ) + label = _left_text(geom.make_point(x, y), label), + value = text_threshold.make_formatted( + geom.make_point(x + w, y), + nil, + _right_text_style, + append_end, + text_threshold.config(patterns.text.critical, limit, false) + ) } -end + end -M.threshold_text_row_draw_static = M.text_row_draw_static + M.threshold_text_row_draw_static = M.text_row_draw_static -M.threshold_text_row_draw_dynamic = function(row, cr) + M.threshold_text_row_draw_dynamic = function(row, cr) text_threshold.draw(row.value, cr) -end + end -M.threshold_text_row_set = function(row, value) + M.threshold_text_row_set = function(row, value) text_threshold.set(row.value, value) -end + end --------------------------------------------------------------------------------- --- multiple text row separated by spacing + ----------------------------------------------------------------------------- + -- multiple text row separated by spacing -M.make_text_rows_formatted = function(x, y, w, spacing, labels, format) + M.make_text_rows_formatted = function(x, y, w, spacing, labels, format) return { - labels = text_column.make( - geom.make_point(x, y), - labels, - _left_text_style, - nil, - spacing - ), - values = text_column.make_n( - geom.make_point(x + w, y), - #labels, - _right_text_style, - format, - spacing, - 0 - ) + labels = text_column.make( + geom.make_point(x, y), + labels, + _left_text_style, + nil, + spacing + ), + values = text_column.make_n( + geom.make_point(x + w, y), + #labels, + _right_text_style, + format, + spacing, + 0 + ) } -end + end -M.make_text_rows = function(x, y, w, spacing, labels) + M.make_text_rows = function(x, y, w, spacing, labels) return M.make_text_rows_formatted( - x, - y, - w, - spacing, - labels, - nil + x, + y, + w, + spacing, + labels, + nil ) -end + end -M.text_rows_draw_static = function(rows, cr) + M.text_rows_draw_static = function(rows, cr) text_column.draw(rows.labels, cr) -end + end -M.text_rows_draw_dynamic = function(rows, cr) + M.text_rows_draw_dynamic = function(rows, cr) text_column.draw(rows.values, cr) -end + end -M.text_rows_set = function(rows, i, value) + M.text_rows_set = function(rows, i, value) text_column.set(rows.values, i, value) -end + end --------------------------------------------------------------------------------- --- table + ----------------------------------------------------------------------------- + -- table -local default_table_font_spec = make_font_spec(FONT, TABLE_FONT_SIZE, false) + local default_table_font_spec = make_font_spec(FONT, TABLE_FONT_SIZE, false) -local default_table_config = function(label) + local default_table_config = function(label) return tbl.config( - rect.config( - style.closed_poly(TABLE_LINE_THICKNESS, CAIRO_LINE_JOIN_MITER), - theme.BORDER_FG - ), - line.config( - style.line(TABLE_LINE_THICKNESS, CAIRO_LINE_CAP_BUTT), - theme.BORDER_FG, - true - ), - tbl.header_config( - default_table_font_spec, - theme.PRIMARY_FG, - TABLE_HEADER_PAD - ), - tbl.body_config( - default_table_font_spec, - theme.INACTIVE_TEXT_FG, - { - tbl.column_config('Name', TABLE_BODY_FORMAT), - tbl.column_config('PID', false), - tbl.column_config(label, false), - } - ), - tbl.padding( - TABLE_HORZ_PAD, - TABLE_VERT_PAD, - TABLE_HORZ_PAD, - TABLE_VERT_PAD - ) + rect.config( + style.closed_poly(TABLE_LINE_THICKNESS, CAIRO_LINE_JOIN_MITER), + patterns.border + ), + line.config( + style.line(TABLE_LINE_THICKNESS, CAIRO_LINE_CAP_BUTT), + patterns.border, + true + ), + tbl.header_config( + default_table_font_spec, + patterns.text.active, + TABLE_HEADER_PAD + ), + tbl.body_config( + default_table_font_spec, + patterns.text.inactive, + { + tbl.column_config('Name', TABLE_BODY_FORMAT), + tbl.column_config('PID', false), + tbl.column_config(label, false), + } + ), + tbl.padding( + TABLE_HORZ_PAD, + TABLE_VERT_PAD, + TABLE_HORZ_PAD, + TABLE_VERT_PAD + ) ) -end + end -M.make_text_table = function(x, y, w, h, n, label) + M.make_text_table = function(x, y, w, h, n, label) return tbl.make( - geom.make_box(x, y, w, h), - n, - default_table_config(label) + geom.make_box(x, y, w, h), + n, + default_table_config(label) ) -end + end --------------------------------------------------------------------------------- --- panel + ----------------------------------------------------------------------------- + -- panel -M.make_panel = function(x, y, w, h, thickness) + M.make_panel = function(x, y, w, h, thickness) return fill_rect.make( - geom.make_box(x, y, w, h), - rect.config( - style.closed_poly(thickness, CAIRO_LINE_JOIN_MITER), - theme.BORDER_FG - ), - theme.PANEL_BG + geom.make_box(x, y, w, h), + rect.config( + style.closed_poly(thickness, CAIRO_LINE_JOIN_MITER), + patterns.border + ), + patterns.panel ) -end - --------------------------------------------------------------------------------- --- setup functions - -local _combine_blocks = function(acc, new) - if new.active == true then - local n = new.f(acc.next_y) - table.insert(acc.objs, n.obj) - acc.w = math.max(acc.w, n.w) - acc.final_y = acc.next_y + n.h - acc.next_y = acc.final_y + new.offset end - return acc -end -local non_false = function(xs) - return pure.filter(function(x) return x ~= false end, xs) + return M end - -local active_blocks = function(blockspecs) - local bs = pure.filter(function(b) return b[2] end, blockspecs) - return pure.map(function(b) return M.mk_block(table.unpack(b)) end, bs) -end - -local flatten_sections = function(top, ...) - local f = function(acc, new) - if #new.blocks == 0 then - return acc - elseif #acc == 0 then - return new.blocks - else - return pure.flatten( - {acc, {M.mk_block(new.sep_fun, true, new.top)}, new.blocks} - ) - end - end - return pure.reduce(f, active_blocks(top), {...}) -end - -M.reduce_blocks_ = function(header, point, width, top_blocks, ...) - local mk_header = function(y) - local obj = M.make_header(point.x, y, width, header) - return M.mk_acc_static( - width, - obj.bottom_y - y, - function(cr) M.draw_header(cr, obj) end - ) - end - local blocks = flatten_sections(top_blocks, ...) - local r = pure.reduce( - _combine_blocks, - {w = 0, next_y = point.y, final_y = point.y, objs = {}}, - {M.mk_block(mk_header, true, 0), table.unpack(blocks)} - ) - local us, ss, ds = table.unpack(pure.unzip(r.objs)) - return { - next_x = point.x + r.w, - next_y = r.final_y, - update = pure.sequence(table.unpack(non_false(us))), - static = pure.sequence(table.unpack(ss)), - dynamic = pure.sequence(table.unpack(non_false(ds))) - } -end - -M.mk_acc = function(w, h, u, s, d) - return {w = w, h = h, obj = {u, s, d}} -end - -M.mk_acc_static = function(w, h, s) - return M.mk_acc(w, h, false, s, false) -end - -M.mk_block = function(f, active, offset) - return {f = f, active = active, offset = offset} -end - -M.mk_section = function(top, sep_fun, ...) - return { - top = top, - sep_fun = sep_fun, - blocks = active_blocks({...}) - } -end - -M.mk_seperator = function(width, x, y) - local separator = M.make_separator(x, y, width) - return M.mk_acc_static(width, 0, pure.partial(line.draw, separator)) -end - -return M diff --git a/drawing/compile.lua b/drawing/compile.lua new file mode 100644 index 0000000..ef43dc2 --- /dev/null +++ b/drawing/compile.lua @@ -0,0 +1,218 @@ +local M = {} + +local pure = require 'pure' +local geom = require 'geom' +local fill_rect = require 'fill_rect' +local line = require 'line' + +-------------------------------------------------------------------------------- +-- compile individual module + +local _combine_blocks = function(acc, new) + if new.active == true then + local n = new.f(acc.next_y) + table.insert(acc.objs, n.obj) + acc.w = math.max(acc.w, n.w) + acc.final_y = acc.next_y + n.h + acc.next_y = acc.final_y + new.offset + end + return acc +end + +local non_false = function(xs) + return pure.filter(function(x) return x ~= false end, xs) +end + +local mk_block = function(f, active, offset) + return {f = f, active = active, offset = offset} +end + +local active_blocks = function(blockspecs) + local bs = pure.filter(function(b) return b[2] end, blockspecs) + return pure.map(function(b) return mk_block(table.unpack(b)) end, bs) +end + +local flatten_sections = function(top, ...) + local f = function(acc, new) + if #new.blocks == 0 then + return acc + elseif #acc == 0 then + return new.blocks + else + return pure.flatten( + {acc, {mk_block(new.sep_fun, true, new.top)}, new.blocks} + ) + end + end + return pure.reduce(f, active_blocks(top), {...}) +end + +M.mk_section = function(top, sep_fun, ...) + return { + top = top, + sep_fun = sep_fun, + blocks = active_blocks({...}) + } +end + +M.mk_seperator = function(common, width, x, y) + local separator = common.make_separator(x, y, width) + return M.mk_acc_static(width, 0, pure.partial(line.draw, separator)) +end + +M.mk_acc = function(w, h, u, s, d) + return {w = w, h = h, obj = {u, s, d}} +end + +M.mk_acc_static = function(w, h, s) + return M.mk_acc(w, h, false, s, false) +end + +M.compile_module = function(common, header, point, width, top_blocks, ...) + local mk_header = function(y) + local obj = common.make_header(point.x, y, width, header) + return M.mk_acc_static( + width, + obj.bottom_y - y, + function(cr) common.draw_header(cr, obj) end + ) + end + local blocks = flatten_sections(top_blocks, ...) + local r = pure.reduce( + _combine_blocks, + {w = 0, next_y = point.y, final_y = point.y, objs = {}}, + {mk_block(mk_header, true, 0), table.unpack(blocks)} + ) + local us, ss, ds = table.unpack(pure.unzip(r.objs)) + return { + next_x = point.x + r.w, + next_y = r.final_y, + update = pure.sequence(table.unpack(non_false(us))), + static = pure.sequence(table.unpack(ss)), + dynamic = pure.sequence(table.unpack(non_false(ds))) + } +end + +-------------------------------------------------------------------------------- +-- compile entire layout + +local reduce_modules_y = function(common, modlist, init_x, width, acc, new) + if type(new) == "number" then + acc.next_y = acc.next_y + new + else + print(new) + local r = modlist[new](common, width, geom.make_point(init_x, acc.next_y)) + table.insert(acc.fgroups, {update = r.update, static = r.static, dynamic = r.dynamic}) + acc.next_x = math.max(acc.next_x, r.next_x) + acc.next_y = r.next_y + end + return acc +end + +local reduce_modules_x = function(common, modlist, init_y, acc, x_mods) + if type(x_mods) == "number" then + acc.next_x = acc.next_x + x_mods + else + local r = pure.reduce( + pure.partial(reduce_modules_y, common, modlist, acc.next_x, x_mods.width), + {next_x = acc.next_x, next_y = init_y, fgroups = acc.fgroups}, + x_mods.blocks + ) + acc.fgroups = r.fgroups + acc.next_x = r.next_x + acc.next_y = math.max(acc.next_y, r.next_y) + end + return acc +end + +local arrange_panel_modules = function(common, modlist, point, mods) + local r = pure.reduce( + pure.partial(reduce_modules_x, common, modlist, point.y), + {next_x = point.x, next_y = point.y, fgroups = {}}, + mods + ) + return { + point_ul = point, + width = r.next_x - point.x, + height = r.next_y - point.y, + update = pure.map_keys('update', r.fgroups), + static = pure.map_keys('static', r.fgroups), + dynamic = pure.map_keys('dynamic', r.fgroups), + } +end + +local build_surface = function(common, box, fs) + local panel_line_thickness = 1 + -- move over by half a pixel so the lines don't need to be antialiased + local _x = box.corner.x + 0.5 + local _y = box.corner.y + 0.5 + local panel = common.make_panel(_x, _y, box.width, box.height, panel_line_thickness) + local cs_x = _x - panel_line_thickness * 0.5 + local cs_y = _y - panel_line_thickness * 0.5 + local cs_w = box.width + panel_line_thickness + local cs_h = box.height + panel_line_thickness + + local cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, cs_w, cs_h) + local cr = cairo_create(cs) + + cairo_translate(cr, -cs_x, -cs_y) + + fill_rect.draw(panel, cr) + for i = 1, #fs do + fs[i](cr) + end + cairo_destroy(cr) + return {x = cs_x, y = cs_y, s = cs} +end + +local reduce_static = function(common, mods, y, acc, panel_mods) + if type(panel_mods) == "number" then + acc.next_x = acc.next_x + panel_mods + else + local margins = panel_mods.margins + local margin_x = margins[1] + local margin_y = margins[2] + local mpoint = geom.make_point(acc.next_x + margin_x, y + margin_y) + local r = arrange_panel_modules(common, mods, mpoint, panel_mods.columns) + local w = r.width + margin_x * 2 + local h = r.height + margin_y * 2 + local pbox = geom.make_box(acc.next_x, y, w, h) + acc.next_x = acc.next_x + w + acc.static = pure.flatten({acc.static, {build_surface(common, pbox, r.static)}}) + acc.update = pure.flatten({acc.update, r.update}) + acc.dynamic = pure.flatten({acc.dynamic, r.dynamic}) + end + return acc +end + +M.compile_layout = function(common, point, mods, module_sets) + local __cairo_set_source_surface = cairo_set_source_surface + local __cairo_paint = cairo_paint + + local r = pure.reduce( + pure.partial( + reduce_static, + common, + mods, + point.y + ), + {next_x = point.x, static = {}, update = {}, dynamic = {}}, + module_sets + ) + + local cs = r.static + + return { + static = function(cr) + for i = 1, #cs do + local c = cs[i] + __cairo_set_source_surface(cr, c.s, c.x, c.y) + __cairo_paint(cr) + end + end, + update = pure.sequence(table.unpack(r.update)), + dynamic = pure.sequence(table.unpack(r.dynamic)), + } +end + +return M diff --git a/drawing/filesystem.lua b/drawing/filesystem.lua index c291ad5..448dbfe 100644 --- a/drawing/filesystem.lua +++ b/drawing/filesystem.lua @@ -1,10 +1,9 @@ local i_o = require 'i_o' -local common = require 'common' +local compile = require 'compile' local pure = require 'pure' local impure = require 'impure' --- ASSUME pathspecs will be at least 1 long -return function(config, main_state, width, point) +return function(config, main_state, common, width, point) local SPACING = 20 local BAR_PAD = 100 local SEPARATOR_SPACING = 20 @@ -20,7 +19,7 @@ return function(config, main_state, width, point) common.text_row_set(obj, (pid == '') and 'Error' or 'Running') end end - return common.mk_acc( + return compile.mk_acc( width, 0, update, @@ -29,7 +28,7 @@ return function(config, main_state, width, point) ) end - local mk_sep = pure.partial(common.mk_seperator, width, point.x) + local mk_sep = pure.partial(compile.mk_seperator, common, width, point.x) ----------------------------------------------------------------------------- -- filesystem bar chart @@ -37,7 +36,6 @@ return function(config, main_state, width, point) local mk_bars = function(y) local paths = pure.map_keys('path', config.fs_paths) local names = pure.map_keys('name', config.fs_paths) - -- local paths, names = table.unpack(pure.unzip(config.fs_paths)) local CONKY_CMDS = pure.map( pure.partial(string.format, '${fs_used_perc %s}', true), paths @@ -60,7 +58,7 @@ return function(config, main_state, width, point) impure.ieach(read_fs, CONKY_CMDS) end end - return common.mk_acc( + return compile.mk_acc( width, (#config.fs_paths - 1) * SPACING, update, @@ -72,11 +70,12 @@ return function(config, main_state, width, point) ----------------------------------------------------------------------------- -- main functions - return common.reduce_blocks_( + return compile.compile_module( + common, 'FILE SYSTEMS', point, width, {{mk_smart, config.show_smart, SEPARATOR_SPACING}}, - common.mk_section(SEPARATOR_SPACING, mk_sep, {mk_bars, true, 0}) + compile.mk_section(SEPARATOR_SPACING, mk_sep, {mk_bars, true, 0}) ) end diff --git a/drawing/graphics.lua b/drawing/graphics.lua index 6485287..cc395f1 100644 --- a/drawing/graphics.lua +++ b/drawing/graphics.lua @@ -1,8 +1,8 @@ local pure = require 'pure' local i_o = require 'i_o' -local common = require 'common' +local compile = require 'compile' -return function(update_freq, config, width, point) +return function(update_freq, config, common, width, point) local SEPARATOR_SPACING = 20 local TEXT_SPACING = 20 local PLOT_SEC_BREAK = 20 @@ -41,7 +41,7 @@ return function(update_freq, config, width, point) ) local static = pure.partial(common.tagged_percent_timeseries_draw_static, obj) local dynamic = pure.partial(common.tagged_percent_timeseries_draw_dynamic, obj) - return common.mk_acc( + return compile.mk_acc( width, PLOT_HEIGHT + PLOT_SEC_BREAK, update, @@ -69,11 +69,12 @@ return function(update_freq, config, width, point) end local static = pure.partial(common.text_row_draw_static, obj) local dynamic = pure.partial(common.text_row_draw_dynamic, obj) - return common.mk_acc(width, 0, update, static, dynamic) + return compile.mk_acc(width, 0, update, static, dynamic) end local mk_sep = pure.partial( - common.mk_seperator, + compile.mk_seperator, + common, width, point.x ) @@ -99,7 +100,7 @@ return function(update_freq, config, width, point) ) local static = pure.partial(common.threshold_text_row_draw_static, obj) local dynamic = pure.partial(common.threshold_text_row_draw_dynamic, obj) - return common.mk_acc(width, 0, update, static, dynamic) + return compile.mk_acc(width, 0, update, static, dynamic) end ----------------------------------------------------------------------------- @@ -124,7 +125,7 @@ return function(update_freq, config, width, point) end local static = pure.partial(common.text_rows_draw_static, obj) local dynamic = pure.partial(common.text_rows_draw_dynamic, obj) - return common.mk_acc(width, TEXT_SPACING, update, static, dynamic) + return compile.mk_acc(width, TEXT_SPACING, update, static, dynamic) end ----------------------------------------------------------------------------- @@ -216,22 +217,23 @@ return function(update_freq, config, width, point) ----------------------------------------------------------------------------- -- main drawing functions - local rbs = common.reduce_blocks_( + local rbs = compile.compile_module( + common, 'NVIDIA GRAPHICS', point, width, {{mk_status, true, SEPARATOR_SPACING}}, - common.mk_section( + compile.mk_section( SEPARATOR_SPACING, mk_sep, {mk_temp, config.show_temp, SEPARATOR_SPACING} ), - common.mk_section( + compile.mk_section( SEPARATOR_SPACING, mk_sep, {mk_clock, config.show_clock, SEPARATOR_SPACING} ), - common.mk_section( + compile.mk_section( SEPARATOR_SPACING, mk_sep, {mk_gpu_util, config.show_gpu_util, PLOT_SEC_BREAK}, diff --git a/drawing/memory.lua b/drawing/memory.lua index 8c7fa34..263661f 100644 --- a/drawing/memory.lua +++ b/drawing/memory.lua @@ -1,10 +1,10 @@ local timeseries = require 'timeseries' local text_table = require 'text_table' local i_o = require 'i_o' -local common = require 'common' local pure = require 'pure' +local compile = require 'compile' -return function(update_freq, config, width, point) +return function(update_freq, config, common, width, point) local DIAL_THICKNESS = 8 local DIAL_RADIUS = 32 local DIAL_SPACING = 40 @@ -78,7 +78,7 @@ return function(update_freq, config, width, point) common.dial_draw_dynamic(swap, cr) common.text_rows_draw_dynamic(cache, cr) end - return common.mk_acc(width, DIAL_DIAMETER, update, static, dynamic) + return compile.mk_acc(width, DIAL_DIAMETER, update, static, dynamic) end ----------------------------------------------------------------------------- @@ -92,7 +92,7 @@ return function(update_freq, config, width, point) PLOT_HEIGHT, update_freq ) - return common.mk_acc( + return compile.mk_acc( width, PLOT_HEIGHT, function(s) timeseries.update(obj, s.mem.used_percent) end, @@ -130,7 +130,7 @@ return function(update_freq, config, width, point) text_table.set(obj, 3, r, i_o.conky(TABLE_CONKY[r].mem)) end end - return common.mk_acc( + return compile.mk_acc( width, TABLE_HEIGHT, update, @@ -179,7 +179,8 @@ return function(update_freq, config, width, point) ----------------------------------------------------------------------------- -- main functions - local rbs = common.reduce_blocks_( + local rbs = compile.compile_module( + common, 'MEMORY', point, width, diff --git a/drawing/network.lua b/drawing/network.lua index 60e4ac1..23dbdf5 100644 --- a/drawing/network.lua +++ b/drawing/network.lua @@ -1,10 +1,10 @@ local format = require 'format' local pure = require 'pure' local i_o = require 'i_o' -local common = require 'common' local sys = require 'sys' +local compile = require 'compile' -return function(update_freq, width, point) +return function(update_freq, common, width, point) local PLOT_SEC_BREAK = 20 local PLOT_HEIGHT = 56 local INTERFACE_PATHS = sys.get_net_interface_paths() @@ -51,7 +51,7 @@ return function(update_freq, width, point) update_freq, state[key] ) - return common.mk_acc( + return compile.mk_acc( width, PLOT_HEIGHT + PLOT_SEC_BREAK, function(s) common.update_rate_timeseries(obj, s[key]) end, @@ -66,7 +66,8 @@ return function(update_freq, width, point) ----------------------------------------------------------------------------- -- main drawing functions - local rbs = common.reduce_blocks_( + local rbs = compile.compile_module( + common, 'NETWORK', point, width, diff --git a/drawing/pacman.lua b/drawing/pacman.lua index 388aa45..fbee23d 100644 --- a/drawing/pacman.lua +++ b/drawing/pacman.lua @@ -1,7 +1,7 @@ -local common = require 'common' +local compile = require 'compile' local pure = require 'pure' -return function(main_state, width, point) +return function(main_state, common, width, point) local TEXT_SPACING = 20 local __string_match = string.match @@ -32,7 +32,7 @@ return function(main_state, width, point) end end end - return common.mk_acc( + return compile.mk_acc( width, TEXT_SPACING * 4, update, @@ -41,7 +41,8 @@ return function(main_state, width, point) ) end - return common.reduce_blocks_( + return compile.compile_module( + common, 'PACMAN', point, width, diff --git a/drawing/power.lua b/drawing/power.lua index cf360c5..bb9bf69 100644 --- a/drawing/power.lua +++ b/drawing/power.lua @@ -1,9 +1,9 @@ local format = require 'format' local pure = require 'pure' -local common = require 'common' +local compile = require 'compile' local sys = require 'sys' -return function(update_freq, config, width, point) +return function(update_freq, config, common, width, point) local TEXT_SPACING = 20 local PLOT_SEC_BREAK = 20 local PLOT_HEIGHT = 56 @@ -40,7 +40,7 @@ return function(update_freq, config, width, point) update_freq, read_joules() ) - return common.mk_acc( + return compile.mk_acc( width, PLOT_HEIGHT + PLOT_SEC_BREAK, function(_) common.update_rate_timeseries(obj, read_joules()) end, @@ -89,7 +89,7 @@ return function(update_freq, config, width, point) 0, update_freq ) - return common.mk_acc( + return compile.mk_acc( width, PLOT_HEIGHT + PLOT_SEC_BREAK, function() @@ -106,7 +106,8 @@ return function(update_freq, config, width, point) ----------------------------------------------------------------------------- -- main functions - return common.reduce_blocks_( + return compile.compile_module( + common, 'POWER', point, width, diff --git a/drawing/processor.lua b/drawing/processor.lua index 3b8f4cf..9b3ce16 100644 --- a/drawing/processor.lua +++ b/drawing/processor.lua @@ -1,13 +1,13 @@ local compound_dial = require 'compound_dial' local text_table = require 'text_table' local i_o = require 'i_o' -local common = require 'common' +local compile = require 'compile' local cpu = require 'sys' local pure = require 'pure' local __math_floor = math.floor -return function(update_freq, config, main_state, width, point) +return function(update_freq, config, main_state, common, width, point) local DIAL_INNER_RADIUS = 30 local DIAL_OUTER_RADIUS = 42 local DIAL_THICKNESS = 5.5 @@ -91,7 +91,7 @@ return function(update_freq, config, main_state, width, point) compound_dial.draw_dynamic(cores[i].loads, cr) end end - return common.mk_acc( + return compile.mk_acc( width, DIAL_OUTER_RADIUS * 2, update, @@ -123,7 +123,7 @@ return function(update_freq, config, main_state, width, point) end local static = pure.partial(common.text_rows_draw_static, cpu_status) local dynamic = pure.partial(common.text_rows_draw_dynamic, cpu_status) - return common.mk_acc( + return compile.mk_acc( width, TEXT_SPACING, update, @@ -136,7 +136,8 @@ return function(update_freq, config, main_state, width, point) -- frequency local mk_sep = pure.partial( - common.mk_seperator, + compile.mk_seperator, + common, width, point.x ) @@ -163,7 +164,7 @@ return function(update_freq, config, main_state, width, point) end local static = pure.partial(common.tagged_percent_timeseries_draw_static, total_load) local dynamic = pure.partial(common.tagged_percent_timeseries_draw_dynamic, total_load) - return common.mk_acc( + return compile.mk_acc( width, PLOT_HEIGHT + PLOT_SECTION_BREAK, update, @@ -202,7 +203,7 @@ return function(update_freq, config, main_state, width, point) end local static = pure.partial(text_table.draw_static, tbl) local dynamic = pure.partial(text_table.draw_dynamic, tbl) - return common.mk_acc( + return compile.mk_acc( width, TABLE_HEIGHT, update, @@ -214,7 +215,8 @@ return function(update_freq, config, main_state, width, point) ----------------------------------------------------------------------------- -- main functions - local rbs = common.reduce_blocks_( + local rbs = compile.compile_module( + common, 'PROCESSOR', point, width, @@ -222,7 +224,7 @@ return function(update_freq, config, main_state, width, point) {mk_cores, config.show_cores, TEXT_SPACING}, {mk_hwp_freq, config.show_stats, SEPARATOR_SPACING}, }, - common.mk_section( + compile.mk_section( SEPARATOR_SPACING, mk_sep, {mk_load_plot, config.show_plot, TABLE_SECTION_BREAK}, diff --git a/drawing/readwrite.lua b/drawing/readwrite.lua index 3f2f9f9..5202117 100644 --- a/drawing/readwrite.lua +++ b/drawing/readwrite.lua @@ -1,9 +1,9 @@ local format = require 'format' local pure = require 'pure' -local common = require 'common' +local compile = require 'compile' local sys = require 'sys' -return function(update_freq, config, width, point) +return function(update_freq, config, common, width, point) local PLOT_SEC_BREAK = 20 local PLOT_HEIGHT = 56 -- TODO currently this will find any block device @@ -34,7 +34,7 @@ return function(update_freq, config, width, point) update_freq, state[key] ) - return common.mk_acc( + return compile.mk_acc( -- TODO construct this more sanely without referring to hardcoded vars width, PLOT_HEIGHT + PLOT_SEC_BREAK, @@ -50,7 +50,8 @@ return function(update_freq, config, width, point) ----------------------------------------------------------------------------- -- main drawing functions - local rbs = common.reduce_blocks_( + local rbs = compile.compile_module( + common, 'INPUT / OUTPUT', point, width, diff --git a/drawing/static.lua b/drawing/static.lua deleted file mode 100644 index c873097..0000000 --- a/drawing/static.lua +++ /dev/null @@ -1,121 +0,0 @@ -local common = require 'common' -local pure = require 'pure' -local geom = require 'geom' -local fill_rect = require 'fill_rect' - -local reduce_modules_y = function(modlist, init_x, width, acc, new) - if type(new) == "number" then - acc.next_y = acc.next_y + new - else - local r = modlist[new](width, geom.make_point(init_x, acc.next_y)) - table.insert(acc.fgroups, {update = r.update, static = r.static, dynamic = r.dynamic}) - acc.next_x = math.max(acc.next_x, r.next_x) - acc.next_y = r.next_y - end - return acc -end - -local reduce_modules_x = function(modlist, init_y, acc, x_mods) - if type(x_mods) == "number" then - acc.next_x = acc.next_x + x_mods - else - local r = pure.reduce( - pure.partial(reduce_modules_y, modlist, acc.next_x, x_mods.width), - {next_x = acc.next_x, next_y = init_y, fgroups = acc.fgroups}, - x_mods.blocks - ) - acc.fgroups = r.fgroups - acc.next_x = r.next_x - acc.next_y = math.max(acc.next_y, r.next_y) - end - return acc -end - -local arrange_panel_modules = function(modlist, point, mods) - local r = pure.reduce( - pure.partial(reduce_modules_x, modlist, point.y), - {next_x = point.x, next_y = point.y, fgroups = {}}, - mods - ) - return { - point_ul = point, - width = r.next_x - point.x, - height = r.next_y - point.y, - update = pure.map_keys('update', r.fgroups), - static = pure.map_keys('static', r.fgroups), - dynamic = pure.map_keys('dynamic', r.fgroups), - } -end - -local build_surface = function(box, fs) - local panel_line_thickness = 1 - -- move over by half a pixel so the lines don't need to be antialiased - local _x = box.corner.x + 0.5 - local _y = box.corner.y + 0.5 - local panel = common.make_panel(_x, _y, box.width, box.height, panel_line_thickness) - local cs_x = _x - panel_line_thickness * 0.5 - local cs_y = _y - panel_line_thickness * 0.5 - local cs_w = box.width + panel_line_thickness - local cs_h = box.height + panel_line_thickness - - local cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, cs_w, cs_h) - local cr = cairo_create(cs) - - cairo_translate(cr, -cs_x, -cs_y) - - fill_rect.draw(panel, cr) - for i = 1, #fs do - fs[i](cr) - end - cairo_destroy(cr) - return {x = cs_x, y = cs_y, s = cs} -end - -local reduce_static = function(mods, y, acc, panel_mods) - if type(panel_mods) == "number" then - acc.next_x = acc.next_x + panel_mods - else - local margins = panel_mods.margins - local margin_x = margins[1] - local margin_y = margins[2] - local mpoint = geom.make_point(acc.next_x + margin_x, y + margin_y) - local r = arrange_panel_modules(mods, mpoint, panel_mods.columns) - local w = r.width + margin_x * 2 - local h = r.height + margin_y * 2 - local pbox = geom.make_box(acc.next_x, y, w, h) - acc.next_x = acc.next_x + w - acc.static = pure.flatten({acc.static, {build_surface(pbox, r.static)}}) - acc.update = pure.flatten({acc.update, r.update}) - acc.dynamic = pure.flatten({acc.dynamic, r.dynamic}) - end - return acc -end - -return function(point, mods, module_sets) - local __cairo_set_source_surface = cairo_set_source_surface - local __cairo_paint = cairo_paint - - local r = pure.reduce( - pure.partial( - reduce_static, - mods, - point.y - ), - {next_x = point.x, static = {}, update = {}, dynamic = {}}, - module_sets - ) - - local cs = r.static - - return { - static = function(cr) - for i = 1, #cs do - local c = cs[i] - __cairo_set_source_surface(cr, c.s, c.x, c.y) - __cairo_paint(cr) - end - end, - update = pure.sequence(table.unpack(r.update)), - dynamic = pure.sequence(table.unpack(r.dynamic)), - } -end diff --git a/drawing/system.lua b/drawing/system.lua index 40f92c6..888990e 100644 --- a/drawing/system.lua +++ b/drawing/system.lua @@ -1,8 +1,8 @@ local i_o = require 'i_o' local pure = require 'pure' -local common = require 'common' +local compile = require 'compile' -return function(main_state, width, point) +return function(main_state, common, width, point) local TEXT_SPACING = 20 local __string_match = string.match @@ -23,7 +23,7 @@ return function(main_state, width, point) "^%d+%s+([^%s]+)%s+([^%s]+).*" ) end - -- TODO this doesn't need to be update every time + -- TODO this doesn't need to be updated every time common.text_rows_set(obj, 1, i_o.conky('$kernel')) common.text_rows_set(obj, 2, i_o.conky('$uptime')) common.text_rows_set(obj, 3, last_update) @@ -31,7 +31,7 @@ return function(main_state, width, point) end local static = pure.partial(common.text_rows_draw_static, obj) local dynamic = pure.partial(common.text_rows_draw_dynamic, obj) - return common.mk_acc( + return compile.mk_acc( width, TEXT_SPACING * 3, update, @@ -40,10 +40,5 @@ return function(main_state, width, point) ) end - return common.reduce_blocks_( - 'SYSTEM', - point, - width, - {{mk_stats, true, 0}} - ) + return compile.compile_module(common, 'SYSTEM', point, width, {{mk_stats, true, 0}}) end diff --git a/main.lua b/main.lua index f40e821..2c30228 100644 --- a/main.lua +++ b/main.lua @@ -32,21 +32,19 @@ local power = require 'power' local readwrite = require 'readwrite' local graphics = require 'graphics' local memory = require 'memory' -local static = require 'static' +local compile = require 'compile' +local common = require 'common' local yaml = require 'lyaml' local draw_dynamic -function conky_start(update_interval) - conky_set_update_interval(update_interval) - +local build_main = function(update_interval, config_path) local update_freq = 1 / update_interval + local config = yaml.load(i_o.read_file(config_path)) + local cmods = config.modules local main_state = {} - local config = yaml.load(i_o.read_file(ABS_PATH..'config.yml')) - local cmods = config.modules - local mods = { memory = pure.partial(memory, update_freq, cmods.memory), readwrite = pure.partial(readwrite, update_freq, cmods.readwrite), @@ -59,7 +57,8 @@ function conky_start(update_interval) pacman = pure.partial(pacman, main_state) } - local compiled = static( + local compiled = compile.compile_layout( + common(config), geom.make_point(table.unpack(config.layout.anchor)), mods, config.layout.panels @@ -67,7 +66,7 @@ function conky_start(update_interval) local STATS_FILE = '/tmp/.conky_pacman' - draw_dynamic = function(cr, _updates) + return function(cr, _updates) main_state.trigger10 = _updates % (update_freq * 10) main_state.pacman_stats = i_o.read_file(STATS_FILE) @@ -77,6 +76,12 @@ function conky_start(update_interval) end end +function conky_start(update_interval) + conky_set_update_interval(update_interval) + + draw_dynamic = build_main(update_interval, ABS_PATH..'config.yml') +end + local updates = -2 -- this accounts for the first few spazzy iterations function conky_main()