diff --git a/Makefile b/Makefile index 728f74d..2fc7b9a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ PROGRAM = xob MANPAGE = doc/xob.1 SYSCONF = styles.cfg -LIBS = x11 libconfig +LIBS = x11 libconfig xrandr SOURCES = src/conf.c src/display.c src/main.c # Feature: alpha channel (transparency) diff --git a/README.md b/README.md index e70349e..8504b6e 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,7 @@ When starting, xob looks for the configuration file in the following order: Consult the man page for detailed information about the configuration file and the available options. The following `styles.cfg` defines a single style called "default" that showcases all the possible options set to the default values. The configuration file may contain additional styles to choose among using the **-s** argument. default = { + monitor = "combined"; x = {relative = 1; offset = -48;}; y = {relative = 0.5; offset = 0;}; length = {relative = 0.3; offset = 0;}; @@ -315,9 +316,10 @@ There is no support for panel integration. You can however use absolute position > "How about multiple monitors?" -xob works well under multihead setups. The `x`, `y`, and `length` style options refer to the combined screen surface. By default the bar is vertical near the right edge of the rightmost monitor. In vertical layouts, you may prefer to switch the bar to horizontal mode as follows to avoid splits. +xob works well under multihead setups. The default orientation of bar is `vertical` and is positioned at the right edge of the screen surface. By default, `x`, `y`, and `length` style options refer to the combined screen surface, but you can specify a monitor for the bar, for example you can set option `monitor = "HDMI-1"` to show the bar only on `HDMI-1` (use `xrandr` command to get monitors names). In vertical layouts, you may prefer to switch the bar to horizontal mode as follows to avoid splits. horizontal = { + monitor = "combined"; x = {relative = 0.5; offset = 0;}; y = {relative = 1; offset = -48;}; orientation = "horizontal"; diff --git a/doc/xob.1 b/doc/xob.1 index 40a3c84..ad8f143 100644 --- a/doc/xob.1 +++ b/doc/xob.1 @@ -169,6 +169,13 @@ In the following, a dot \[lq].\[rq] means \[lq]suboption\[rq]. For instance \[lq]color.normal.fg\[rq] means \[lq]The suboption fg of the suboption normal of option color\[rq]. .TP +\f[B]monitor\f[R] \f[I]\[lq]output_name\[rq] | \[lq]relative_focus\[rq] | \[lq]relative_pointer\[rq] | \[lq]combined\[rq]\f[R] (default: combined) +Output monitor for the bar, use \f[I]xrandr\f[R] command to get monitors names. +Use \f[I]relative_focus\f[R] to show the bar on the monitor with a focused window. +Use \f[I]relative_pointer\f[R] to show the bar on the monitor with a mouse +pointer. Use \f[I]combined\f[R] to show the bar on the combined surface of all +monitors. The option is case-sensitive. +.TP \f[B]orientation\f[R] \f[I]\[lq]horizontal\[rq] | \[lq]vertical\[rq]\f[R] (default: vertical) Orientation of the bar which either fills up from left to right (\[lq]horizontal\[rq]) or bottom to top (\[lq]vertical\[rq]). @@ -301,6 +308,7 @@ backlight = { .nf \f[C] default = { + monitor = \[dq]combined\[dq]; x = {relative = 1; offset = -48;}; y = {relative = 0.5; offset = 0;}; length = {relative = 0.3; offset = 0;}; @@ -367,17 +375,17 @@ situations. \[lq]How to set up xob with multiple monitors?\[rq] .RE .PP -xob works well under multihead setups but there is no easy way to -configure the position of the bar for now. -For example, in a dual monitor setup with the default configuration, the -horizontal centering is not local to one of the two monitors. -It is global. -The bar might be split in two: one part on each screen. -Stick to a corner or use absolute positioning. -If you want an xob instance to be centered (horizontally) on the -far-right monitor, set \f[I]x.relative\f[R] to 1.0 (anchored on the far -right) and the \f[I]x.offset\f[R] to minus half the width of that -screen. +xob works well under multihead setups, use option \f[I]monitor\f[R] to +specify one. By default xob use \f[I]combined\f[R] for the option. It means +that in a dual monitor setup with the default configuration, +the horizontal centering is not local to one of the two monitors. +It is global. The bar might be split in two: one part on each screen. +If you want an xob instance to be centered (horizontally) on the specific +monitor, set \f[I]monitor\f[R] option to your monitor output, +for example \f[I]monitor = \[lq]HDMI-1\[rq]\f[R] and set options +\f[I]x.relative\f[R] and \f[I]y.relative\f[R] relative to the monitor. +To get monitors output name you can use \f[I]xrandr --listmonitors\f[R] +command in your terminal. .SH CONTRIBUTIONS .PP Feedback and contributions are welcome. diff --git a/doc/xob.md b/doc/xob.md index b6c2f35..7a4d111 100644 --- a/doc/xob.md +++ b/doc/xob.md @@ -104,6 +104,9 @@ Options can be grouped together inside curly brackets. Some options expect a gro In the following, a dot "." means "suboption". For instance "color.normal.fg" means "The suboption fg of the suboption normal of option color". +**monitor** *"output_name" | "relative_focus" | "relative_pointer" | "combined"* (default: combined) +: Output monitor for the bar, use `xrandr` command to get monitors names. Use "relative_focus" to show the bar on the monitor with a focused window. Use "relative_pointer" to show the bar on the monitor with a mouse pointer. Use "combined" to show the bar on the combined surface of all monitors. The option is case-sensitive. + **orientation** *"horizontal" | "vertical"* (default: vertical) : Orientation of the bar which either fills up from left to right ("horizontal") or bottom to top ("vertical"). @@ -156,7 +159,6 @@ Each of the following have three suboptions ".fg", ".bg", and ".border" correspo ## STYLES - All the options described above must be encompassed inside a style specification. A style consists of a group of all or some of the options described above. The name of the style is the name of an option at the root level of the configuration file. When an option is missing from a style, the default values are used instead. A configuration file may specify several styles (at least 1) to choose using the **-s** argument. This example configuration file provides two styles "volume" and "backlight". Instances of xog launched with **-s volume** and **-s backlight** will look according to the corresponding style. @@ -194,6 +196,7 @@ This example configuration file provides two styles "volume" and "backlight". In ## DEFAULT CONFIGURATION FILE default = { + monitor = "combined"; x = {relative = 1; offset = -48;}; y = {relative = 0.5; offset = 0;}; length = {relative = 0.3; offset = 0;}; @@ -242,7 +245,7 @@ There is no support for panel integration. You can however use absolute position > "How to set up xob with multiple monitors?" -xob works well under multihead setups but there is no easy way to configure the position of the bar for now. For example, in a dual monitor setup with the default configuration, the horizontal centering is not local to one of the two monitors. It is global. The bar might be split in two: one part on each screen. Stick to a corner or use absolute positioning. If you want an xob instance to be centered (horizontally) on the far-right monitor, set *x.relative* to 1.0 (anchored on the far right) and the *x.offset* to minus half the width of that screen. +xob works well under multihead setups, use option `monitor` to specify one. By default xob use `combined` for the option. It means that in a dual monitor setup with the default configuration, the horizontal centering is not local to one of the two monitors. It is global. The bar might be split in two: one part on each screen. If you want an xob instance to be centered (horizontally) on the specific monitor, set `monitor` option to your monitor output, for example `monitor = "HDMI-1"` and set options `x.relative` and `y.relative` relative to the monitor. To get monitors output name you can use `xrandr --listmonitors` command in your terminal. # CONTRIBUTIONS diff --git a/src/conf.c b/src/conf.c index 25559a7..bb2f0f7 100644 --- a/src/conf.c +++ b/src/conf.c @@ -229,6 +229,22 @@ static int config_setting_lookup_orientation(const config_setting_t *setting, return success_status; } +static int config_setting_lookup_monitor(const config_setting_t *setting, + const char *name, char *monitorvalue) +{ + const char *stringvalue; + + if (config_setting_lookup_string(setting, name, &stringvalue)) + { + strncpy(monitorvalue, stringvalue, LNAME_MONITOR); + } + else + { + fprintf(stderr, "Error: No style %s.\n", name); + } + return CONFIG_TRUE; +} + Style parse_style_config(FILE *file, const char *stylename, Style default_style) { config_t config; @@ -243,6 +259,7 @@ Style parse_style_config(FILE *file, const char *stylename, Style default_style) xob_config = config_lookup(&config, stylename); if (xob_config != NULL) { + config_setting_lookup_monitor(xob_config, "monitor", style.monitor); config_setting_lookup_int(xob_config, "thickness", &style.thickness); config_setting_lookup_int(xob_config, "border", &style.border); diff --git a/src/conf.h b/src/conf.h index fac68e6..985a8ea 100644 --- a/src/conf.h +++ b/src/conf.h @@ -20,6 +20,12 @@ #include +#define MONITOR_RELATIVE_FOCUS "relative_focus" +#define MONITOR_RELATIVE_POINTER "relative_pointer" +#define MONITOR_COMBINED "combined" + +#define LNAME_MONITOR 17 + typedef struct { unsigned char red; @@ -63,6 +69,7 @@ typedef enum typedef struct { + char monitor[LNAME_MONITOR]; Dim x; Dim y; Dim length; @@ -77,6 +84,7 @@ typedef struct /* clang-format off */ #define DEFAULT_CONFIGURATION (Style) {\ + .monitor = MONITOR_COMBINED,\ .x =\ {\ .rel = 1.0,\ diff --git a/src/display.c b/src/display.c index c20eedf..4623357 100644 --- a/src/display.c +++ b/src/display.c @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include @@ -35,6 +37,7 @@ static int size_x(Geometry_context g) { return g.orientation == HORIZONTAL ? g.length : g.thickness; } + static int size_y(Geometry_context g) { return g.orientation == HORIZONTAL ? g.thickness : g.length; @@ -44,34 +47,107 @@ static int size_y(Geometry_context g) static void draw_empty(X_context x, Geometry_context g, Colors colors) { /* Outline */ +#ifdef _DEBUG_ + // fill_rectangle(x, colors.bg, 0, 0, + // 2 * (g.outline + g.border + g.padding) + size_x(g), + // 2 * (g.outline + g.border + g.padding) + size_y(g)); +#endif + /* Left */ + fill_rectangle(x, colors.bg, 0, 0, g.outline, + 2 * (g.outline + g.border + g.padding) + size_y(g)); + + /* Right */ + fill_rectangle( + x, colors.bg, 2 * (g.border + g.padding) + g.outline + size_x(g), 0, + g.outline, 2 * (g.outline + g.border + g.padding) + size_y(g)); + + /* Top */ fill_rectangle(x, colors.bg, 0, 0, 2 * (g.outline + g.border + g.padding) + size_x(g), - 2 * (g.outline + g.border + g.padding) + size_y(g)); + g.outline); + + /* Bottom */ + fill_rectangle( + x, colors.bg, 0, 2 * (g.border + g.padding) + g.outline + size_y(g), + 2 * (g.outline + g.border + g.padding) + size_x(g), g.outline); + /* Border */ +#ifdef _DEBUG_ fill_rectangle(x, colors.border, g.outline, g.outline, 2 * (g.border + g.padding) + size_x(g), 2 * (g.border + g.padding) + size_y(g)); +#endif + /* Left */ + fill_rectangle(x, colors.border, g.outline, g.outline, g.border, + 2 * (g.border + g.padding) + size_y(g)); + + /* Right */ + fill_rectangle(x, colors.border, + g.outline + g.border + 2 * g.padding + size_x(g), g.outline, + g.border, 2 * (g.border + g.padding) + size_y(g)); + + /* Top */ + fill_rectangle(x, colors.border, g.outline, g.outline, + 2 * (g.border + g.padding) + size_x(g), g.border); + + /* Bottom */ + fill_rectangle(x, colors.border, g.outline, + g.outline + g.border + 2 * g.padding + size_y(g), + 2 * (g.border + g.padding) + size_x(g), g.border); + /* Padding */ +#ifdef _DEBUG_ fill_rectangle(x, colors.bg, g.outline + g.border, g.outline + g.border, 2 * g.padding + size_x(g), 2 * g.padding + size_y(g)); +#endif + + /* Left */ + fill_rectangle(x, colors.bg, g.outline + g.border, g.outline + g.border, + g.padding, 2 * g.padding + size_y(g)); + + /* Right */ + fill_rectangle(x, colors.bg, g.outline + g.border + g.padding + size_x(g), + g.outline + g.border, g.padding, 2 * g.padding + size_y(g)); + + /* Top */ + fill_rectangle(x, colors.bg, g.outline + g.border, g.outline + g.border, + 2 * g.padding + size_x(g), g.padding); + + /* Bottom */ + fill_rectangle(x, colors.bg, g.outline + g.border, + g.outline + g.border + g.padding + size_y(g), + 2 * g.padding + size_x(g), g.padding); } /* Draw a given length of filled bar with the given color */ static void draw_content(X_context x, Geometry_context g, int filled_length, - Color color) + Colors colors) { if (g.orientation == HORIZONTAL) { - fill_rectangle(x, color, g.outline + g.border + g.padding, + /* Fill foreground color */ + fill_rectangle(x, colors.fg, g.outline + g.border + g.padding, g.outline + g.border + g.padding, filled_length, g.thickness); + + /* Fill background color */ + fill_rectangle(x, colors.bg, + g.outline + g.border + g.padding + filled_length, + g.outline + g.border + g.padding, + g.length - filled_length, g.thickness); } else { - fill_rectangle(x, color, g.outline + g.border + g.padding, + /* fill foreground color */ + fill_rectangle(x, colors.fg, g.outline + g.border + g.padding, g.outline + g.border + g.padding + g.length - filled_length, g.thickness, filled_length); + + /* Fill background color */ + fill_rectangle(x, colors.bg, g.outline + g.border + g.padding, + g.outline + g.border + g.padding, g.thickness, + g.length - filled_length); } } @@ -102,31 +178,178 @@ void compute_geometry(Style conf, Display_context *dc, int *topleft_x, dc->geometry.padding = conf.padding; dc->geometry.thickness = conf.thickness; dc->geometry.orientation = conf.orientation; + dc->geometry.length_dynamic.rel = conf.length.rel; + dc->geometry.length_dynamic.abs = conf.length.abs; + *fat_layer = dc->geometry.padding + dc->geometry.border + dc->geometry.outline; /* Orientation-related dimensions */ *available_length = dc->geometry.orientation == HORIZONTAL - ? WidthOfScreen(dc->x.screen) - : HeightOfScreen(dc->x.screen); + ? dc->x.monitor_info.width + : dc->x.monitor_info.height; dc->geometry.length = fit_in(*available_length * conf.length.rel + conf.length.abs, 0, *available_length - 2 * *fat_layer); /* Compute position of the top-left corner */ - *topleft_x = fit_in(WidthOfScreen(dc->x.screen) * conf.x.rel - + *topleft_x = fit_in(dc->x.monitor_info.width * conf.x.rel - (size_x(dc->geometry) + 2 * *fat_layer) / 2, 0, - WidthOfScreen(dc->x.screen) - + dc->x.monitor_info.width - (size_x(dc->geometry) + 2 * *fat_layer)) + - conf.x.abs; - *topleft_y = fit_in(HeightOfScreen(dc->x.screen) * conf.y.rel - + conf.x.abs + dc->x.monitor_info.x; + *topleft_y = fit_in(dc->x.monitor_info.height * conf.y.rel - (size_y(dc->geometry) + 2 * *fat_layer) / 2, 0, - HeightOfScreen(dc->x.screen) - + dc->x.monitor_info.height - (size_y(dc->geometry) + 2 * *fat_layer)) + - conf.y.abs; + conf.y.abs + dc->x.monitor_info.y; +} + +/* Set combined positon */ +static void set_combined_position(Display_context *pdc) +{ + pdc->x.monitor_info.x = 0; + pdc->x.monitor_info.y = 0; + pdc->x.monitor_info.width = WidthOfScreen(pdc->x.screen); + pdc->x.monitor_info.height = HeightOfScreen(pdc->x.screen); + // strcpy(pdc->x.monitor_info.name, MONITOR_COMBINED); +} + +/* Set specified monitor */ +static void set_specified_position(Display_context *pdc, const Style *pconf) +{ + Window root = RootWindow(pdc->x.display, pdc->x.screen_number); + + /* Get monitors info */ + int num_monitors; + char *monitor_name; + XRRMonitorInfo *monitor_sizes = + XRRGetMonitors(pdc->x.display, root, 0, &num_monitors); + + /* Compare monitors output names */ + int i; + for (i = 0; i < num_monitors; i++) + { + monitor_name = XGetAtomName(pdc->x.display, monitor_sizes[i].name); + if (strcmp(pconf->monitor, monitor_name) == 0) + break; + } + if (i != num_monitors) + { + pdc->x.monitor_info.x = monitor_sizes[i].x; + pdc->x.monitor_info.y = monitor_sizes[i].y; + pdc->x.monitor_info.width = monitor_sizes[i].width; + pdc->x.monitor_info.height = monitor_sizes[i].height; + strcpy(pdc->x.monitor_info.name, monitor_name); + } + else // Monitor name is not found + { + /* Use combined surface for monitor option if no monitors with + * provided name found */ + fprintf(stderr, "Error: monitor %s is not found.\n", pconf->monitor); + fprintf(stderr, "Info: falling back to combined mode.\n"); + set_combined_position(pdc); + // strcpy(pdc.x.monitor_info.name, MONITOR_COMBINED); + } + XRRFreeMonitors(monitor_sizes); +} + +/* Move and resize the bar relative to a monitor with provided coords */ +static void move_resize_to_coords_monitor(Display_context *pdc, int x, int y) +{ + int fat_layer, available_length, bar_size_x, bar_size_y, i; + int topleft_x, topleft_y; + int num_monitors; + XRRMonitorInfo *monitor_sizes; + fat_layer = + pdc->geometry.padding + pdc->geometry.border + pdc->geometry.outline; + + monitor_sizes = XRRGetMonitors( + pdc->x.display, RootWindow(pdc->x.display, pdc->x.screen_number), 0, + &num_monitors); + for (i = 0; i < num_monitors; i++) + { + /* Find monitor by coords */ + if (x >= monitor_sizes[i].x && + x < monitor_sizes[i].x + monitor_sizes[i].width && + y >= monitor_sizes[i].y && + y < monitor_sizes[i].y + monitor_sizes[i].height) + { + /* Recalculate bar sizes */ + available_length = pdc->geometry.orientation == HORIZONTAL + ? monitor_sizes[i].width + : monitor_sizes[i].height; + + pdc->geometry.length = + fit_in(available_length * pdc->geometry.length_dynamic.rel + + pdc->geometry.length_dynamic.abs, + 0, available_length - 2 * fat_layer); + + bar_size_x = size_x(pdc->geometry) + 2 * fat_layer; + bar_size_y = size_y(pdc->geometry) + 2 * fat_layer; + + /* Recalculate bar position */ + topleft_x = fit_in(monitor_sizes[i].width * pdc->geometry.x.rel - + bar_size_x / 2, + 0, monitor_sizes[i].width - bar_size_x) + + pdc->geometry.x.abs + monitor_sizes[i].x; + + topleft_y = fit_in(monitor_sizes[i].height * pdc->geometry.y.rel - + bar_size_y / 2, + 0, monitor_sizes[i].height - bar_size_y) + + pdc->geometry.y.abs + monitor_sizes[i].y; + + /* Move and resize bar */ + XMoveResizeWindow(pdc->x.display, pdc->x.window, topleft_x, + topleft_y, bar_size_x, bar_size_y); + break; + } + } + + XRRFreeMonitors(monitor_sizes); +} + +/* Mobe the bar to monitor with focused window */ +static void move_resize_to_focused_monitor(Display_context *pdc) +{ + int revert_to_window; + int focused_x, focused_y; + int dummy_x, dummy_y; + unsigned int focused_width, focused_height, focused_border, focused_depth; + + Window focused_window, fchild_window; + + XGetInputFocus(pdc->x.display, &focused_window, &revert_to_window); + + /* Get coords of focused window */ + XTranslateCoordinates(pdc->x.display, focused_window, + RootWindow(pdc->x.display, pdc->x.screen_number), 0, + 0, &focused_x, &focused_y, &fchild_window); + /* Get focused window width and height to move bar relative to + * the center of focused window */ + XGetGeometry(pdc->x.display, focused_window, &fchild_window, &dummy_x, + &dummy_y, &focused_width, &focused_height, &focused_border, + &focused_depth); + + move_resize_to_coords_monitor(pdc, focused_x + focused_width / 2, + focused_y + focused_height / 2); +} + +/* Move the bar to monitor with pointer */ +static void move_resize_to_pointer_monitor(Display_context *pdc) +{ + int pointer_x, pointer_y, win_x, win_y; + unsigned int p_mask; + Window p_root, p_child; + + XQueryPointer(pdc->x.display, + RootWindow(pdc->x.display, pdc->x.screen_number), &p_root, + &p_child, &pointer_x, &pointer_y, &win_x, &win_y, &p_mask); + + move_resize_to_coords_monitor(pdc, pointer_x, pointer_y); } /* PUBLIC Returns a new display context from a given configuration. If the @@ -159,6 +382,42 @@ Display_context init(Style conf) window_attributes.border_pixel = 0; window_attributes.override_redirect = True; + /* Write bar position relative data to X_context */ + dc.geometry.x.rel = conf.x.rel; + dc.geometry.x.abs = conf.x.abs; + dc.geometry.y.rel = conf.y.rel; + dc.geometry.y.abs = conf.y.abs; + + /* Get bar position from conf */ + if (strcmp(conf.monitor, MONITOR_RELATIVE_FOCUS) == 0) + dc.geometry.bar_position = POSITION_RELATIVE_FOCUS; + else if (strcmp(conf.monitor, MONITOR_RELATIVE_POINTER) == 0) + dc.geometry.bar_position = POSITION_RELATIVE_POINTER; + else if (strcmp(conf.monitor, MONITOR_COMBINED) == 0) + dc.geometry.bar_position = POSITION_COMBINED; + else + dc.geometry.bar_position = POSITION_SPECIFIED; + + switch (dc.geometry.bar_position) + { + case POSITION_RELATIVE_FOCUS: + case POSITION_RELATIVE_POINTER: + /* Bar position and sizes will be recalculated every time before + * showing, so the code just init position and sizes like for + * combined surface */ + set_combined_position(&dc); + break; + case POSITION_COMBINED: + set_combined_position(&dc); + break; + case POSITION_SPECIFIED: + set_specified_position(&dc, &conf); + break; + default: + fprintf(stderr, "Error: in switch position\n"); + break; + } + compute_geometry(conf, &dc, &topleft_x, &topleft_y, &fat_layer, &available_length); @@ -190,79 +449,112 @@ Display_context init(Style conf) } /* PUBLIC Cleans the X memory buffers. */ -void display_context_destroy(Display_context dc) +void display_context_destroy(Display_context *pdc) { - XCloseDisplay(dc.x.display); + XCloseDisplay(pdc->x.display); } /* PUBLIC Show a bar filled at value/cap in normal or alternative mode */ -Display_context show(Display_context dc, int value, int cap, - Overflow_mode overflow_mode, Show_mode show_mode) +void show(Display_context *pdc, int value, int cap, Overflow_mode overflow_mode, + Show_mode show_mode) { - Display_context newdc = dc; - Colors colors; Colors colors_overflow_proportional; + static int_fast8_t current_state = 0x0; + static int_fast8_t last_state = 0x0; + + int old_length = pdc->geometry.length; - if (!dc.x.mapped) + /* Move the bar for relative positions */ + switch (pdc->geometry.bar_position) { - XMapWindow(dc.x.display, dc.x.window); - XRaiseWindow(dc.x.display, dc.x.window); - newdc.x.mapped = True; + case POSITION_RELATIVE_FOCUS: + move_resize_to_focused_monitor(pdc); + break; + case POSITION_RELATIVE_POINTER: + move_resize_to_pointer_monitor(pdc); + break; + case POSITION_COMBINED: + case POSITION_SPECIFIED: + break; + } + + if (!pdc->x.mapped) + { + XMapWindow(pdc->x.display, pdc->x.window); + XRaiseWindow(pdc->x.display, pdc->x.window); + pdc->x.mapped = True; + current_state ^= STATE_MAPPED; } switch (show_mode) { case NORMAL: - colors_overflow_proportional = dc.colorscheme.normal; + colors_overflow_proportional = pdc->colorscheme.normal; + current_state &= ~STATE_ALT; if (value <= cap) - colors = dc.colorscheme.normal; + { + colors = pdc->colorscheme.normal; + current_state &= ~STATE_OVERFLOW; + } else - colors = dc.colorscheme.overflow; + { + colors = pdc->colorscheme.overflow; + colors_overflow_proportional.bg = colors.fg; + current_state |= STATE_OVERFLOW; + } break; case ALTERNATIVE: - colors_overflow_proportional = dc.colorscheme.alt; + colors_overflow_proportional = pdc->colorscheme.alt; + current_state |= STATE_ALT; if (value <= cap) - colors = dc.colorscheme.alt; + { + colors = pdc->colorscheme.alt; + current_state &= ~STATE_OVERFLOW; + } else - colors = dc.colorscheme.altoverflow; + { + colors = pdc->colorscheme.altoverflow; + colors_overflow_proportional.bg = colors.fg; + current_state |= STATE_OVERFLOW; + } break; } - /* Empty bar */ - draw_empty(dc.x, dc.geometry, colors); - - /* Content */ - draw_content(dc.x, dc.geometry, - fit_in(value, 0, cap) * dc.geometry.length / cap, colors.fg); + /* Redraw empty bar only if needed */ + if (last_state != current_state || old_length != pdc->geometry.length) + { + /* Empty bar */ + draw_empty(pdc->x, pdc->geometry, colors); + } + last_state = current_state; /* Proportional overflow : draw separator */ if (value > cap && overflow_mode == PROPORTIONAL && - cap * dc.geometry.length / value > dc.geometry.padding) + cap * pdc->geometry.length / value > pdc->geometry.padding) { - draw_content(dc.x, dc.geometry, cap * dc.geometry.length / value, - colors_overflow_proportional.fg); - draw_separator(dc.x, dc.geometry, cap * dc.geometry.length / value, - colors.bg); + draw_content(pdc->x, pdc->geometry, cap * pdc->geometry.length / value, + colors_overflow_proportional); + draw_separator(pdc->x, pdc->geometry, + cap * pdc->geometry.length / value, colors.bg); } + else // Value is less then cap + /* Content */ + draw_content(pdc->x, pdc->geometry, + fit_in(value, 0, cap) * pdc->geometry.length / cap, + colors); - XFlush(dc.x.display); - - return newdc; + XFlush(pdc->x.display); } /* PUBLIC Hide the window */ -Display_context hide(Display_context dc) +void hide(Display_context *pdc) { - Display_context newdc = dc; - - if (dc.x.mapped) + if (pdc->x.mapped) { - XUnmapWindow(dc.x.display, dc.x.window); - newdc.x.mapped = False; - XFlush(dc.x.display); + XUnmapWindow(pdc->x.display, pdc->x.window); + pdc->x.mapped = False; + XFlush(pdc->x.display); } - - return newdc; } diff --git a/src/display.h b/src/display.h index 0077c05..02327a1 100644 --- a/src/display.h +++ b/src/display.h @@ -20,6 +20,19 @@ #include "conf.h" #include +#include + +#define STATE_ALT (0x1) +#define STATE_OVERFLOW (0x1 << 1) +#define STATE_MAPPED (0x1 << 2) + +typedef enum +{ + POSITION_RELATIVE_FOCUS, + POSITION_RELATIVE_POINTER, + POSITION_COMBINED, + POSITION_SPECIFIED +} Bar_position; typedef enum { @@ -27,6 +40,15 @@ typedef enum ALTERNATIVE } Show_mode; +typedef struct +{ + char name[10]; + int x; + int y; + int width; + int height; +} MonitorInfo; + typedef struct { Display *display; @@ -34,6 +56,7 @@ typedef struct Screen *screen; Window window; Bool mapped; + MonitorInfo monitor_info; } X_context; typedef struct @@ -42,7 +65,23 @@ typedef struct int border; int padding; int length; + struct + { + double rel; + int abs; + } length_dynamic; int thickness; + struct + { + double rel; + int abs; + } x; + struct + { + double rel; + int abs; + } y; + Bar_position bar_position; Orientation orientation; } Geometry_context; @@ -54,10 +93,10 @@ typedef struct } Display_context; Display_context init(Style conf); -Display_context show(Display_context dc, int value, int cap, - Overflow_mode overflow_mode, Show_mode show_mode); -Display_context hide(Display_context dc); -void display_context_destroy(Display_context dc); +void show(Display_context *pdc, int value, int cap, Overflow_mode overflow_mode, + Show_mode show_mode); +void hide(Display_context *pdc); +void display_context_destroy(Display_context *pdc); /* Draw a rectangle with the given size, position and color */ void fill_rectangle(X_context xc, Color c, int x, int y, unsigned int w, diff --git a/src/main.c b/src/main.c index 1a3eb6a..a33bdb7 100644 --- a/src/main.c +++ b/src/main.c @@ -206,7 +206,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); case 0: /* Time to hide the gauge */ - display_context = hide(display_context); + hide(&display_context); displayed = false; break; default: @@ -214,9 +214,8 @@ int main(int argc, char *argv[]) input_value = parse_input(); if (input_value.valid) { - display_context = - show(display_context, input_value.value, cap, - style.overflow, input_value.show_mode); + show(&display_context, input_value.value, cap, + style.overflow, input_value.show_mode); printf("Update: %d/%d %s\n", input_value.value, cap, (input_value.show_mode == ALTERNATIVE) ? "[ALT]" : ""); @@ -233,7 +232,7 @@ int main(int argc, char *argv[]) } /* Clean the memory */ - display_context_destroy(display_context); + display_context_destroy(&display_context); } return EXIT_SUCCESS; } diff --git a/styles.cfg b/styles.cfg index 5e5c5ca..77d10bd 100644 --- a/styles.cfg +++ b/styles.cfg @@ -1,4 +1,5 @@ default = { + monitor = "combined"; x = {relative = 1; offset = -48;}; y = {relative = 0.5; offset = 0;}; length = {relative = 0.3; offset = 0;};