swaybar: complete barconfig_update event handling

This adds complete support for the barconfig_update ipc event. This also
changes the bar command and subcommand handlers to correctly emit the
event. This makes it so all bar subcommands other than id and
swaybar_command are dynamically changeable at runtime. sway-bar.5 has
been updated accordingly
This commit is contained in:
Brian Ashworth 2019-09-02 21:41:11 -04:00 committed by Drew DeVault
parent 187306640b
commit 1fd2c6ba49
15 changed files with 412 additions and 321 deletions

View file

@ -41,6 +41,7 @@ struct swaybar {
int ipc_socketfd; int ipc_socketfd;
struct wl_list outputs; // swaybar_output::link struct wl_list outputs; // swaybar_output::link
struct wl_list unused_outputs; // swaybar_output::link
struct wl_list seats; // swaybar_seat::link struct wl_list seats; // swaybar_seat::link
#if HAVE_TRAY #if HAVE_TRAY
@ -109,4 +110,8 @@ void set_bar_dirty(struct swaybar *bar);
bool determine_bar_visibility(struct swaybar *bar, bool moving_layer); bool determine_bar_visibility(struct swaybar *bar, bool moving_layer);
void free_workspaces(struct wl_list *list); void free_workspaces(struct wl_list *list);
void status_in(int fd, short mask, void *data);
void destroy_layer_surface(struct swaybar_output *output);
#endif #endif

View file

@ -16,7 +16,6 @@ struct box_colors {
struct config_output { struct config_output {
struct wl_list link; // swaybar_config::outputs struct wl_list link; // swaybar_config::outputs
char *name; char *name;
size_t index;
}; };
struct swaybar_binding { struct swaybar_binding {
@ -41,7 +40,6 @@ struct swaybar_config {
bool workspace_buttons; bool workspace_buttons;
list_t *bindings; list_t *bindings;
struct wl_list outputs; // config_output::link struct wl_list outputs; // config_output::link
bool all_outputs;
int height; int height;
int status_padding; int status_padding;
int status_edge_padding; int status_edge_padding;
@ -83,10 +81,13 @@ struct tray_binding {
char *command; char *command;
struct wl_list link; // struct tray_binding::link struct wl_list link; // struct tray_binding::link
}; };
void free_tray_binding(struct tray_binding *binding);
#endif #endif
struct swaybar_config *init_config(void); struct swaybar_config *init_config(void);
void free_config(struct swaybar_config *config); void free_config(struct swaybar_config *config);
uint32_t parse_position(const char *position); uint32_t parse_position(const char *position);
void free_binding(struct swaybar_binding *binding);
#endif #endif

View file

@ -4,6 +4,7 @@
#include <strings.h> #include <strings.h>
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/ipc-server.h"
#include "log.h" #include "log.h"
// Must be in alphabetical order for bsearch // Must be in alphabetical order for bsearch
@ -56,30 +57,27 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
return error; return error;
} }
bool spawn = false; char *id = NULL;
struct bar_config *bar = NULL;
if (strcmp(argv[0], "id") != 0 && is_subcommand(argv[1])) { if (strcmp(argv[0], "id") != 0 && is_subcommand(argv[1])) {
for (int i = 0; i < config->bars->length; ++i) { for (int i = 0; i < config->bars->length; ++i) {
struct bar_config *item = config->bars->items[i]; struct bar_config *item = config->bars->items[i];
if (strcmp(item->id, argv[0]) == 0) { if (strcmp(item->id, argv[0]) == 0) {
sway_log(SWAY_DEBUG, "Selecting bar: %s", argv[0]); sway_log(SWAY_DEBUG, "Selecting bar: %s", argv[0]);
bar = item; config->current_bar = item;
break; break;
} }
} }
if (!bar) { if (!config->current_bar) {
spawn = !config->reading; id = strdup(argv[0]);
sway_log(SWAY_DEBUG, "Creating bar: %s", argv[0]);
bar = default_bar_config();
if (!bar) {
return cmd_results_new(CMD_FAILURE,
"Unable to allocate bar state");
} }
bar->id = strdup(argv[0]);
}
config->current_bar = bar;
++argv; --argc; ++argv; --argc;
} else if (config->reading && !config->current_bar) {
int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1;
id = malloc(len * sizeof(char));
if (!id) {
return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id");
}
snprintf(id, len, "bar-%d", config->bars->length);
} else if (!config->reading && strcmp(argv[0], "mode") != 0 && } else if (!config->reading && strcmp(argv[0], "mode") != 0 &&
strcmp(argv[0], "hidden_state") != 0) { strcmp(argv[0], "hidden_state") != 0) {
if (is_subcommand(argv[0])) { if (is_subcommand(argv[0])) {
@ -90,56 +88,49 @@ struct cmd_results *cmd_bar(int argc, char **argv) {
} }
} }
if (id) {
sway_log(SWAY_DEBUG, "Creating bar: %s", id);
config->current_bar = default_bar_config();
if (!config->current_bar) { if (!config->current_bar) {
if (config->reading) { free(id);
// Create new bar with default values return cmd_results_new(CMD_FAILURE, "Unable to allocate bar config");
struct bar_config *bar = default_bar_config(); }
if (!bar) { config->current_bar->id = id;
return cmd_results_new(CMD_FAILURE,
"Unable to allocate bar state");
}
// set bar id
int len = snprintf(NULL, 0, "bar-%d", config->bars->length - 1) + 1;
bar->id = malloc(len * sizeof(char));
if (bar->id) {
snprintf(bar->id, len, "bar-%d", config->bars->length - 1);
} else {
return cmd_results_new(CMD_FAILURE, "Unable to allocate bar ID");
}
// Set current bar
config->current_bar = bar;
sway_log(SWAY_DEBUG, "Creating bar %s", bar->id);
}
} }
struct cmd_results *res = NULL;
if (find_handler(argv[0], bar_config_handlers, if (find_handler(argv[0], bar_config_handlers,
sizeof(bar_config_handlers))) { sizeof(bar_config_handlers))) {
if (config->reading) { if (config->reading) {
return config_subcommand(argv, argc, bar_config_handlers, res = config_subcommand(argv, argc, bar_config_handlers,
sizeof(bar_config_handlers)); sizeof(bar_config_handlers));
} else if (spawn) { } else {
for (int i = config->bars->length - 1; i >= 0; i--) { res = cmd_results_new(CMD_INVALID,
struct bar_config *bar = config->bars->items[i]; "Can only be used in the config file");
if (bar == config->current_bar) {
list_del(config->bars, i);
free_bar_config(bar);
break;
} }
} } else {
} res = config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers));
return cmd_results_new(CMD_INVALID,
"Can only be used in the config file.");
} }
struct cmd_results *res = if (res && res->status != CMD_SUCCESS) {
config_subcommand(argv, argc, bar_handlers, sizeof(bar_handlers)); if (id) {
if (!config->reading) { free_bar_config(config->current_bar);
if (spawn) { id = NULL;
}
return res;
}
if (id) {
list_add(config->bars, config->current_bar);
}
if (!config->reading && config->current_bar) {
ipc_event_barconfig_update(config->current_bar);
if (id) {
load_swaybar(config->current_bar); load_swaybar(config->current_bar);
} }
config->current_bar = NULL; config->current_bar = NULL;
} }
return res; return res;
} }

View file

@ -23,7 +23,7 @@ static struct cmd_results *bar_set_hidden_state(struct bar_config *bar,
return cmd_results_new(CMD_INVALID, "Invalid value %s", hidden_state); return cmd_results_new(CMD_INVALID, "Invalid value %s", hidden_state);
} }
if (strcmp(old_state, bar->hidden_state) != 0) { if (strcmp(old_state, bar->hidden_state) != 0) {
if (!config->reading) { if (!config->current_bar) {
ipc_event_barconfig_update(bar); ipc_event_barconfig_update(bar);
} }
sway_log(SWAY_DEBUG, "Setting hidden_state: '%s' for bar: %s", sway_log(SWAY_DEBUG, "Setting hidden_state: '%s' for bar: %s",
@ -47,6 +47,12 @@ struct cmd_results *bar_cmd_hidden_state(int argc, char **argv) {
"Unexpected value %s in config mode", argv[1]); "Unexpected value %s in config mode", argv[1]);
} }
if (config->current_bar && argc == 2 &&
strcmp(config->current_bar->id, argv[1]) != 0) {
return cmd_results_new(CMD_INVALID, "Conflicting bar ids: %s and %s",
config->current_bar->id, argv[1]);
}
const char *state = argv[0]; const char *state = argv[0];
if (config->reading) { if (config->reading) {
error = bar_set_hidden_state(config->current_bar, state); error = bar_set_hidden_state(config->current_bar, state);

View file

@ -27,7 +27,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode
} }
if (strcmp(old_mode, bar->mode) != 0) { if (strcmp(old_mode, bar->mode) != 0) {
if (!config->reading) { if (!config->current_bar) {
ipc_event_barconfig_update(bar); ipc_event_barconfig_update(bar);
} }
sway_log(SWAY_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id); sway_log(SWAY_DEBUG, "Setting mode: '%s' for bar: %s", bar->mode, bar->id);
@ -51,6 +51,12 @@ struct cmd_results *bar_cmd_mode(int argc, char **argv) {
"Unexpected value %s in config mode", argv[1]); "Unexpected value %s in config mode", argv[1]);
} }
if (config->current_bar && argc == 2 &&
strcmp(config->current_bar->id, argv[1]) != 0) {
return cmd_results_new(CMD_INVALID, "Conflicting bar ids: %s and %s",
config->current_bar->id, argv[1]);
}
const char *mode = argv[0]; const char *mode = argv[0];
if (config->reading) { if (config->reading) {
error = bar_set_mode(config->current_bar, mode); error = bar_set_mode(config->current_bar, mode);

View file

@ -21,16 +21,19 @@ struct cmd_results *bar_cmd_output(int argc, char **argv) {
bool add_output = true; bool add_output = true;
if (strcmp("*", output) == 0) { if (strcmp("*", output) == 0) {
// remove all previous defined outputs and replace with '*' // remove all previous defined outputs and replace with '*'
for (int i = 0; i < outputs->length; ++i) { while (outputs->length) {
free(outputs->items[i]); free(outputs->items[0]);
list_del(outputs, i); list_del(outputs, 0);
} }
} else { } else {
// only add output if not already defined with either the same // only add output if not already defined, if the list has '*', remove
// name or as '*' // it, in favor of a manual list
for (int i = 0; i < outputs->length; ++i) { for (int i = 0; i < outputs->length; ++i) {
const char *find = outputs->items[i]; const char *find = outputs->items[i];
if (strcmp("*", find) == 0 || strcmp(output, find) == 0) { if (strcmp("*", find) == 0) {
free(outputs->items[i]);
list_del(outputs, i);
} else if (strcmp(output, find) == 0) {
add_output = false; add_output = false;
break; break;
} }

View file

@ -19,10 +19,5 @@ struct cmd_results *bar_cmd_status_command(int argc, char **argv) {
} else { } else {
free(new_command); free(new_command);
} }
if (config->active && !config->validating) {
load_swaybar(config->current_bar);
}
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
} }

View file

@ -24,9 +24,21 @@ struct cmd_results *bar_cmd_tray_output(int argc, char **argv) {
free(outputs->items[i]); free(outputs->items[i]);
} }
outputs->length = 0; outputs->length = 0;
} else if (strcmp(argv[0], "*") == 0) {
sway_log(SWAY_DEBUG, "Showing tray on all outputs for bar: %s",
config->current_bar->id);
while (outputs->length) {
free(outputs->items[0]);
list_del(outputs, 0);
}
return cmd_results_new(CMD_SUCCESS, NULL);
} else { } else {
sway_log(SWAY_DEBUG, "Showing tray on output '%s' for bar: %s", argv[0], sway_log(SWAY_DEBUG, "Showing tray on output '%s' for bar: %s", argv[0],
config->current_bar->id); config->current_bar->id);
if (outputs->length == 1 && strcmp(outputs->items[0], "none") == 0) {
free(outputs->items[0]);
list_del(outputs, 0);
}
} }
list_add(outputs, strdup(argv[0])); list_add(outputs, strdup(argv[0]));

View file

@ -172,7 +172,6 @@ struct bar_config *default_bar_config(void) {
wl_list_init(&bar->tray_bindings); wl_list_init(&bar->tray_bindings);
#endif #endif
list_add(config->bars, bar);
return bar; return bar;
cleanup: cleanup:
free_bar_config(bar); free_bar_config(bar);

View file

@ -10,67 +10,16 @@ Sway allows configuring swaybar in the sway configuration file.
# COMMANDS # COMMANDS
*status_command* <status command> The following commands may only be used in the configuration file.
Executes the bar _status command_ with _sh -c_. Each line of text printed
to stdout from this command will be displayed in the status area of the
bar. You may also use swaybar's JSON status line protocol. See
*swaybar-protocol*(7) for more information on the protocol
If running this command via IPC, you can disable a running status command by
setting the command to a single dash: _swaybar bar bar-0 status\_command -_
*pango_markup* enabled|disabled
Enables or disables pango markup for status lines. This has no effect on
status lines using the i3bar JSON protocol.
*id* <bar_id> *id* <bar_id>
Sets the ID of the bar. Sets the ID of the bar.
*position* top|bottom
Sets position of the bar. Default is _bottom_.
*output* <output>
Restrict the bar to a certain output, can be specified multiple times. If
the output command is omitted, the bar will be displayed on all outputs.
*swaybar_command* <command> *swaybar_command* <command>
Executes custom bar command. Default is _swaybar_. Executes custom bar command. Default is _swaybar_.
*font* <font> The following commands may be used either in the configuration file or at
Specifies the font to be used in the bar. _font_ should be specified as a runtime.
pango font description. For more information on pango font descriptions,
see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string
*separator_symbol* <symbol>
Specifies the separator symbol to separate blocks on the bar.
*wrap_scroll* yes|no
Enables or disables wrapping when scrolling through workspaces with the
scroll wheel. Default is _no_.
*workspace_buttons* yes|no
Enables or disables workspace buttons on the bar. Default is _yes_.
*strip_workspace_name* yes|no
If set to _yes_, then workspace names will be omitted from the workspace
button and only the custom number will be shown. Default is _no_.
*strip_workspace_numbers* yes|no
If set to _yes_, then workspace numbers will be omitted from the workspace
button and only the custom name will be shown. Default is _no_.
*binding_mode_indicator* yes|no
Enable or disable binding mode indicator. Default is _yes_.
*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
Sets the gaps from the edge of the screen for the bar. Gaps can either be
set all at once, per direction, or per side. Note that only sides that
touch an edge of the screen can have gaps. For the side that does not
touch an edge of the screen, per-side outer gaps for workspaces may be of
use.
*height* <height>
Sets the height of the bar. Default height (0) will match the font size.
*bindcode* [--release] <event-code> <command> *bindcode* [--release] <event-code> <command>
Executes _command_ when the mouse button has been pressed (or if _released_ Executes _command_ when the mouse button has been pressed (or if _released_
@ -85,7 +34,39 @@ Sway allows configuring swaybar in the sway configuration file.
debug-events*. To disable the default behavior for a button, use the debug-events*. To disable the default behavior for a button, use the
command _nop_. command _nop_.
*mode* dock|hide|invisible|overlay *binding_mode_indicator* yes|no
Enable or disable binding mode indicator. Default is _yes_.
*font* <font>
Specifies the font to be used in the bar. _font_ should be specified as a
pango font description. For more information on pango font descriptions,
see https://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string
*gaps* <all> | <horizontal> <vertical> | <top> <right> <bottom> <left>
Sets the gaps from the edge of the screen for the bar. Gaps can either be
set all at once, per direction, or per side. Note that only sides that
touch an edge of the screen can have gaps. For the side that does not
touch an edge of the screen, per-side outer gaps for workspaces may be of
use.
*height* <height>
Sets the height of the bar. Default height (0) will match the font size.
*hidden_state* hide|show [<bar-id>]
Specifies the behaviour of the bar when it is in _hide_ mode. When the
hidden state is _hide_, then it is normally hidden, and only unhidden by
pressing the modifier key or in case of urgency hints. When the hidden
state is _show_, then it is permanently visible, drawn on top of the
currently visible workspace. Default is _hide_.
For compatibility with i3, _bar hidden_state hide|show [<bar-id>]_ is
supported along with the sway only _bar <bar-id> hidden_state hide|show_
syntax. When using the i3 syntax, if _bar-id_ is omitted, the hidden_state
will be changed for all bars. Attempting to use _bar <bar-id1>
hidden_state hide|show <bar-id2>_ will result in an error due to
conflicting bar ids.
*mode* dock|hide|invisible|overlay [<bar-id>]
Specifies the visibility of the bar. In _dock_ mode, it is permanently Specifies the visibility of the bar. In _dock_ mode, it is permanently
visible at one edge of the screen. In _hide_ mode, it is hidden unless the visible at one edge of the screen. In _hide_ mode, it is hidden unless the
modifier key is pressed, though this behaviour depends on the hidden state. modifier key is pressed, though this behaviour depends on the hidden state.
@ -93,32 +74,70 @@ Sway allows configuring swaybar in the sway configuration file.
permanently visible on top of other windows. (In _overlay_ mode the bar is permanently visible on top of other windows. (In _overlay_ mode the bar is
transparent to input events.) Default is _dock_. transparent to input events.) Default is _dock_.
*hidden_state* hide|show For compatibility with i3, _bar mode <mode> [<bar-id>]_ syntax is supported
Specifies the behaviour of the bar when it is in _hide_ mode. When the along with the sway only _bar <bar-id> mode <mode>_ syntax. When using the
hidden state is _hide_, then it is normally hidden, and only unhidden by i3 syntax, if _bar-id_ is omitted, the mode will be changed for all bars.
pressing the modifier key or in case of urgency hints. When the hidden Attempting to use _bar <bar-id1> mode <mode> <bar-id2>_ will result in an
state is _show_, then it is permanently visible, drawn on top of the error due to conflicting bar ids.
currently visible workspace. Default is _hide_.
*modifier* <Modifier>|none *modifier* <Modifier>|none
Specifies the modifier key that shows a hidden bar. Default is _Mod4_. Specifies the modifier key that shows a hidden bar. Default is _Mod4_.
*status_padding* <padding> *output* <output>|\*
Sets the vertical padding that is used for the status line. The default is Restrict the bar to a certain output, can be specified multiple times. If
_1_. If _padding_ is _0_, blocks will be able to take up the full height of the output command is omitted, the bar will be displayed on all outputs. _\*_
the bar. This value will be multiplied by the output scale. can be given at any point to reset it back to all outputs.
*pango_markup* enabled|disabled
Enables or disables pango markup for status lines. This has no effect on
status lines using the i3bar JSON protocol.
*position* top|bottom
Sets position of the bar. Default is _bottom_.
*separator_symbol* <symbol>
Specifies the separator symbol to separate blocks on the bar.
*status_command* <status command>
Executes the bar _status command_ with _sh -c_. Each line of text printed
to stdout from this command will be displayed in the status area of the
bar. You may also use swaybar's JSON status line protocol. See
*swaybar-protocol*(7) for more information on the protocol
If running this command via IPC, you can disable a running status command by
setting the command to a single dash: _swaybar bar bar-0 status\_command -_
*status_edge_padding* <padding> *status_edge_padding* <padding>
Sets the padding that is used when the status line is at the right edge of Sets the padding that is used when the status line is at the right edge of
the bar. This value will be multiplied by the output scale. The default is the bar. This value will be multiplied by the output scale. The default is
_3_. _3_.
*status_padding* <padding>
Sets the vertical padding that is used for the status line. The default is
_1_. If _padding_ is _0_, blocks will be able to take up the full height of
the bar. This value will be multiplied by the output scale.
*strip_workspace_name* yes|no
If set to _yes_, then workspace names will be omitted from the workspace
button and only the custom number will be shown. Default is _no_.
*strip_workspace_numbers* yes|no
If set to _yes_, then workspace numbers will be omitted from the workspace
button and only the custom name will be shown. Default is _no_.
*unbindcode* [--release] <event-code> *unbindcode* [--release] <event-code>
Removes the binding with the given <event-code>. Removes the binding with the given <event-code>.
*unbindsym* [--release] button[1-9]|<event-name> *unbindsym* [--release] button[1-9]|<event-name>
Removes the binding with the given <button> or <event-name>. Removes the binding with the given <button> or <event-name>.
*wrap_scroll* yes|no
Enables or disables wrapping when scrolling through workspaces with the
scroll wheel. Default is _no_.
*workspace_buttons* yes|no
Enables or disables workspace buttons on the bar. Default is _yes_.
## TRAY ## TRAY
Swaybar provides a system tray where third-party applications may place icons. Swaybar provides a system tray where third-party applications may place icons.
@ -142,10 +161,11 @@ ContextMenu|Activate|SecondaryActivate|ScrollDown|ScrollLeft|ScrollRight|ScrollU
Sets the pixel padding of the system tray. This padding will surround the Sets the pixel padding of the system tray. This padding will surround the
tray on all sides and between each item. The default value for _px_ is 2. tray on all sides and between each item. The default value for _px_ is 2.
*tray_output* none|<output> *tray_output* none|<output>|\*
Restrict the tray to a certain output, can be specified multiple times. If Restrict the tray to a certain output, can be specified multiple times. If
omitted, the tray will be displayed on all outputs. Unlike i3bar, swaybar omitted, the tray will be displayed on all outputs. Unlike i3bar, swaybar
can show icons on any number of bars and outputs without races. can show icons on any number of bars and outputs without races. _\*_ can be
given at any point to reset it to display on all outputs.
*icon_theme* <name> *icon_theme* <name>
Sets the icon theme that sway will look for item icons in. This option has Sets the icon theme that sway will look for item icons in. This option has

View file

@ -755,18 +755,6 @@ The default colors are:
*workspace_layout* default|stacking|tabbed *workspace_layout* default|stacking|tabbed
Specifies the initial layout for new workspaces. Specifies the initial layout for new workspaces.
# BAR CONTROL
*bar hidden_state* hide|show|toggle [<bar_id>]
Sets the hidden state of the bar (see *sway-bar*(5)), either individually,
by specifying a bar id, or if none is given, for all bar instances.
_toggle_ switches between _hide_ and _show_.
*bar mode* dock|hide|invisible|toggle [<bar_id>]
Sets the mode of the bar (see *sway-bar*(5)), either individually,
by specifying a bar id, or if none is given, for all bar instances.
_toggle_ switches between _dock_ and _hide_.
# CRITERIA # CRITERIA
A criteria is a string in the form of, for example: A criteria is a string in the form of, for example:

View file

@ -126,7 +126,7 @@ static void add_layer_surface(struct swaybar_output *output) {
} }
} }
static void destroy_layer_surface(struct swaybar_output *output) { void destroy_layer_surface(struct swaybar_output *output) {
if (!output->layer_surface) { if (!output->layer_surface) {
return; return;
} }
@ -181,7 +181,7 @@ bool determine_bar_visibility(struct swaybar *bar, bool moving_layer) {
} }
static bool bar_uses_output(struct swaybar_output *output) { static bool bar_uses_output(struct swaybar_output *output) {
if (output->bar->config->all_outputs) { if (wl_list_empty(&output->bar->config->outputs)) {
return true; return true;
} }
char *identifier = output->identifier; char *identifier = output->identifier;
@ -256,13 +256,17 @@ static void xdg_output_handle_done(void *data,
struct swaybar_output *output = data; struct swaybar_output *output = data;
struct swaybar *bar = output->bar; struct swaybar *bar = output->bar;
assert(output->name != NULL); if (!wl_list_empty(&output->link)) {
if (!bar_uses_output(output)) { return;
swaybar_output_free(output); }
assert(output->name != NULL);
if (!bar_uses_output(output)) {
wl_list_remove(&output->link);
wl_list_insert(&bar->unused_outputs, &output->link);
return; return;
} }
if (wl_list_empty(&output->link)) {
wl_list_remove(&output->link); wl_list_remove(&output->link);
wl_list_insert(&bar->outputs, &output->link); wl_list_insert(&bar->outputs, &output->link);
@ -270,6 +274,9 @@ static void xdg_output_handle_done(void *data,
assert(output->surface); assert(output->surface);
determine_bar_visibility(bar, false); determine_bar_visibility(bar, false);
if (bar->running && bar->config->workspace_buttons) {
ipc_get_workspaces(bar);
} }
} }
@ -373,6 +380,12 @@ static void handle_global_remove(void *data, struct wl_registry *registry,
return; return;
} }
} }
wl_list_for_each_safe(output, tmp, &bar->unused_outputs, link) {
if (output->wl_name == name) {
swaybar_output_free(output);
return;
}
}
struct swaybar_seat *seat, *tmp_seat; struct swaybar_seat *seat, *tmp_seat;
wl_list_for_each_safe(seat, tmp_seat, &bar->seats, link) { wl_list_for_each_safe(seat, tmp_seat, &bar->seats, link) {
if (seat->wl_name == name) { if (seat->wl_name == name) {
@ -391,6 +404,7 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) {
bar->visible = true; bar->visible = true;
bar->config = init_config(); bar->config = init_config();
wl_list_init(&bar->outputs); wl_list_init(&bar->outputs);
wl_list_init(&bar->unused_outputs);
wl_list_init(&bar->seats); wl_list_init(&bar->seats);
bar->eventloop = loop_create(); bar->eventloop = loop_create();
@ -458,7 +472,7 @@ static void ipc_in(int fd, short mask, void *data) {
} }
} }
static void status_in(int fd, short mask, void *data) { void status_in(int fd, short mask, void *data) {
struct swaybar *bar = data; struct swaybar *bar = data;
if (mask & (POLLHUP | POLLERR)) { if (mask & (POLLHUP | POLLERR)) {
status_error(bar->status, "[error reading from status command]"); status_error(bar->status, "[error reading from status command]");
@ -510,6 +524,7 @@ void bar_teardown(struct swaybar *bar) {
destroy_tray(bar->tray); destroy_tray(bar->tray);
#endif #endif
free_outputs(&bar->outputs); free_outputs(&bar->outputs);
free_outputs(&bar->unused_outputs);
free_seats(&bar->seats); free_seats(&bar->seats);
if (bar->config) { if (bar->config) {
free_config(bar->config); free_config(bar->config);

View file

@ -84,7 +84,7 @@ struct swaybar_config *init_config(void) {
return config; return config;
} }
static void free_binding(struct swaybar_binding *binding) { void free_binding(struct swaybar_binding *binding) {
if (!binding) { if (!binding) {
return; return;
} }
@ -93,7 +93,7 @@ static void free_binding(struct swaybar_binding *binding) {
} }
#if HAVE_TRAY #if HAVE_TRAY
static void free_tray_binding(struct tray_binding *binding) { void free_tray_binding(struct tray_binding *binding) {
if (!binding) { if (!binding) {
return; return;
} }

View file

@ -1,15 +1,21 @@
#define _POSIX_C_SOURCE 200809 #define _POSIX_C_SOURCE 200809
#include <limits.h> #include <limits.h>
#include <poll.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <strings.h> #include <strings.h>
#include <json.h> #include <json.h>
#include "swaybar/config.h" #include "swaybar/config.h"
#include "swaybar/ipc.h" #include "swaybar/ipc.h"
#include "swaybar/status_line.h"
#if HAVE_TRAY
#include "swaybar/tray/tray.h"
#endif
#include "config.h" #include "config.h"
#include "ipc-client.h" #include "ipc-client.h"
#include "list.h" #include "list.h"
#include "log.h" #include "log.h"
#include "loop.h"
#include "util.h" #include "util.h"
void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { void ipc_send_workspace_command(struct swaybar *bar, const char *ws) {
@ -169,75 +175,58 @@ static bool ipc_parse_config(
json_object *success; json_object *success;
if (json_object_object_get_ex(bar_config, "success", &success) if (json_object_object_get_ex(bar_config, "success", &success)
&& !json_object_get_boolean(success)) { && !json_object_get_boolean(success)) {
sway_log(SWAY_ERROR, "No bar with that ID. Use 'swaymsg -t get_bar_config to get the available bar configs."); sway_log(SWAY_ERROR, "No bar with that ID. Use 'swaymsg -t "
"get_bar_config' to get the available bar configs.");
json_object_put(bar_config); json_object_put(bar_config);
return false; return false;
} }
json_object *markup, *mode, *hidden_state, *position, *status_command;
json_object *font, *gaps, *bar_height, *wrap_scroll, *workspace_buttons; json_object *bar_height = json_object_object_get(bar_config, "bar_height");
json_object *strip_workspace_numbers, *strip_workspace_name; if (bar_height) {
json_object *binding_mode_indicator, *verbose, *colors, *sep_symbol; config->height = json_object_get_int(bar_height);
json_object *outputs, *bindings, *status_padding, *status_edge_padding;
json_object_object_get_ex(bar_config, "mode", &mode);
json_object_object_get_ex(bar_config, "hidden_state", &hidden_state);
json_object_object_get_ex(bar_config, "position", &position);
json_object_object_get_ex(bar_config, "status_command", &status_command);
json_object_object_get_ex(bar_config, "font", &font);
json_object_object_get_ex(bar_config, "gaps", &gaps);
json_object_object_get_ex(bar_config, "bar_height", &bar_height);
json_object_object_get_ex(bar_config, "wrap_scroll", &wrap_scroll);
json_object_object_get_ex(bar_config, "workspace_buttons", &workspace_buttons);
json_object_object_get_ex(bar_config, "strip_workspace_numbers", &strip_workspace_numbers);
json_object_object_get_ex(bar_config, "strip_workspace_name", &strip_workspace_name);
json_object_object_get_ex(bar_config, "binding_mode_indicator", &binding_mode_indicator);
json_object_object_get_ex(bar_config, "verbose", &verbose);
json_object_object_get_ex(bar_config, "separator_symbol", &sep_symbol);
json_object_object_get_ex(bar_config, "colors", &colors);
json_object_object_get_ex(bar_config, "outputs", &outputs);
json_object_object_get_ex(bar_config, "pango_markup", &markup);
json_object_object_get_ex(bar_config, "bindings", &bindings);
json_object_object_get_ex(bar_config, "status_padding", &status_padding);
json_object_object_get_ex(bar_config, "status_edge_padding",
&status_edge_padding);
if (status_command) {
free(config->status_command);
config->status_command = strdup(json_object_get_string(status_command));
} }
if (position) {
config->position = parse_position(json_object_get_string(position)); json_object *binding_mode_indicator =
json_object_object_get(bar_config, "binding_mode_indicator");
if (binding_mode_indicator) {
config->binding_mode_indicator =
json_object_get_boolean(binding_mode_indicator);
} }
json_object *bindings = json_object_object_get(bar_config, "bindings");
while (config->bindings->length) {
struct swaybar_binding *binding = config->bindings->items[0];
list_del(config->bindings, 0);
free_binding(binding);
}
if (bindings) {
int length = json_object_array_length(bindings);
for (int i = 0; i < length; ++i) {
json_object *bindobj = json_object_array_get_idx(bindings, i);
struct swaybar_binding *binding =
calloc(1, sizeof(struct swaybar_binding));
binding->button = json_object_get_int(
json_object_object_get(bindobj, "event_code"));
binding->command = strdup(json_object_get_string(
json_object_object_get(bindobj, "command")));
binding->release = json_object_get_boolean(
json_object_object_get(bindobj, "release"));
list_add(config->bindings, binding);
}
}
json_object *colors = json_object_object_get(bar_config, "colors");
if (colors) {
ipc_parse_colors(config, colors);
}
json_object *font = json_object_object_get(bar_config, "font");
if (font) { if (font) {
free(config->font); free(config->font);
config->font = parse_font(json_object_get_string(font)); config->font = parse_font(json_object_get_string(font));
} }
if (sep_symbol) {
free(config->sep_symbol); json_object *gaps = json_object_object_get(bar_config, "gaps");
config->sep_symbol = strdup(json_object_get_string(sep_symbol));
}
if (strip_workspace_numbers) {
config->strip_workspace_numbers = json_object_get_boolean(strip_workspace_numbers);
}
if (strip_workspace_name) {
config->strip_workspace_name = json_object_get_boolean(strip_workspace_name);
}
if (binding_mode_indicator) {
config->binding_mode_indicator = json_object_get_boolean(binding_mode_indicator);
}
if (wrap_scroll) {
config->wrap_scroll = json_object_get_boolean(wrap_scroll);
}
if (workspace_buttons) {
config->workspace_buttons = json_object_get_boolean(workspace_buttons);
}
if (bar_height) {
config->height = json_object_get_int(bar_height);
}
if (status_padding) {
config->status_padding = json_object_get_int(status_padding);
}
if (status_edge_padding) {
config->status_edge_padding = json_object_get_int(status_edge_padding);
}
if (gaps) { if (gaps) {
json_object *top = json_object_object_get(gaps, "top"); json_object *top = json_object_object_get(gaps, "top");
if (top) { if (top) {
@ -256,33 +245,21 @@ static bool ipc_parse_config(
config->gaps.left = json_object_get_int(left); config->gaps.left = json_object_get_int(left);
} }
} }
if (markup) {
config->pango_markup = json_object_get_boolean(markup); json_object *hidden_state =
} json_object_object_get(bar_config, "hidden_state");
if (bindings) {
int length = json_object_array_length(bindings);
for (int i = 0; i < length; ++i) {
json_object *bindobj = json_object_array_get_idx(bindings, i);
struct swaybar_binding *binding =
calloc(1, sizeof(struct swaybar_binding));
binding->button = json_object_get_int(
json_object_object_get(bindobj, "event_code"));
binding->command = strdup(json_object_get_string(
json_object_object_get(bindobj, "command")));
binding->release = json_object_get_boolean(
json_object_object_get(bindobj, "release"));
list_add(config->bindings, binding);
}
}
if (hidden_state) { if (hidden_state) {
free(config->hidden_state); free(config->hidden_state);
config->hidden_state = strdup(json_object_get_string(hidden_state)); config->hidden_state = strdup(json_object_get_string(hidden_state));
} }
json_object *mode = json_object_object_get(bar_config, "mode");
if (mode) { if (mode) {
free(config->mode); free(config->mode);
config->mode = strdup(json_object_get_string(mode)); config->mode = strdup(json_object_get_string(mode));
} }
json_object *outputs = json_object_object_get(bar_config, "outputs");
struct config_output *output, *tmp; struct config_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &config->outputs, link) { wl_list_for_each_safe(output, tmp, &config->outputs, link) {
wl_list_remove(&output->link); wl_list_remove(&output->link);
@ -295,40 +272,115 @@ static bool ipc_parse_config(
json_object *output = json_object_array_get_idx(outputs, i); json_object *output = json_object_array_get_idx(outputs, i);
const char *name = json_object_get_string(output); const char *name = json_object_get_string(output);
if (strcmp("*", name) == 0) { if (strcmp("*", name) == 0) {
config->all_outputs = true; struct config_output *coutput, *tmp;
wl_list_for_each_safe(coutput, tmp, &config->outputs, link) {
wl_list_remove(&coutput->link);
free(coutput->name);
free(coutput);
}
break; break;
} }
struct config_output *coutput = calloc( struct config_output *coutput = calloc(
1, sizeof(struct config_output)); 1, sizeof(struct config_output));
coutput->name = strdup(name); coutput->name = strdup(name);
coutput->index = SIZE_MAX;
wl_list_insert(&config->outputs, &coutput->link); wl_list_insert(&config->outputs, &coutput->link);
} }
} else {
config->all_outputs = true;
} }
if (colors) { json_object *pango_markup =
ipc_parse_colors(config, colors); json_object_object_get(bar_config, "pango_markup");
if (pango_markup) {
config->pango_markup = json_object_get_boolean(pango_markup);
} }
json_object *position = json_object_object_get(bar_config, "position");
if (position) {
config->position = parse_position(json_object_get_string(position));
}
json_object *separator_symbol =
json_object_object_get(bar_config, "separator_symbol");
if (separator_symbol) {
free(config->sep_symbol);
config->sep_symbol = strdup(json_object_get_string(separator_symbol));
}
json_object *status_command =
json_object_object_get(bar_config, "status_command");
if (status_command) {
const char *command = json_object_get_string(status_command);
free(config->status_command);
config->status_command = strdup(command);
}
json_object *status_edge_padding =
json_object_object_get(bar_config, "status_edge_padding");
if (status_edge_padding) {
config->status_edge_padding = json_object_get_int(status_edge_padding);
}
json_object *status_padding =
json_object_object_get(bar_config, "status_padding");
if (status_padding) {
config->status_padding = json_object_get_int(status_padding);
}
json_object *strip_workspace_name =
json_object_object_get(bar_config, "strip_workspace_name");
if (strip_workspace_name) {
config->strip_workspace_name =
json_object_get_boolean(strip_workspace_name);
}
json_object *strip_workspace_numbers =
json_object_object_get(bar_config, "strip_workspace_numbers");
if (strip_workspace_numbers) {
config->strip_workspace_numbers =
json_object_get_boolean(strip_workspace_numbers);
}
json_object *workspace_buttons =
json_object_object_get(bar_config, "workspace_buttons");
if (workspace_buttons) {
config->workspace_buttons = json_object_get_boolean(workspace_buttons);
}
json_object *wrap_scroll = json_object_object_get(bar_config, "wrap_scroll");
if (wrap_scroll) {
config->wrap_scroll = json_object_get_boolean(wrap_scroll);
}
#if HAVE_TRAY #if HAVE_TRAY
json_object *tray_outputs, *tray_padding, *tray_bindings, *icon_theme; json_object *tray_outputs, *tray_padding, *tray_bindings, *icon_theme;
if (config->tray_outputs && config->tray_outputs->length) {
list_free_items_and_destroy(config->tray_outputs);
}
if ((json_object_object_get_ex(bar_config, "tray_outputs", &tray_outputs))) { if ((json_object_object_get_ex(bar_config, "tray_outputs", &tray_outputs))) {
config->tray_outputs = create_list(); config->tray_outputs = create_list();
int length = json_object_array_length(tray_outputs); int length = json_object_array_length(tray_outputs);
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
json_object *o = json_object_array_get_idx(tray_outputs, i); json_object *output= json_object_array_get_idx(tray_outputs, i);
list_add(config->tray_outputs, strdup(json_object_get_string(o))); const char *name = json_object_get_string(output);
if (strcmp(name, "none") == 0) {
config->tray_hidden = true;
list_free_items_and_destroy(config->tray_outputs);
config->tray_outputs = create_list();
break;
}
list_add(config->tray_outputs, strdup(name));
} }
config->tray_hidden = strcmp(config->tray_outputs->items[0], "none") == 0;
} }
if ((json_object_object_get_ex(bar_config, "tray_padding", &tray_padding))) { if ((json_object_object_get_ex(bar_config, "tray_padding", &tray_padding))) {
config->tray_padding = json_object_get_int(tray_padding); config->tray_padding = json_object_get_int(tray_padding);
} }
struct tray_binding *tray_bind = NULL, *tmp_tray_bind = NULL;
wl_list_for_each_safe(tray_bind, tmp_tray_bind, &config->tray_bindings,
link) {
wl_list_remove(&tray_bind->link);
free_tray_binding(tray_bind);
}
if ((json_object_object_get_ex(bar_config, "tray_bindings", &tray_bindings))) { if ((json_object_object_get_ex(bar_config, "tray_bindings", &tray_bindings))) {
int length = json_object_array_length(tray_bindings); int length = json_object_array_length(tray_bindings);
for (int i = 0; i < length; ++i) { for (int i = 0; i < length; ++i) {
@ -423,41 +475,6 @@ bool ipc_get_workspaces(struct swaybar *bar) {
return determine_bar_visibility(bar, false); return determine_bar_visibility(bar, false);
} }
static void ipc_get_outputs(struct swaybar *bar) {
uint32_t len = 0;
char *res = ipc_single_command(bar->ipc_socketfd,
IPC_GET_OUTPUTS, NULL, &len);
json_object *outputs = json_tokener_parse(res);
for (size_t i = 0; i < json_object_array_length(outputs); ++i) {
json_object *output = json_object_array_get_idx(outputs, i);
json_object *output_name, *output_active;
json_object_object_get_ex(output, "name", &output_name);
json_object_object_get_ex(output, "active", &output_active);
const char *name = json_object_get_string(output_name);
bool active = json_object_get_boolean(output_active);
if (!active) {
continue;
}
if (bar->config->all_outputs) {
struct config_output *coutput =
calloc(1, sizeof(struct config_output));
coutput->name = strdup(name);
coutput->index = i;
wl_list_insert(&bar->config->outputs, &coutput->link);
} else {
struct config_output *coutput;
wl_list_for_each(coutput, &bar->config->outputs, link) {
if (strcmp(name, coutput->name) == 0) {
coutput->index = i;
break;
}
}
}
}
json_object_put(outputs);
free(res);
}
void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) { void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) {
sway_log(SWAY_DEBUG, "Executing binding for button %u (release=%d): `%s`", sway_log(SWAY_DEBUG, "Executing binding for button %u (release=%d): `%s`",
bind->button, bind->release, bind->command); bind->button, bind->release, bind->command);
@ -475,7 +492,6 @@ bool ipc_initialize(struct swaybar *bar) {
return false; return false;
} }
free(res); free(res);
ipc_get_outputs(bar);
struct swaybar_config *config = bar->config; struct swaybar_config *config = bar->config;
char subscribe[128]; // suitably large buffer char subscribe[128]; // suitably large buffer
@ -509,56 +525,87 @@ static bool handle_bar_state_update(struct swaybar *bar, json_object *event) {
return determine_bar_visibility(bar, false); return determine_bar_visibility(bar, false);
} }
static bool handle_barconfig_update(struct swaybar *bar, static bool handle_barconfig_update(struct swaybar *bar, const char *payload,
json_object *json_config) { json_object *json_config) {
json_object *json_id; json_object *json_id = json_object_object_get(json_config, "id");
json_object_object_get_ex(json_config, "id", &json_id);
const char *id = json_object_get_string(json_id); const char *id = json_object_get_string(json_id);
if (strcmp(id, bar->id) != 0) { if (strcmp(id, bar->id) != 0) {
return false; return false;
} }
struct swaybar_config *config = bar->config; struct swaybar_config *newcfg = init_config();
ipc_parse_config(newcfg, payload);
json_object *json_state; struct swaybar_config *oldcfg = bar->config;
json_object_object_get_ex(json_config, "hidden_state", &json_state); bar->config = newcfg;
const char *new_state = json_object_get_string(json_state);
char *old_state = config->hidden_state;
if (strcmp(new_state, old_state) != 0) {
sway_log(SWAY_DEBUG, "Changing bar hidden state to %s", new_state);
free(old_state);
config->hidden_state = strdup(new_state);
return determine_bar_visibility(bar, false);
}
free(config->mode); struct swaybar_output *output, *tmp_output;
json_object *json_mode; wl_list_for_each_safe(output, tmp_output, &bar->outputs, link) {
json_object_object_get_ex(json_config, "mode", &json_mode); bool found = wl_list_empty(&newcfg->outputs);
config->mode = strdup(json_object_get_string(json_mode)); struct config_output *coutput;
sway_log(SWAY_DEBUG, "Changing bar mode to %s", config->mode); wl_list_for_each(coutput, &newcfg->outputs, link) {
if (strcmp(coutput->name, output->name) == 0 ||
json_object *gaps; strcmp(coutput->name, output->identifier) == 0) {
json_object_object_get_ex(json_config, "gaps", &gaps); found = true;
if (gaps) { break;
json_object *top = json_object_object_get(gaps, "top");
if (top) {
config->gaps.top = json_object_get_int(top);
} }
json_object *right = json_object_object_get(gaps, "right");
if (right) {
config->gaps.right = json_object_get_int(right);
} }
json_object *bottom = json_object_object_get(gaps, "bottom"); if (!found) {
if (bottom) { destroy_layer_surface(output);
config->gaps.bottom = json_object_get_int(bottom); wl_list_remove(&output->link);
wl_list_insert(&bar->unused_outputs, &output->link);
} else if (!oldcfg->font || !newcfg->font ||
strcmp(oldcfg->font, newcfg->font) != 0) {
output->height = 0; // force update height
} }
json_object *left = json_object_object_get(gaps, "left"); }
if (left) { wl_list_for_each_safe(output, tmp_output, &bar->unused_outputs, link) {
config->gaps.left = json_object_get_int(left); bool found = wl_list_empty(&newcfg->outputs);
struct config_output *coutput;
wl_list_for_each(coutput, &newcfg->outputs, link) {
if (strcmp(coutput->name, output->name) == 0 ||
strcmp(coutput->name, output->identifier) == 0) {
found = true;
break;
}
}
if (found) {
wl_list_remove(&output->link);
wl_list_insert(&bar->outputs, &output->link);
} }
} }
return determine_bar_visibility(bar, true); if (bar->status && (!newcfg->status_command ||
strcmp(newcfg->status_command, oldcfg->status_command) != 0)) {
status_line_free(bar->status);
bar->status = NULL;
}
if (!bar->status && newcfg->status_command) {
bar->status = status_line_init(newcfg->status_command);
bar->status->bar = bar;
loop_add_fd(bar->eventloop, bar->status->read_fd, POLLIN,
status_in, bar);
}
#if HAVE_TRAY
if (oldcfg->tray_hidden && !newcfg->tray_hidden) {
bar->tray = create_tray(bar);
loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in,
bar->tray->bus);
} else if (bar->tray && newcfg->tray_hidden) {
loop_remove_fd(bar->eventloop, bar->tray->fd);
destroy_tray(bar->tray);
bar->tray = NULL;
}
#endif
if (newcfg->workspace_buttons) {
ipc_get_workspaces(bar);
}
free_config(oldcfg);
determine_bar_visibility(bar, true);
return true;
} }
bool handle_ipc_readable(struct swaybar *bar) { bool handle_ipc_readable(struct swaybar *bar) {
@ -599,7 +646,7 @@ bool handle_ipc_readable(struct swaybar *bar) {
break; break;
} }
case IPC_EVENT_BARCONFIG_UPDATE: case IPC_EVENT_BARCONFIG_UPDATE:
bar_is_dirty = handle_barconfig_update(bar, result); bar_is_dirty = handle_barconfig_update(bar, resp->payload, result);
break; break;
case IPC_EVENT_BAR_STATE_UPDATE: case IPC_EVENT_BAR_STATE_UPDATE:
bar_is_dirty = handle_bar_state_update(bar, result); bar_is_dirty = handle_bar_state_update(bar, result);

View file

@ -5,6 +5,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include "log.h" #include "log.h"
#include "loop.h" #include "loop.h"
@ -174,6 +176,7 @@ struct status_line *status_line_init(char *cmd) {
void status_line_free(struct status_line *status) { void status_line_free(struct status_line *status) {
status_line_close_fds(status); status_line_close_fds(status);
kill(status->pid, SIGTERM); kill(status->pid, SIGTERM);
waitpid(status->pid, NULL, 0);
if (status->protocol == PROTOCOL_I3BAR) { if (status->protocol == PROTOCOL_I3BAR) {
struct i3bar_block *block, *tmp; struct i3bar_block *block, *tmp;
wl_list_for_each_safe(block, tmp, &status->blocks, link) { wl_list_for_each_safe(block, tmp, &status->blocks, link) {