]> git.mdlowis.com Git - proto/labwc.git/commitdiff
action.c: split actions_run()
authortokyo4j <hrak1529@gmail.com>
Fri, 1 Aug 2025 07:06:58 +0000 (16:06 +0900)
committerJohan Malm <johanmalm@users.noreply.github.com>
Sat, 2 Aug 2025 12:19:35 +0000 (13:19 +0100)
src/action.c

index c4fd131ab0907e0eaa8d367bb78234076755a999..0d6f9e323e2cb48adca4951144f6c6bd10a14a3d 100644 (file)
@@ -862,6 +862,488 @@ warp_cursor(struct server *server, struct view *view, const char *to, const char
        cursor_update_focus(server);
 }
 
+static void
+run_action(struct view *view, struct server *server, struct action *action,
+       struct cursor_context *ctx)
+{
+       switch (action->type) {
+       case ACTION_TYPE_CLOSE:
+               if (view) {
+                       view_close(view);
+               }
+               break;
+       case ACTION_TYPE_KILL:
+               if (view) {
+                       /* Send SIGTERM to the process associated with the surface */
+                       assert(view->impl->get_pid);
+                       pid_t pid = view->impl->get_pid(view);
+                       if (pid == getpid()) {
+                               wlr_log(WLR_ERROR, "Preventing sending SIGTERM to labwc");
+                       } else if (pid > 0) {
+                               kill(pid, SIGTERM);
+                       }
+               }
+               break;
+       case ACTION_TYPE_DEBUG:
+               debug_dump_scene(server);
+               break;
+       case ACTION_TYPE_EXECUTE: {
+               struct buf cmd = BUF_INIT;
+               buf_add(&cmd, action_get_str(action, "command", NULL));
+               buf_expand_tilde(&cmd);
+               spawn_async_no_shell(cmd.data);
+               buf_reset(&cmd);
+               break;
+       }
+       case ACTION_TYPE_EXIT:
+               wl_display_terminate(server->wl_display);
+               break;
+       case ACTION_TYPE_MOVE_TO_EDGE:
+               if (view) {
+                       /* Config parsing makes sure that direction is a valid direction */
+                       enum view_edge edge = action_get_int(action, "direction", 0);
+                       bool snap_to_windows = action_get_bool(action, "snapWindows", true);
+                       view_move_to_edge(view, edge, snap_to_windows);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE:
+       case ACTION_TYPE_SNAP_TO_EDGE:
+               if (view) {
+                       /* Config parsing makes sure that direction is a valid direction */
+                       enum view_edge edge = action_get_int(action, "direction", 0);
+                       if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE
+                                       && view->maximized == VIEW_AXIS_NONE
+                                       && !view->fullscreen
+                                       && view_is_tiled(view)
+                                       && view->tiled == edge) {
+                               view_set_untiled(view);
+                               view_apply_natural_geometry(view);
+                               break;
+                       }
+                       view_snap_to_edge(view, edge,
+                               /*across_outputs*/ true,
+                               /*store_natural_geometry*/ true);
+               }
+               break;
+       case ACTION_TYPE_GROW_TO_EDGE:
+               if (view) {
+                       /* Config parsing makes sure that direction is a valid direction */
+                       enum view_edge edge = action_get_int(action, "direction", 0);
+                       view_grow_to_edge(view, edge);
+               }
+               break;
+       case ACTION_TYPE_SHRINK_TO_EDGE:
+               if (view) {
+                       /* Config parsing makes sure that direction is a valid direction */
+                       enum view_edge edge = action_get_int(action, "direction", 0);
+                       view_shrink_to_edge(view, edge);
+               }
+               break;
+       case ACTION_TYPE_NEXT_WINDOW:
+               if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+                       osd_cycle(server, LAB_CYCLE_DIR_FORWARD);
+               } else {
+                       osd_begin(server, LAB_CYCLE_DIR_FORWARD);
+               }
+               break;
+       case ACTION_TYPE_PREVIOUS_WINDOW:
+               if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
+                       osd_cycle(server, LAB_CYCLE_DIR_BACKWARD);
+               } else {
+                       osd_begin(server, LAB_CYCLE_DIR_BACKWARD);
+               }
+               break;
+       case ACTION_TYPE_RECONFIGURE:
+               kill(getpid(), SIGHUP);
+               break;
+       case ACTION_TYPE_SHOW_MENU:
+               show_menu(server, view, ctx,
+                       action_get_str(action, "menu", NULL),
+                       action_get_bool(action, "atCursor", true),
+                       action_get_str(action, "x.position", NULL),
+                       action_get_str(action, "y.position", NULL));
+               break;
+       case ACTION_TYPE_TOGGLE_MAXIMIZE:
+               if (view) {
+                       enum view_axis axis = action_get_int(action,
+                               "direction", VIEW_AXIS_BOTH);
+                       view_toggle_maximize(view, axis);
+               }
+               break;
+       case ACTION_TYPE_MAXIMIZE:
+               if (view) {
+                       enum view_axis axis = action_get_int(action,
+                               "direction", VIEW_AXIS_BOTH);
+                       view_maximize(view, axis,
+                               /*store_natural_geometry*/ true);
+               }
+               break;
+       case ACTION_TYPE_UNMAXIMIZE:
+               if (view) {
+                       enum view_axis axis = action_get_int(action,
+                               "direction", VIEW_AXIS_BOTH);
+                       view_maximize(view, view->maximized & ~axis,
+                               /*store_natural_geometry*/ true);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_FULLSCREEN:
+               if (view) {
+                       view_toggle_fullscreen(view);
+               }
+               break;
+       case ACTION_TYPE_SET_DECORATIONS:
+               if (view) {
+                       enum ssd_mode mode = action_get_int(action,
+                               "decorations", LAB_SSD_MODE_FULL);
+                       bool force_ssd = action_get_bool(action,
+                               "forceSSD", false);
+                       view_set_decorations(view, mode, force_ssd);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_DECORATIONS:
+               if (view) {
+                       view_toggle_decorations(view);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP:
+               if (view) {
+                       view_toggle_always_on_top(view);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM:
+               if (view) {
+                       view_toggle_always_on_bottom(view);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_OMNIPRESENT:
+               if (view) {
+                       view_toggle_visible_on_all_workspaces(view);
+               }
+               break;
+       case ACTION_TYPE_FOCUS:
+               if (view) {
+                       desktop_focus_view(view, /*raise*/ false);
+               }
+               break;
+       case ACTION_TYPE_UNFOCUS:
+               seat_focus_surface(&server->seat, NULL);
+               break;
+       case ACTION_TYPE_ICONIFY:
+               if (view) {
+                       view_minimize(view, true);
+               }
+               break;
+       case ACTION_TYPE_MOVE:
+               if (view) {
+                       interactive_begin(view, LAB_INPUT_STATE_MOVE, 0);
+               }
+               break;
+       case ACTION_TYPE_RAISE:
+               if (view) {
+                       view_move_to_front(view);
+               }
+               break;
+       case ACTION_TYPE_LOWER:
+               if (view) {
+                       view_move_to_back(view);
+               }
+               break;
+       case ACTION_TYPE_RESIZE:
+               if (view) {
+                       uint32_t resize_edges = cursor_get_resize_edges(
+                               server->seat.cursor, ctx);
+                       interactive_begin(view, LAB_INPUT_STATE_RESIZE,
+                               resize_edges);
+               }
+               break;
+       case ACTION_TYPE_RESIZE_RELATIVE:
+               if (view) {
+                       int left = action_get_int(action, "left", 0);
+                       int right = action_get_int(action, "right", 0);
+                       int top = action_get_int(action, "top", 0);
+                       int bottom = action_get_int(action, "bottom", 0);
+                       view_resize_relative(view, left, right, top, bottom);
+               }
+               break;
+       case ACTION_TYPE_MOVETO:
+               if (view) {
+                       int x = action_get_int(action, "x", 0);
+                       int y = action_get_int(action, "y", 0);
+                       struct border margin = ssd_thickness(view);
+                       view_move(view, x + margin.left, y + margin.top);
+               }
+               break;
+       case ACTION_TYPE_RESIZETO:
+               if (view) {
+                       int width = action_get_int(action, "width", 0);
+                       int height = action_get_int(action, "height", 0);
+
+                       /*
+                        * To support only setting one of width/height
+                        * in <action name="ResizeTo" width="" height=""/>
+                        * we fall back to current dimension when unset.
+                        */
+                       struct wlr_box box = {
+                               .x = view->pending.x,
+                               .y = view->pending.y,
+                               .width = width ? : view->pending.width,
+                               .height = height ? : view->pending.height,
+                       };
+                       view_set_shade(view, false);
+                       view_move_resize(view, box);
+               }
+               break;
+       case ACTION_TYPE_MOVE_RELATIVE:
+               if (view) {
+                       int x = action_get_int(action, "x", 0);
+                       int y = action_get_int(action, "y", 0);
+                       view_move_relative(view, x, y);
+               }
+               break;
+       case ACTION_TYPE_MOVETO_CURSOR:
+               wlr_log(WLR_ERROR,
+                       "Action MoveToCursor is deprecated. To ensure your config works in future labwc "
+                       "releases, please use <action name=\"AutoPlace\" policy=\"cursor\">");
+               if (view) {
+                       view_move_to_cursor(view);
+               }
+               break;
+       case ACTION_TYPE_SEND_TO_DESKTOP:
+               if (!view) {
+                       break;
+               }
+               /* Falls through to GoToDesktop */
+       case ACTION_TYPE_GO_TO_DESKTOP: {
+               bool follow = true;
+               bool wrap = action_get_bool(action, "wrap", true);
+               const char *to = action_get_str(action, "to", NULL);
+               /*
+                * `to` is always != NULL here because otherwise we would have
+                * removed the action during the initial parsing step as it is
+                * a required argument for both SendToDesktop and GoToDesktop.
+                */
+               struct workspace *target_workspace = workspaces_find(
+                       server->workspaces.current, to, wrap);
+               if (!target_workspace) {
+                       break;
+               }
+               if (action->type == ACTION_TYPE_SEND_TO_DESKTOP) {
+                       view_move_to_workspace(view, target_workspace);
+                       follow = action_get_bool(action, "follow", true);
+
+                       /* Ensure that the focus is not on another desktop */
+                       if (!follow && server->active_view == view) {
+                               desktop_focus_topmost_view(server);
+                       }
+               }
+               if (follow) {
+                       workspaces_switch_to(target_workspace,
+                               /*update_focus*/ true);
+               }
+               break;
+       }
+       case ACTION_TYPE_MOVE_TO_OUTPUT: {
+               if (!view) {
+                       break;
+               }
+               struct output *target_output =
+                       get_target_output(view->output, server, action);
+               if (target_output) {
+                       view_move_to_output(view, target_output);
+               }
+               break;
+       }
+       case ACTION_TYPE_FIT_TO_OUTPUT:
+               if (!view) {
+                       break;
+               }
+               view_constrain_size_to_that_of_usable_area(view);
+               break;
+       case ACTION_TYPE_TOGGLE_SNAP_TO_REGION:
+       case ACTION_TYPE_SNAP_TO_REGION: {
+               if (!view) {
+                       break;
+               }
+               struct output *output = view->output;
+               if (!output) {
+                       break;
+               }
+               const char *region_name = action_get_str(action, "region", NULL);
+               struct region *region = regions_from_name(region_name, output);
+               if (region) {
+                       if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_REGION
+                                       && view->maximized == VIEW_AXIS_NONE
+                                       && !view->fullscreen
+                                       && view_is_tiled(view)
+                                       && view->tiled_region == region) {
+                               view_set_untiled(view);
+                               view_apply_natural_geometry(view);
+                               break;
+                       }
+                       view_snap_to_region(view, region,
+                               /*store_natural_geometry*/ true);
+               } else {
+                       wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name);
+               }
+               break;
+       }
+       case ACTION_TYPE_UNSNAP:
+               if (view && !view->fullscreen && !view_is_floating(view)) {
+                       view_maximize(view, VIEW_AXIS_NONE,
+                               /* store_natural_geometry */ false);
+                       view_set_untiled(view);
+                       view_apply_natural_geometry(view);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_KEYBINDS:
+               if (view) {
+                       view_toggle_keybinds(view);
+               }
+               break;
+       case ACTION_TYPE_FOCUS_OUTPUT: {
+               struct output *output = output_nearest_to_cursor(server);
+               struct output *target_output =
+                       get_target_output(output, server, action);
+               if (target_output) {
+                       desktop_focus_output(target_output);
+               }
+               break;
+       }
+       case ACTION_TYPE_IF:
+               if (view) {
+                       run_if_action(view, server, action);
+               }
+               break;
+       case ACTION_TYPE_FOR_EACH: {
+               struct wl_array views;
+               struct view **item;
+               bool matches = false;
+               wl_array_init(&views);
+               view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE);
+               wl_array_for_each(item, &views) {
+                       matches |= run_if_action(*item, server, action);
+               }
+               wl_array_release(&views);
+               if (!matches) {
+                       struct wl_list *child_actions;
+                       child_actions = action_get_actionlist(action, "none");
+                       if (child_actions) {
+                               actions_run(view, server, child_actions, NULL);
+                       }
+               }
+               break;
+       }
+       case ACTION_TYPE_VIRTUAL_OUTPUT_ADD: {
+               /* TODO: rename this argument to "outputName" */
+               const char *output_name =
+                       action_get_str(action, "output_name", NULL);
+               output_virtual_add(server, output_name,
+                               /*store_wlr_output*/ NULL);
+               break;
+       }
+       case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: {
+               /* TODO: rename this argument to "outputName" */
+               const char *output_name =
+                       action_get_str(action, "output_name", NULL);
+               output_virtual_remove(server, output_name);
+               break;
+       }
+       case ACTION_TYPE_AUTO_PLACE:
+               if (view) {
+                       enum view_placement_policy policy =
+                               action_get_int(action, "policy", LAB_PLACE_AUTOMATIC);
+                       view_place_by_policy(view,
+                               /* allow_cursor */ true, policy);
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_TEARING:
+               if (view) {
+                       switch (view->force_tearing) {
+                       case LAB_STATE_UNSPECIFIED:
+                               view->force_tearing =
+                                       output_get_tearing_allowance(view->output)
+                                               ? LAB_STATE_DISABLED : LAB_STATE_ENABLED;
+                               break;
+                       case LAB_STATE_DISABLED:
+                               view->force_tearing = LAB_STATE_ENABLED;
+                               break;
+                       case LAB_STATE_ENABLED:
+                               view->force_tearing = LAB_STATE_DISABLED;
+                               break;
+                       }
+                       wlr_log(WLR_ERROR, "force tearing %sabled",
+                               view->force_tearing == LAB_STATE_ENABLED
+                                       ? "en" : "dis");
+               }
+               break;
+       case ACTION_TYPE_TOGGLE_SHADE:
+               if (view) {
+                       view_set_shade(view, !view->shaded);
+               }
+               break;
+       case ACTION_TYPE_SHADE:
+               if (view) {
+                       view_set_shade(view, true);
+               }
+               break;
+       case ACTION_TYPE_UNSHADE:
+               if (view) {
+                       view_set_shade(view, false);
+               }
+               break;
+       case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION:
+               server->seat.cursor_scroll_wheel_emulation = true;
+               break;
+       case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION:
+               server->seat.cursor_scroll_wheel_emulation = false;
+               break;
+       case ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION:
+               server->seat.cursor_scroll_wheel_emulation =
+                       !server->seat.cursor_scroll_wheel_emulation;
+               break;
+       case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION:
+               rc.tablet.force_mouse_emulation = true;
+               break;
+       case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION:
+               rc.tablet.force_mouse_emulation = false;
+               break;
+       case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION:
+               rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation;
+               break;
+       case ACTION_TYPE_TOGGLE_MAGNIFY:
+               magnifier_toggle(server);
+               break;
+       case ACTION_TYPE_ZOOM_IN:
+               magnifier_set_scale(server, MAGNIFY_INCREASE);
+               break;
+       case ACTION_TYPE_ZOOM_OUT:
+               magnifier_set_scale(server, MAGNIFY_DECREASE);
+               break;
+       case ACTION_TYPE_WARP_CURSOR: {
+               const char *to = action_get_str(action, "to", "output");
+               const char *x = action_get_str(action, "x", "center");
+               const char *y = action_get_str(action, "y", "center");
+               warp_cursor(server, view, to, x, y);
+               break;
+       }
+       case ACTION_TYPE_HIDE_CURSOR:
+               cursor_set_visible(&server->seat, false);
+               break;
+       case ACTION_TYPE_INVALID:
+               wlr_log(WLR_ERROR, "Not executing unknown action");
+               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)"
+                       " This is a BUG. Please report.", action->type);
+       }
+}
+
 void
 actions_run(struct view *activator, struct server *server,
        struct wl_list *actions, struct cursor_context *cursor_ctx)
@@ -898,481 +1380,6 @@ actions_run(struct view *activator, struct server *server,
                 */
                struct view *view = view_for_action(activator, server, action, &ctx);
 
-               switch (action->type) {
-               case ACTION_TYPE_CLOSE:
-                       if (view) {
-                               view_close(view);
-                       }
-                       break;
-               case ACTION_TYPE_KILL:
-                       if (view) {
-                               /* Send SIGTERM to the process associated with the surface */
-                               assert(view->impl->get_pid);
-                               pid_t pid = view->impl->get_pid(view);
-                               if (pid == getpid()) {
-                                       wlr_log(WLR_ERROR, "Preventing sending SIGTERM to labwc");
-                               } else if (pid > 0) {
-                                       kill(pid, SIGTERM);
-                               }
-                       }
-                       break;
-               case ACTION_TYPE_DEBUG:
-                       debug_dump_scene(server);
-                       break;
-               case ACTION_TYPE_EXECUTE: {
-                       struct buf cmd = BUF_INIT;
-                       buf_add(&cmd, action_get_str(action, "command", NULL));
-                       buf_expand_tilde(&cmd);
-                       spawn_async_no_shell(cmd.data);
-                       buf_reset(&cmd);
-                       break;
-               }
-               case ACTION_TYPE_EXIT:
-                       wl_display_terminate(server->wl_display);
-                       break;
-               case ACTION_TYPE_MOVE_TO_EDGE:
-                       if (view) {
-                               /* Config parsing makes sure that direction is a valid direction */
-                               enum view_edge edge = action_get_int(action, "direction", 0);
-                               bool snap_to_windows = action_get_bool(action, "snapWindows", true);
-                               view_move_to_edge(view, edge, snap_to_windows);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_SNAP_TO_EDGE:
-               case ACTION_TYPE_SNAP_TO_EDGE:
-                       if (view) {
-                               /* Config parsing makes sure that direction is a valid direction */
-                               enum view_edge edge = action_get_int(action, "direction", 0);
-                               if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_EDGE
-                                               && view->maximized == VIEW_AXIS_NONE
-                                               && !view->fullscreen
-                                               && view_is_tiled(view)
-                                               && view->tiled == edge) {
-                                       view_set_untiled(view);
-                                       view_apply_natural_geometry(view);
-                                       break;
-                               }
-                               view_snap_to_edge(view, edge,
-                                       /*across_outputs*/ true,
-                                       /*store_natural_geometry*/ true);
-                       }
-                       break;
-               case ACTION_TYPE_GROW_TO_EDGE:
-                       if (view) {
-                               /* Config parsing makes sure that direction is a valid direction */
-                               enum view_edge edge = action_get_int(action, "direction", 0);
-                               view_grow_to_edge(view, edge);
-                       }
-                       break;
-               case ACTION_TYPE_SHRINK_TO_EDGE:
-                       if (view) {
-                               /* Config parsing makes sure that direction is a valid direction */
-                               enum view_edge edge = action_get_int(action, "direction", 0);
-                               view_shrink_to_edge(view, edge);
-                       }
-                       break;
-               case ACTION_TYPE_NEXT_WINDOW:
-                       if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
-                               osd_cycle(server, LAB_CYCLE_DIR_FORWARD);
-                       } else {
-                               osd_begin(server, LAB_CYCLE_DIR_FORWARD);
-                       }
-                       break;
-               case ACTION_TYPE_PREVIOUS_WINDOW:
-                       if (server->input_mode == LAB_INPUT_STATE_WINDOW_SWITCHER) {
-                               osd_cycle(server, LAB_CYCLE_DIR_BACKWARD);
-                       } else {
-                               osd_begin(server, LAB_CYCLE_DIR_BACKWARD);
-                       }
-                       break;
-               case ACTION_TYPE_RECONFIGURE:
-                       kill(getpid(), SIGHUP);
-                       break;
-               case ACTION_TYPE_SHOW_MENU:
-                       show_menu(server, view, &ctx,
-                               action_get_str(action, "menu", NULL),
-                               action_get_bool(action, "atCursor", true),
-                               action_get_str(action, "x.position", NULL),
-                               action_get_str(action, "y.position", NULL));
-                       break;
-               case ACTION_TYPE_TOGGLE_MAXIMIZE:
-                       if (view) {
-                               enum view_axis axis = action_get_int(action,
-                                       "direction", VIEW_AXIS_BOTH);
-                               view_toggle_maximize(view, axis);
-                       }
-                       break;
-               case ACTION_TYPE_MAXIMIZE:
-                       if (view) {
-                               enum view_axis axis = action_get_int(action,
-                                       "direction", VIEW_AXIS_BOTH);
-                               view_maximize(view, axis,
-                                       /*store_natural_geometry*/ true);
-                       }
-                       break;
-               case ACTION_TYPE_UNMAXIMIZE:
-                       if (view) {
-                               enum view_axis axis = action_get_int(action,
-                                       "direction", VIEW_AXIS_BOTH);
-                               view_maximize(view, view->maximized & ~axis,
-                                       /*store_natural_geometry*/ true);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_FULLSCREEN:
-                       if (view) {
-                               view_toggle_fullscreen(view);
-                       }
-                       break;
-               case ACTION_TYPE_SET_DECORATIONS:
-                       if (view) {
-                               enum ssd_mode mode = action_get_int(action,
-                                       "decorations", LAB_SSD_MODE_FULL);
-                               bool force_ssd = action_get_bool(action,
-                                       "forceSSD", false);
-                               view_set_decorations(view, mode, force_ssd);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_DECORATIONS:
-                       if (view) {
-                               view_toggle_decorations(view);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_ALWAYS_ON_TOP:
-                       if (view) {
-                               view_toggle_always_on_top(view);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_ALWAYS_ON_BOTTOM:
-                       if (view) {
-                               view_toggle_always_on_bottom(view);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_OMNIPRESENT:
-                       if (view) {
-                               view_toggle_visible_on_all_workspaces(view);
-                       }
-                       break;
-               case ACTION_TYPE_FOCUS:
-                       if (view) {
-                               desktop_focus_view(view, /*raise*/ false);
-                       }
-                       break;
-               case ACTION_TYPE_UNFOCUS:
-                       seat_focus_surface(&server->seat, NULL);
-                       break;
-               case ACTION_TYPE_ICONIFY:
-                       if (view) {
-                               view_minimize(view, true);
-                       }
-                       break;
-               case ACTION_TYPE_MOVE:
-                       if (view) {
-                               interactive_begin(view, LAB_INPUT_STATE_MOVE, 0);
-                       }
-                       break;
-               case ACTION_TYPE_RAISE:
-                       if (view) {
-                               view_move_to_front(view);
-                       }
-                       break;
-               case ACTION_TYPE_LOWER:
-                       if (view) {
-                               view_move_to_back(view);
-                       }
-                       break;
-               case ACTION_TYPE_RESIZE:
-                       if (view) {
-                               uint32_t resize_edges = cursor_get_resize_edges(
-                                       server->seat.cursor, &ctx);
-                               interactive_begin(view, LAB_INPUT_STATE_RESIZE,
-                                       resize_edges);
-                       }
-                       break;
-               case ACTION_TYPE_RESIZE_RELATIVE:
-                       if (view) {
-                               int left = action_get_int(action, "left", 0);
-                               int right = action_get_int(action, "right", 0);
-                               int top = action_get_int(action, "top", 0);
-                               int bottom = action_get_int(action, "bottom", 0);
-                               view_resize_relative(view, left, right, top, bottom);
-                       }
-                       break;
-               case ACTION_TYPE_MOVETO:
-                       if (view) {
-                               int x = action_get_int(action, "x", 0);
-                               int y = action_get_int(action, "y", 0);
-                               struct border margin = ssd_thickness(view);
-                               view_move(view, x + margin.left, y + margin.top);
-                       }
-                       break;
-               case ACTION_TYPE_RESIZETO:
-                       if (view) {
-                               int width = action_get_int(action, "width", 0);
-                               int height = action_get_int(action, "height", 0);
-
-                               /*
-                                * To support only setting one of width/height
-                                * in <action name="ResizeTo" width="" height=""/>
-                                * we fall back to current dimension when unset.
-                                */
-                               struct wlr_box box = {
-                                       .x = view->pending.x,
-                                       .y = view->pending.y,
-                                       .width = width ? : view->pending.width,
-                                       .height = height ? : view->pending.height,
-                               };
-                               view_set_shade(view, false);
-                               view_move_resize(view, box);
-                       }
-                       break;
-               case ACTION_TYPE_MOVE_RELATIVE:
-                       if (view) {
-                               int x = action_get_int(action, "x", 0);
-                               int y = action_get_int(action, "y", 0);
-                               view_move_relative(view, x, y);
-                       }
-                       break;
-               case ACTION_TYPE_MOVETO_CURSOR:
-                       wlr_log(WLR_ERROR,
-                               "Action MoveToCursor is deprecated. To ensure your config works in future labwc "
-                               "releases, please use <action name=\"AutoPlace\" policy=\"cursor\">");
-                       if (view) {
-                               view_move_to_cursor(view);
-                       }
-                       break;
-               case ACTION_TYPE_SEND_TO_DESKTOP:
-                       if (!view) {
-                               break;
-                       }
-                       /* Falls through to GoToDesktop */
-               case ACTION_TYPE_GO_TO_DESKTOP: {
-                       bool follow = true;
-                       bool wrap = action_get_bool(action, "wrap", true);
-                       const char *to = action_get_str(action, "to", NULL);
-                       /*
-                        * `to` is always != NULL here because otherwise we would have
-                        * removed the action during the initial parsing step as it is
-                        * a required argument for both SendToDesktop and GoToDesktop.
-                        */
-                       struct workspace *target_workspace = workspaces_find(
-                               server->workspaces.current, to, wrap);
-                       if (!target_workspace) {
-                               break;
-                       }
-                       if (action->type == ACTION_TYPE_SEND_TO_DESKTOP) {
-                               view_move_to_workspace(view, target_workspace);
-                               follow = action_get_bool(action, "follow", true);
-
-                               /* Ensure that the focus is not on another desktop */
-                               if (!follow && server->active_view == view) {
-                                       desktop_focus_topmost_view(server);
-                               }
-                       }
-                       if (follow) {
-                               workspaces_switch_to(target_workspace,
-                                       /*update_focus*/ true);
-                       }
-                       break;
-               }
-               case ACTION_TYPE_MOVE_TO_OUTPUT: {
-                       if (!view) {
-                               break;
-                       }
-                       struct output *target_output =
-                               get_target_output(view->output, server, action);
-                       if (target_output) {
-                               view_move_to_output(view, target_output);
-                       }
-                       break;
-               }
-               case ACTION_TYPE_FIT_TO_OUTPUT:
-                       if (!view) {
-                               break;
-                       }
-                       view_constrain_size_to_that_of_usable_area(view);
-                       break;
-               case ACTION_TYPE_TOGGLE_SNAP_TO_REGION:
-               case ACTION_TYPE_SNAP_TO_REGION: {
-                       if (!view) {
-                               break;
-                       }
-                       struct output *output = view->output;
-                       if (!output) {
-                               break;
-                       }
-                       const char *region_name = action_get_str(action, "region", NULL);
-                       struct region *region = regions_from_name(region_name, output);
-                       if (region) {
-                               if (action->type == ACTION_TYPE_TOGGLE_SNAP_TO_REGION
-                                               && view->maximized == VIEW_AXIS_NONE
-                                               && !view->fullscreen
-                                               && view_is_tiled(view)
-                                               && view->tiled_region == region) {
-                                       view_set_untiled(view);
-                                       view_apply_natural_geometry(view);
-                                       break;
-                               }
-                               view_snap_to_region(view, region,
-                                       /*store_natural_geometry*/ true);
-                       } else {
-                               wlr_log(WLR_ERROR, "Invalid SnapToRegion id: '%s'", region_name);
-                       }
-                       break;
-               }
-               case ACTION_TYPE_UNSNAP:
-                       if (view && !view->fullscreen && !view_is_floating(view)) {
-                               view_maximize(view, VIEW_AXIS_NONE,
-                                       /* store_natural_geometry */ false);
-                               view_set_untiled(view);
-                               view_apply_natural_geometry(view);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_KEYBINDS:
-                       if (view) {
-                               view_toggle_keybinds(view);
-                       }
-                       break;
-               case ACTION_TYPE_FOCUS_OUTPUT: {
-                       struct output *output = output_nearest_to_cursor(server);
-                       struct output *target_output =
-                               get_target_output(output, server, action);
-                       if (target_output) {
-                               desktop_focus_output(target_output);
-                       }
-                       break;
-               }
-               case ACTION_TYPE_IF:
-                       if (view) {
-                               run_if_action(view, server, action);
-                       }
-                       break;
-               case ACTION_TYPE_FOR_EACH: {
-                       struct wl_array views;
-                       struct view **item;
-                       bool matches = false;
-                       wl_array_init(&views);
-                       view_array_append(server, &views, LAB_VIEW_CRITERIA_NONE);
-                       wl_array_for_each(item, &views) {
-                               matches |= run_if_action(*item, server, action);
-                       }
-                       wl_array_release(&views);
-                       if (!matches) {
-                               struct wl_list *child_actions;
-                               child_actions = action_get_actionlist(action, "none");
-                               if (child_actions) {
-                                       actions_run(view, server, child_actions, NULL);
-                               }
-                       }
-                       break;
-               }
-               case ACTION_TYPE_VIRTUAL_OUTPUT_ADD: {
-                       /* TODO: rename this argument to "outputName" */
-                       const char *output_name =
-                               action_get_str(action, "output_name", NULL);
-                       output_virtual_add(server, output_name,
-                                       /*store_wlr_output*/ NULL);
-                       break;
-               }
-               case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE: {
-                       /* TODO: rename this argument to "outputName" */
-                       const char *output_name =
-                               action_get_str(action, "output_name", NULL);
-                       output_virtual_remove(server, output_name);
-                       break;
-               }
-               case ACTION_TYPE_AUTO_PLACE:
-                       if (view) {
-                               enum view_placement_policy policy =
-                                       action_get_int(action, "policy", LAB_PLACE_AUTOMATIC);
-                               view_place_by_policy(view,
-                                       /* allow_cursor */ true, policy);
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_TEARING:
-                       if (view) {
-                               switch (view->force_tearing) {
-                               case LAB_STATE_UNSPECIFIED:
-                                       view->force_tearing =
-                                               output_get_tearing_allowance(view->output)
-                                                       ? LAB_STATE_DISABLED : LAB_STATE_ENABLED;
-                                       break;
-                               case LAB_STATE_DISABLED:
-                                       view->force_tearing = LAB_STATE_ENABLED;
-                                       break;
-                               case LAB_STATE_ENABLED:
-                                       view->force_tearing = LAB_STATE_DISABLED;
-                                       break;
-                               }
-                               wlr_log(WLR_ERROR, "force tearing %sabled",
-                                       view->force_tearing == LAB_STATE_ENABLED
-                                               ? "en" : "dis");
-                       }
-                       break;
-               case ACTION_TYPE_TOGGLE_SHADE:
-                       if (view) {
-                               view_set_shade(view, !view->shaded);
-                       }
-                       break;
-               case ACTION_TYPE_SHADE:
-                       if (view) {
-                               view_set_shade(view, true);
-                       }
-                       break;
-               case ACTION_TYPE_UNSHADE:
-                       if (view) {
-                               view_set_shade(view, false);
-                       }
-                       break;
-               case ACTION_TYPE_ENABLE_SCROLL_WHEEL_EMULATION:
-                       server->seat.cursor_scroll_wheel_emulation = true;
-                       break;
-               case ACTION_TYPE_DISABLE_SCROLL_WHEEL_EMULATION:
-                       server->seat.cursor_scroll_wheel_emulation = false;
-                       break;
-               case ACTION_TYPE_TOGGLE_SCROLL_WHEEL_EMULATION:
-                       server->seat.cursor_scroll_wheel_emulation =
-                               !server->seat.cursor_scroll_wheel_emulation;
-                       break;
-               case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION:
-                       rc.tablet.force_mouse_emulation = true;
-                       break;
-               case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION:
-                       rc.tablet.force_mouse_emulation = false;
-                       break;
-               case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION:
-                       rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation;
-                       break;
-               case ACTION_TYPE_TOGGLE_MAGNIFY:
-                       magnifier_toggle(server);
-                       break;
-               case ACTION_TYPE_ZOOM_IN:
-                       magnifier_set_scale(server, MAGNIFY_INCREASE);
-                       break;
-               case ACTION_TYPE_ZOOM_OUT:
-                       magnifier_set_scale(server, MAGNIFY_DECREASE);
-                       break;
-               case ACTION_TYPE_WARP_CURSOR: {
-                       const char *to = action_get_str(action, "to", "output");
-                       const char *x = action_get_str(action, "x", "center");
-                       const char *y = action_get_str(action, "y", "center");
-                       warp_cursor(server, view, to, x, y);
-                       break;
-               }
-               case ACTION_TYPE_HIDE_CURSOR:
-                       cursor_set_visible(&server->seat, false);
-                       break;
-               case ACTION_TYPE_INVALID:
-                       wlr_log(WLR_ERROR, "Not executing unknown action");
-                       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)"
-                               " This is a BUG. Please report.", action->type);
-               }
+               run_action(view, server, action, &ctx);
        }
 }