diff --git a/include/ipc.h b/include/ipc.h index ff0117502..42bb8ccb2 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 b934bb568..b69b3c6ae 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 833db0ef8..b1ef7578f 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