From 495ba1e96ec83f7d1e481614cc22d100e456b15d Mon Sep 17 00:00:00 2001 From: OXDBXKXO <18035000+OXDBXKXO@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:44:33 +0200 Subject: [PATCH 1/2] ipc-server: add get_cursor IPC command that returns cursor position and information about the node under it --- include/ipc.h | 1 + sway/ipc-server.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ sway/sway-ipc.7.scd | 68 +++++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) diff --git a/include/ipc.h b/include/ipc.h index ff011750..42bb8ccb 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -22,6 +22,7 @@ enum ipc_command_type { // sway-specific command types IPC_GET_INPUTS = 100, IPC_GET_SEATS = 101, + IPC_GET_CURSOR = 102, // Events sent from sway to clients. Events have the highest bits set. IPC_EVENT_WORKSPACE = ((1<<31) | 0), diff --git a/sway/ipc-server.c b/sway/ipc-server.c index b934bb56..b69b3c6a 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/desktop/transaction.h" @@ -24,9 +25,12 @@ #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" +#include "sway/input/cursor.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#include "sway/tree/node.h" +#include "sway/tree/container.h" #include "list.h" #include "log.h" #include "util.h" @@ -811,6 +815,100 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt goto exit_cleanup; } + case IPC_GET_CURSOR: + { + json_object *cursor_info = json_object_new_object(); + struct sway_seat *seat = input_manager_get_default_seat(); + + if (seat && seat->cursor) { + json_object_object_add(cursor_info, "x", + json_object_new_int((int)seat->cursor->cursor->x)); + json_object_object_add(cursor_info, "y", + json_object_new_int((int)seat->cursor->cursor->y)); + + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + seat->cursor->cursor->x, seat->cursor->cursor->y, &surface, &sx, &sy); + + if (node) { + const char *node_type_str; + switch (node->type) { + case N_ROOT: + node_type_str = "root"; + break; + case N_OUTPUT: + node_type_str = "output"; + break; + case N_WORKSPACE: + node_type_str = "workspace"; + break; + case N_CONTAINER: + node_type_str = "con"; + break; + default: + node_type_str = "none"; + break; + } + + json_object_object_add(cursor_info, "node_type", + json_object_new_string(node_type_str)); + + // Node-specific information + if (node->type == N_CONTAINER) { + json_object_object_add(cursor_info, "surface_x", + json_object_new_int((int)sx)); + json_object_object_add(cursor_info, "surface_y", + json_object_new_int((int)sy)); + + struct sway_container *container = node->sway_container; + if (container->title) { + json_object_object_add(cursor_info, "window_title", + json_object_new_string(container->title)); + } + if (container->view) { + // Handle both XDG and XWayland views + const char *app_id = NULL; + if (container->view->type == SWAY_VIEW_XDG_SHELL) { + app_id = view_get_app_id(container->view); + } +#if WLR_HAS_XWAYLAND + else if (container->view->type == SWAY_VIEW_XWAYLAND) { + app_id = view_get_class(container->view); + } +#endif + + if (app_id) { + json_object_object_add(cursor_info, "app_id", + json_object_new_string(app_id)); + } + + if (container->view->pid > 0) { + json_object_object_add(cursor_info, "pid", + json_object_new_int(container->view->pid)); + } + } + + if (container->pending.workspace && container->pending.workspace->name) { + json_object_object_add(cursor_info, "workspace_name", + json_object_new_string(container->pending.workspace->name)); + } + } else if (node->type == N_WORKSPACE) { + struct sway_workspace *workspace = node->sway_workspace; + if (workspace->name) { + json_object_object_add(cursor_info, "workspace_name", + json_object_new_string(workspace->name)); + } + } + } + } + + const char *json_string = json_object_to_json_string(cursor_info); + ipc_send_reply(client, payload_type, json_string, (uint32_t)strlen(json_string)); + json_object_put(cursor_info); + break; + } + case IPC_GET_TREE: { json_object *tree = ipc_json_describe_node_recursive(&root->node); diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 833db0ef..b1ef7578 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -84,6 +84,9 @@ supported. *For all replies, any properties not listed are subject to removal.* |- 101 : GET_SEATS : Get the list of seats +|- 102 +: GET_CURSOR +: Get the current cursor position and information about the element under it ## 0. RUN_COMMAND @@ -1458,6 +1461,71 @@ one seat. Each object has the following properties: ] ``` +## 102. GET_CURSOR + +*MESSAGE*++ +Retrieve the current cursor position and information about the element under it + +*REPLY*++ +An object containing the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- x +: integer +:[ The x coordinate of the cursor (absolute screen coordinates) +|- y +: integer +: The y coordinate of the cursor (absolute screen coordinates) +|- surface_x +: integer +: The x coordinate of the cursor relative to the surface under it (only available when over a container) +|- surface_y +: integer +: The y coordinate of the cursor relative to the surface under it (only available when over a container) +|- node_type +: string +: The type of the node under the cursor (e.g., "con", "workspace", "output") +|- window_title +: string +: The title of the window under the cursor (if applicable) +|- app_id +: string +: The application ID of the window under the cursor (if applicable) +|- workspace_name +: string +: The name of the workspace under the cursor (if applicable) +|- pid +: integer +: The process ID of the application under the cursor (if applicable) + + +*Example Reply (cursor over container):* +``` +{ + "x": 1024, + "y": 768, + "surface_x": 24, + "surface_y": 48, + "node_type": "con", + "window_title": "Terminal", + "app_id": "termite", + "workspace_name": "1", + "pid": 25370 +} +``` + +*Example Reply (cursor over workspace):* +``` +{ + "x": 1024, + "y": 768, + "node_type": "workspace", + "workspace_name": "1" +} +``` + # EVENTS Events are a way for client to get notified of changes to sway. A client can From 945abfc49a9b0d97ac06f3719c77297e5aaabbe3 Mon Sep 17 00:00:00 2001 From: OXDBXKXO <18035000+OXDBXKXO@users.noreply.github.com> Date: Wed, 25 Jun 2025 23:47:28 +0200 Subject: [PATCH 2/2] swaymsg: get_cursor support --- completions/fish/swaymsg.fish | 1 + completions/zsh/_swaymsg | 1 + swaymsg/main.c | 46 ++++++++++++++++++++++++++++++++++- swaymsg/swaymsg.1.scd | 3 +++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index 5ee7dcc0..47e298c9 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -21,5 +21,6 @@ complete -c swaymsg -s t -l type -fra 'get_binding_modes' --description "Gets a complete -c swaymsg -s t -l type -fra 'get_binding_state' --description "Get JSON-encoded info about the current binding state." complete -c swaymsg -s t -l type -fra 'get_config' --description "Gets a JSON-encoded copy of the current configuration." complete -c swaymsg -s t -l type -fra 'get_seats' --description "Gets a JSON-encoded list of all seats, its properties and all assigned devices." +complete -c swaymsg -s t -l type -fra 'get_cursor' --description "Get JSON-encoded information about the current cursor position and the node under it." complete -c swaymsg -s t -l type -fra 'send_tick' --description "Sends a tick event to all subscribed clients." complete -c swaymsg -s t -l type -fra 'subscribe' --description "Subscribe to a list of event types." diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 106f3d9d..f22ca84f 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -25,6 +25,7 @@ types=( 'get_binding_modes' 'get_binding_state' 'get_config' +'get_cursor' 'send_tick' 'subscribe' ) diff --git a/swaymsg/main.c b/swaymsg/main.c index 6a9eb198..7f2efbdb 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -1,4 +1,3 @@ - #include #include #include @@ -377,6 +376,46 @@ static void pretty_print_tree(json_object *obj, int indent) { } } +static void pretty_print_cursor(json_object *c) { + json_object *x, *y, *surface_x, *surface_y, *node_type, *window_title, *app_id, *workspace_name, *pid; + json_object_object_get_ex(c, "x", &x); + json_object_object_get_ex(c, "y", &y); + json_object_object_get_ex(c, "surface_x", &surface_x); + json_object_object_get_ex(c, "surface_y", &surface_y); + json_object_object_get_ex(c, "node_type", &node_type); + json_object_object_get_ex(c, "window_title", &window_title); + json_object_object_get_ex(c, "app_id", &app_id); + json_object_object_get_ex(c, "workspace_name", &workspace_name); + json_object_object_get_ex(c, "pid", &pid); + + printf("Cursor position: %d, %d", + json_object_get_int(x), json_object_get_int(y)); + + if (surface_x && surface_y) { + printf(" (relative: %d, %d)", + json_object_get_int(surface_x), json_object_get_int(surface_y)); + } + printf("\n"); + + if (node_type) { + printf("Under cursor: %s", json_object_get_string(node_type)); + + if (workspace_name) { + printf(" \"%s\"", json_object_get_string(workspace_name)); + } + if (window_title) { + printf(" - %s", json_object_get_string(window_title)); + } + if (app_id) { + printf(" (%s)", json_object_get_string(app_id)); + } + if (pid) { + printf(" [PID: %d]", json_object_get_int(pid)); + } + printf("\n"); + } +} + static void pretty_print(int type, json_object *resp) { switch (type) { case IPC_SEND_TICK: @@ -390,6 +429,9 @@ static void pretty_print(int type, json_object *resp) { case IPC_GET_TREE: pretty_print_tree(resp, 0); return; + case IPC_GET_CURSOR: + pretty_print_cursor(resp); + return; case IPC_COMMAND: case IPC_GET_WORKSPACES: case IPC_GET_INPUTS: @@ -518,6 +560,8 @@ int main(int argc, char **argv) { type = IPC_GET_WORKSPACES; } else if (strcasecmp(cmdtype, "get_seats") == 0) { type = IPC_GET_SEATS; + } else if (strcasecmp(cmdtype, "get_cursor") == 0) { + type = IPC_GET_CURSOR; } else if (strcasecmp(cmdtype, "get_inputs") == 0) { type = IPC_GET_INPUTS; } else if (strcasecmp(cmdtype, "get_outputs") == 0) { diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index abee1bb9..7be5df74 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -82,6 +82,9 @@ _swaymsg_ [options...] [message] Gets a list of all seats, its properties and all assigned devices. +*get\_cursor* + Gets the current cursor position and information about the element under it. + *get\_marks* Get a JSON-encoded list of marks.