diff --git a/include/action.h b/include/action.h new file mode 100644 index 00000000..78f3e2ab --- /dev/null +++ b/include/action.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LABWC_ACTION_H +#define __LABWC_ACTION_H + +struct action { + uint32_t type; + char *arg; + struct wl_list link; +}; + +struct action *action_create(const char *action_str); + +#endif diff --git a/include/config/keybind.h b/include/config/keybind.h index 78caf9a3..86eb4b52 100644 --- a/include/config/keybind.h +++ b/include/config/keybind.h @@ -11,8 +11,7 @@ struct keybind { uint32_t modifiers; xkb_keysym_t *keysyms; size_t keysyms_len; - char *action; - char *command; + struct wl_list actions; struct wl_list link; }; diff --git a/include/config/mousebind.h b/include/config/mousebind.h index dc96315f..da43594c 100644 --- a/include/config/mousebind.h +++ b/include/config/mousebind.h @@ -24,8 +24,7 @@ struct mousebind { /* ex: doubleclick, press, drag */ enum mouse_event mouse_event; - const char *action; - const char *command; + struct wl_list actions; struct wl_list link; /* rcxml::mousebinds */ bool pressed_in_context; /* used in click events */ @@ -34,6 +33,5 @@ struct mousebind { enum mouse_event mousebind_event_from_str(const char *str); uint32_t mousebind_button_from_str(const char *str, uint32_t *modifiers); struct mousebind *mousebind_create(const char *context); -struct mousebind *mousebind_create_from(struct mousebind *from, const char *context); #endif /* __LABWC_MOUSEBIND_H */ diff --git a/include/labwc.h b/include/labwc.h index e6a579fc..8823f3ef 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -500,8 +500,8 @@ void server_init(struct server *server); void server_start(struct server *server); void server_finish(struct server *server); -void action(struct view *activator, struct server *server, const char *action, - const char *command, uint32_t resize_edges); +void action(struct view *activator, struct server *server, + struct wl_list *actions, uint32_t resize_edges); /* update onscreen display 'alt-tab' texture */ void osd_update(struct server *server); diff --git a/include/menu/menu.h b/include/menu/menu.h index 8f688c5c..627b9b63 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -6,8 +6,7 @@ #include struct menuitem { - char *action; - char *command; + struct wl_list actions; struct menu *submenu; struct wlr_box box; struct { diff --git a/src/action.c b/src/action.c index 926db063..ab220558 100644 --- a/src/action.c +++ b/src/action.c @@ -4,6 +4,78 @@ #include "common/spawn.h" #include "labwc.h" #include "menu/menu.h" +#include "action.h" + +enum action_type { + ACTION_TYPE_NONE = 0, + ACTION_TYPE_CLOSE, + ACTION_TYPE_DEBUG, + ACTION_TYPE_EXECUTE, + ACTION_TYPE_EXIT, + ACTION_TYPE_MOVE_TO_EDGE, + ACTION_TYPE_SNAP_TO_EDGE, + ACTION_TYPE_NEXT_WINDOW, + ACTION_TYPE_PREVIOUS_WINDOW, + ACTION_TYPE_RECONFIGURE, + ACTION_TYPE_SHOW_MENU, + ACTION_TYPE_TOGGLE_MAXIMIZE, + ACTION_TYPE_TOGGLE_FULLSCREEN, + ACTION_TYPE_TOGGLE_DECORATIONS, + ACTION_TYPE_FOCUS, + ACTION_TYPE_ICONIFY, + ACTION_TYPE_MOVE, + ACTION_TYPE_RAISE, + ACTION_TYPE_RESIZE, +}; + +const char *action_names[] = { + "NoOp", + "Close", + "Debug", + "Execute", + "Exit", + "MoveToEdge", + "SnapToEdge", + "NextWindow", + "PreviousWindow", + "Reconfigure", + "ShowMenu", + "ToggleMaximize", + "ToggleFullscreen", + "ToggleDecorations", + "Focus", + "Iconify", + "Move", + "Raise", + "Resize", + NULL +}; + + +static enum action_type +action_type_from_str(const char *action_name) +{ + for (size_t i=1; action_names[i] != NULL; i++) { + if (!strcasecmp(action_name, action_names[i])) { + return i; + } + } + wlr_log(WLR_ERROR, "Invalid action: %s", action_name); + return ACTION_TYPE_NONE; +} + +struct action * +action_create(const char *action_name) +{ + if (!action_name) { + wlr_log(WLR_ERROR, "action name not specified"); + return NULL; + } + struct action *action = calloc(1, sizeof(struct action)); + action->type = action_type_from_str(action_name); + return action; +} + static void show_menu(struct server *server, const char *menu) @@ -26,85 +98,123 @@ activator_or_focused_view(struct view *activator, struct server *server) } void -action(struct view *activator, struct server *server, const char *action, const char *command, uint32_t resize_edges) +action(struct view *activator, struct server *server, struct wl_list *actions, uint32_t resize_edges) { - if (!action) + if (!actions) { + wlr_log(WLR_ERROR, "empty actions"); return; - if (!strcasecmp(action, "Close")) { - struct view *view = activator_or_focused_view(activator, server); - if (view) { - view_close(view); + } + + struct view *view; + struct action *action; + wl_list_for_each(action, actions, link) { + wlr_log(WLR_DEBUG, "Handling action %s (%u) with arg %s", + action_names[action->type], action->type, action->arg); + + /* Refetch view because it may have been changed due to the previous action */ + view = activator_or_focused_view(activator, server); + + switch(action->type) { + case ACTION_TYPE_CLOSE:; + if (view) { + view_close(view); + } + break; + case ACTION_TYPE_DEBUG:; + /* nothing */ + break; + case ACTION_TYPE_EXECUTE:; + { + struct buf cmd; + buf_init(&cmd); + buf_add(&cmd, action->arg); + buf_expand_shell_variables(&cmd); + spawn_async_no_shell(cmd.buf); + free(cmd.buf); + } + break; + case ACTION_TYPE_EXIT:; + wl_display_terminate(server->wl_display); + break; + case ACTION_TYPE_MOVE_TO_EDGE:; + view_move_to_edge(view, action->arg); + break; + case ACTION_TYPE_SNAP_TO_EDGE:; + view_snap_to_edge(view, action->arg); + break; + case ACTION_TYPE_NEXT_WINDOW:; + server->cycle_view = + desktop_cycle_view(server, server->cycle_view, LAB_CYCLE_DIR_FORWARD); + osd_update(server); + break; + case ACTION_TYPE_PREVIOUS_WINDOW:; + server->cycle_view = + desktop_cycle_view(server, server->cycle_view, LAB_CYCLE_DIR_BACKWARD); + osd_update(server); + break; + case ACTION_TYPE_RECONFIGURE:; + /* Should be changed to signal() */ + spawn_async_no_shell("killall -SIGHUP labwc"); + break; + case ACTION_TYPE_SHOW_MENU:; + show_menu(server, action->arg); + break; + case ACTION_TYPE_TOGGLE_MAXIMIZE:; + if (view) { + view_toggle_maximize(view); + } + break; + case ACTION_TYPE_TOGGLE_FULLSCREEN:; + if (view) { + view_toggle_fullscreen(view); + } + break; + case ACTION_TYPE_TOGGLE_DECORATIONS:; + if (view) { + view_toggle_decorations(view); + } + break; + case ACTION_TYPE_FOCUS:; + view = desktop_view_at_cursor(server); + if (view) { + desktop_focus_and_activate_view(&server->seat, view); + damage_all_outputs(server); + } + break; + case ACTION_TYPE_ICONIFY:; + if (view) { + view_minimize(view, true); + } + break; + case ACTION_TYPE_MOVE:; + view = desktop_view_at_cursor(server); + if (view) { + interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); + } + break; + case ACTION_TYPE_RAISE:; + if (view) { + desktop_move_to_front(view); + damage_all_outputs(server); + } + break; + case ACTION_TYPE_RESIZE:; + view = desktop_view_at_cursor(server); + if (view) { + interactive_begin(view, LAB_INPUT_STATE_RESIZE, resize_edges); + } + break; + case ACTION_TYPE_NONE:; + wlr_log(WLR_ERROR, "Not executing unknown action with arg %s", action->arg); + break; + default:; + /* + * If we get here it must be a BUG caused most likely by + * action_names and action_type being out of sync or by + * adding a new action without installing a handler here. + */ + wlr_log(WLR_ERROR, "Not executing invalid action (%u) with arg %s" + "This is a BUG. Please report.", action->type, action->arg); } - } else if (!strcasecmp(action, "Debug")) { - /* nothing */ - } else if (!strcasecmp(action, "Execute")) { - struct buf cmd; - buf_init(&cmd); - buf_add(&cmd, command); - buf_expand_shell_variables(&cmd); - spawn_async_no_shell(cmd.buf); - free(cmd.buf); - } else if (!strcasecmp(action, "Exit")) { - wl_display_terminate(server->wl_display); - } else if (!strcasecmp(action, "MoveToEdge")) { - view_move_to_edge(activator_or_focused_view(activator, server), command); - } else if (!strcasecmp(action, "SnapToEdge")) { - view_snap_to_edge(activator_or_focused_view(activator, server), command); - } else if (!strcasecmp(action, "NextWindow")) { - server->cycle_view = - desktop_cycle_view(server, server->cycle_view, LAB_CYCLE_DIR_FORWARD); - osd_update(server); - } else if (!strcasecmp(action, "PreviousWindow")) { - server->cycle_view = - desktop_cycle_view(server, server->cycle_view, LAB_CYCLE_DIR_BACKWARD); - osd_update(server); - } else if (!strcasecmp(action, "Reconfigure")) { - spawn_async_no_shell("killall -SIGHUP labwc"); - } else if (!strcasecmp(action, "ShowMenu")) { - show_menu(server, command); - } else if (!strcasecmp(action, "ToggleMaximize")) { - struct view *view = activator_or_focused_view(activator, server); - if (view) { - view_toggle_maximize(view); - } - } else if (!strcasecmp(action, "ToggleFullscreen")) { - struct view *view = activator_or_focused_view(activator, server); - if (view) { - view_toggle_fullscreen(view); - } - } else if (!strcasecmp(action, "ToggleDecorations")) { - struct view *view = activator_or_focused_view(activator, server); - if (view) { - view_toggle_decorations(view); - } - } else if (!strcasecmp(action, "Focus")) { - struct view *view = desktop_view_at_cursor(server); - if (view) { - desktop_focus_and_activate_view(&server->seat, view); - damage_all_outputs(server); - } - } else if (!strcasecmp(action, "Iconify")) { - struct view *view = activator_or_focused_view(activator, server); - if (view) { - view_minimize(view, true); - } - } else if (!strcasecmp(action, "Move")) { - struct view *view = desktop_view_at_cursor(server); - if (view) { - interactive_begin(view, LAB_INPUT_STATE_MOVE, 0); - } - } else if (!strcasecmp(action, "Raise")) { - struct view *view = activator_or_focused_view(activator, server); - if (view) { - desktop_move_to_front(view); - damage_all_outputs(server); - } - } else if (!strcasecmp(action, "Resize")) { - struct view *view = desktop_view_at_cursor(server); - if (view) { - interactive_begin(view, LAB_INPUT_STATE_RESIZE, resize_edges); - } - } else { - wlr_log(WLR_ERROR, "(%s) not supported", action); } } diff --git a/src/config/keybind.c b/src/config/keybind.c index 3a6066ee..88883b8f 100644 --- a/src/config/keybind.c +++ b/src/config/keybind.c @@ -61,5 +61,6 @@ keybind_create(const char *keybind) wl_list_insert(&rc.keybinds, &k->link); k->keysyms = malloc(k->keysyms_len * sizeof(xkb_keysym_t)); memcpy(k->keysyms, keysyms, k->keysyms_len * sizeof(xkb_keysym_t)); + wl_list_init(&k->actions); return k; } diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 903be153..091e5824 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -108,19 +108,6 @@ mousebind_create(const char *context) if (m->context != LAB_SSD_NONE) { wl_list_insert(&rc.mousebinds, &m->link); } - return m; -} - -struct mousebind * -mousebind_create_from(struct mousebind *from, const char *context) -{ - if (!from) { - wlr_log(WLR_ERROR, "invalid mousebind instance specified"); - return NULL; - } - struct mousebind *m = mousebind_create(context); - m->button = from->button; - m->modifiers = from->modifiers; - m->mouse_event = from->mouse_event; + wl_list_init(&m->actions); return m; } diff --git a/src/config/rcxml.c b/src/config/rcxml.c index c73f3aa1..d65c657b 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -13,6 +13,7 @@ #include #include #include +#include "action.h" #include "common/dir.h" #include "common/nodename.h" #include "common/string-helpers.h" @@ -29,6 +30,8 @@ static struct keybind *current_keybind; static struct mousebind *current_mousebind; static struct libinput_category *current_libinput_category; static const char *current_mouse_context; +static struct action *current_keybind_action; +static struct action *current_mousebind_action; enum font_place { FONT_PLACE_UNKNOWN = 0, @@ -59,13 +62,15 @@ fill_keybind(char *nodename, char *content) return; } if (!strcmp(nodename, "name.action")) { - current_keybind->action = strdup(content); + current_keybind_action = action_create(content); + wl_list_insert(current_keybind->actions.prev, + ¤t_keybind_action->link); } else if (!strcmp(nodename, "command.action")) { - current_keybind->command = strdup(content); + current_keybind_action->arg = strdup(content); } else if (!strcmp(nodename, "direction.action")) { - current_keybind->command = strdup(content); + current_keybind_action->arg = strdup(content); } else if (!strcmp(nodename, "menu.action")) { - current_keybind->command = strdup(content); + current_keybind_action->arg = strdup(content); } } @@ -99,15 +104,15 @@ fill_mousebind(char *nodename, char *content) current_mousebind->mouse_event = mousebind_event_from_str(content); } else if (!strcmp(nodename, "name.action")) { - if (current_mousebind->action) { - current_mousebind = mousebind_create_from(current_mousebind, - current_mouse_context); - } - current_mousebind->action = strdup(content); + current_mousebind_action = action_create(content); + wl_list_insert(current_mousebind->actions.prev, + ¤t_mousebind_action->link); } else if (!strcmp(nodename, "command.action")) { - current_mousebind->command = strdup(content); + current_mousebind_action->arg = strdup(content); + } else if (!strcmp(nodename, "direction.action")) { + current_mousebind_action->arg = strdup(content); } else if (!strcmp(nodename, "menu.action")) { - current_mousebind->command = strdup(content); + current_mousebind_action->arg = strdup(content); } } @@ -476,14 +481,19 @@ static struct { static void load_default_key_bindings(void) { + struct keybind *k; + struct action *action; for (int i = 0; key_combos[i].binding; i++) { - struct keybind *k = keybind_create(key_combos[i].binding); + k = keybind_create(key_combos[i].binding); if (!k) { continue; } - k->action = strdup(key_combos[i].action); + + action = action_create(key_combos[i].action); + wl_list_insert(k->actions.prev, &action->link); + if (key_combos[i].command) { - k->command = strdup(key_combos[i].command); + action->arg = strdup(key_combos[i].command); } } } @@ -527,14 +537,19 @@ static struct { static void load_default_mouse_bindings(void) { + struct mousebind *m; + struct action *action; for (int i = 0; mouse_combos[i].context; i++) { - struct mousebind *m = mousebind_create(mouse_combos[i].context); + m = mousebind_create(mouse_combos[i].context); m->button = mousebind_button_from_str(mouse_combos[i].button, &m->modifiers); m->mouse_event = mousebind_event_from_str(mouse_combos[i].event); - m->action = strdup(mouse_combos[i].action); + + action = action_create(mouse_combos[i].action); + wl_list_insert(m->actions.prev, &action->link); + if (mouse_combos[i].command) { - m->command = strdup(mouse_combos[i].command); + action->arg = strdup(mouse_combos[i].command); } } } @@ -635,6 +650,8 @@ no_config: void rcxml_finish(void) { + struct action *action, *action_tmp; + zfree(rc.font_name_activewindow); zfree(rc.font_name_menuitem); zfree(rc.font_name_osd); @@ -643,8 +660,11 @@ rcxml_finish(void) struct keybind *k, *k_tmp; wl_list_for_each_safe(k, k_tmp, &rc.keybinds, link) { wl_list_remove(&k->link); - zfree(k->command); - zfree(k->action); + wl_list_for_each_safe(action, action_tmp, &k->actions, link) { + wl_list_remove(&action->link); + zfree(action->arg); + zfree(action); + } zfree(k->keysyms); zfree(k); } @@ -652,8 +672,11 @@ rcxml_finish(void) struct mousebind *m, *m_tmp; wl_list_for_each_safe(m, m_tmp, &rc.mousebinds, link) { wl_list_remove(&m->link); - zfree(m->command); - zfree(m->action); + wl_list_for_each_safe(action, action_tmp, &m->actions, link) { + wl_list_remove(&action->link); + zfree(action->arg); + zfree(action); + } zfree(m); } diff --git a/src/cursor.c b/src/cursor.c index 55da096c..d9836bee 100644 --- a/src/cursor.c +++ b/src/cursor.c @@ -470,8 +470,7 @@ handle_release_mousebinding(struct view *view, struct server *server, } activated_any = true; activated_any_frame |= mousebind->context == LAB_SSD_FRAME; - action(view, server, mousebind->action, - mousebind->command, resize_edges); + action(view, server, &mousebind->actions, resize_edges); } } return activated_any && activated_any_frame; @@ -532,8 +531,7 @@ handle_press_mousebinding(struct view *view, struct server *server, } activated_any = true; activated_any_frame |= mousebind->context == LAB_SSD_FRAME; - action(view, server, mousebind->action, - mousebind->command, resize_edges); + action(view, server, &mousebind->actions, resize_edges); } } return activated_any && activated_any_frame; diff --git a/src/keyboard.c b/src/keyboard.c index c10e3c9c..72bcb875 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -67,8 +67,7 @@ handle_keybinding(struct server *server, uint32_t modifiers, xkb_keysym_t sym) for (size_t i = 0; i < keybind->keysyms_len; i++) { if (xkb_keysym_to_lower(sym) == keybind->keysyms[i]) { wlr_keyboard_set_repeat_info(kb, 0, 0); - action(NULL, server, keybind->action, - keybind->command, 0); + action(NULL, server, &keybind->actions, 0); return true; } } diff --git a/src/menu/menu.c b/src/menu/menu.c index e4c4201e..1fae6c01 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -18,6 +18,7 @@ #include "labwc.h" #include "menu/menu.h" #include "theme.h" +#include "action.h" #define MENUWIDTH (110) #define MENU_ITEM_PADDING_Y (4) @@ -26,6 +27,7 @@ /* state-machine variables for processing */ static bool in_item; static struct menuitem *current_item; +static struct action *current_item_action; static int menu_level; static struct menu *current_menu; @@ -94,6 +96,7 @@ item_create(struct menu *menu, const char *text) menuitem->texture.offset_x = MENU_ITEM_PADDING_X; wl_list_insert(&menu->menuitems, &menuitem->link); + wl_list_init(&menuitem->actions); return menuitem; } @@ -113,15 +116,17 @@ fill_item(char *nodename, char *content) /* defines the start of a new item */ if (!strcmp(nodename, "label")) { current_item = item_create(current_menu, content); - } - if (!current_item) { - wlr_log(WLR_ERROR, "expect element first"); - return; - } - if (!strcmp(nodename, "name.action")) { - current_item->action = strdup(content); + } else if (!current_item) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); + } else if (!strcmp(nodename, "name.action")) { + current_item_action = action_create(content); + wl_list_insert(current_item->actions.prev, ¤t_item_action->link); + } else if (!current_item_action) { + wlr_log(WLR_ERROR, "expect element first. " + "nodename: '%s' content: '%s'", nodename, content); } else if (!strcmp(nodename, "command.action")) { - current_item->command = strdup(content); + current_item_action->arg = strdup(content); } } @@ -310,9 +315,9 @@ menu_init_rootmenu(struct server *server) } if (wl_list_empty(&server->rootmenu->menuitems)) { current_item = item_create(server->rootmenu, "Reconfigure"); - current_item->action = strdup("Reconfigure"); + fill_item("name.action", "Reconfigure"); current_item = item_create(server->rootmenu, "Exit"); - current_item->action = strdup("Exit"); + fill_item("name.action", "Exit"); } server->rootmenu->visible = true; @@ -323,13 +328,17 @@ void menu_finish(void) { struct menu *menu; + struct action *action, *action_tmp; for (int i = 0; i < nr_menus; ++i) { menu = menus + i; struct menuitem *item, *next; wl_list_for_each_safe(item, next, &menu->menuitems, link) { - zfree(item->action); - zfree(item->command); wl_list_remove(&item->link); + wl_list_for_each_safe(action, action_tmp, &item->actions, link) { + wl_list_remove(&action->link); + zfree(action->arg); + zfree(action); + } free(item); } } @@ -416,7 +425,7 @@ menu_action_selected(struct server *server, struct menu *menu) struct menuitem *menuitem; wl_list_for_each (menuitem, &menu->menuitems, link) { if (menuitem->selected && !menuitem->submenu) { - action(NULL, server, menuitem->action, menuitem->command, 0); + action(NULL, server, &menuitem->actions, 0); break; } if (menuitem->submenu) {