From fa4f144b9da9327f1bd2d0d36538705695f6b6fc Mon Sep 17 00:00:00 2001 From: Ernesto Cruz Date: Sat, 20 Jun 2026 00:23:54 +0100 Subject: [PATCH] feat(ipc): add 'get cursorpos' and 'get option ' queries --- docs/ipc.md | 3 + mmsg/mmsg.1 | 11 ++ mmsg/mmsg.c | 4 + src/config/parse_config.h | 239 ++++++++++++++++++++++++++++++++++++++ src/ipc/ipc.h | 19 +++ 5 files changed, 276 insertions(+) diff --git a/docs/ipc.md b/docs/ipc.md index 489293fa..11c0c5bb 100644 --- a/docs/ipc.md +++ b/docs/ipc.md @@ -24,6 +24,8 @@ description: Control mangowm programmatically using mmsg. | Command | Description | | :--- | :--- | | `get version` | Returns the current version of the compositor. | +| `get cursorpos` | Returns the global pointer position (`x`, `y`) and the monitor under it. | +| `get option ` | Returns the current value of a scalar config option. Numbers as numbers, colors as `0xRRGGBBAA` strings, bezier curves as 4-element arrays. | | `get keymode` | Returns the current active keyboard mode (e.g., normal, insert). | | `get keyboardlayout` | Returns the active XKB layout (abbreviated). | | `get monitor ` | Returns full JSON details for a specific monitor. | @@ -41,6 +43,7 @@ description: Control mangowm programmatically using mmsg. mmsg get monitor eDP-1 mmsg get all-clients mmsg get all-monitors +mmsg get option bordercolor ``` ### WATCH (Event Subscription) diff --git a/mmsg/mmsg.1 b/mmsg/mmsg.1 index 8a935f50..36933c1e 100644 --- a/mmsg/mmsg.1 +++ b/mmsg/mmsg.1 @@ -21,6 +21,17 @@ All \fBget\fR commands print a single JSON object and then close the connection. \fBget version\fR Return compositor version. .TP +\fBget cursorpos\fR +Return the global pointer position (\fBx\fR, \fBy\fR) and the monitor under it +(\fBnull\fR if over no output). +.TP +\fBget option \fIname\fR +Return the current value of a scalar config option. Numbers are returned as +numbers, colors as \fB0xRRGGBBAA\fR strings, bezier curves as 4-element arrays. +Values round-trip through \fBdispatch setoption,\fIname\fB,\fIvalue\fR, except +the comma-list options \fBcircle_layout\fR and \fBscroller_proportion_preset\fR +(readable as arrays, but written via the config file). +.TP \fBget keymode\fR Return current keymode. .TP diff --git a/mmsg/mmsg.c b/mmsg/mmsg.c index adb0b539..955a53e4 100644 --- a/mmsg/mmsg.c +++ b/mmsg/mmsg.c @@ -12,6 +12,10 @@ static void usage(void) { printf("One-shot queries (get):\n"); printf( " get version Show compositor version\n"); + printf(" get cursorpos Show pointer position + " + "monitor\n"); + printf(" get option Show current value of a " + "config option\n"); printf(" get keymode Show current keymode\n"); printf(" get keyboardlayout Show current keyboard " "layout\n"); diff --git a/src/config/parse_config.h b/src/config/parse_config.h index 61af421b..8ce327a8 100644 --- a/src/config/parse_config.h +++ b/src/config/parse_config.h @@ -1,3 +1,4 @@ +#include #include #include #include @@ -3016,6 +3017,244 @@ bool parse_option(Config *config, char *key, char *value) { return true; } +static cJSON *ipc_color_to_hex(const float c[4]) { + unsigned r = (unsigned)(c[0] * 255.0f + 0.5f); + unsigned g = (unsigned)(c[1] * 255.0f + 0.5f); + unsigned b = (unsigned)(c[2] * 255.0f + 0.5f); + unsigned a = (unsigned)(c[3] * 255.0f + 0.5f); + char buf[11]; + snprintf(buf, sizeof(buf), "0x%02X%02X%02X%02X", r, g, b, a); + return cJSON_CreateString(buf); +} + +static cJSON *ipc_curve_to_json(const double v[4]) { + cJSON *arr = cJSON_CreateArray(); + for (int i = 0; i < 4; i++) + cJSON_AddItemToArray(arr, cJSON_CreateNumber(v[i])); + return arr; +} + +cJSON *read_option(Config *config, const char *key) { +#define OPT_NUM(k, f) \ + if (strcmp(key, k) == 0) \ + return cJSON_CreateNumber(config->f) +#define OPT_STR(k, f) \ + if (strcmp(key, k) == 0) \ + return cJSON_CreateString(config->f) +#define OPT_STRP(k, f) \ + if (strcmp(key, k) == 0) \ + return config->f ? cJSON_CreateString(config->f) : cJSON_CreateNull() +#define OPT_COLOR(k, f) \ + if (strcmp(key, k) == 0) \ + return ipc_color_to_hex(config->f) +#define OPT_CURVE(k, f) \ + if (strcmp(key, k) == 0) \ + return ipc_curve_to_json(config->f) + + OPT_STR("keymode", keymode); + OPT_NUM("animations", animations); + OPT_NUM("layer_animations", layer_animations); + OPT_STR("animation_type_open", animation_type_open); + OPT_STR("animation_type_close", animation_type_close); + OPT_STR("layer_animation_type_open", layer_animation_type_open); + OPT_STR("layer_animation_type_close", layer_animation_type_close); + OPT_NUM("animation_fade_in", animation_fade_in); + OPT_NUM("animation_fade_out", animation_fade_out); + OPT_NUM("tag_animation_direction", tag_animation_direction); + OPT_NUM("zoom_initial_ratio", zoom_initial_ratio); + OPT_NUM("zoom_end_ratio", zoom_end_ratio); + OPT_NUM("fadein_begin_opacity", fadein_begin_opacity); + OPT_NUM("fadeout_begin_opacity", fadeout_begin_opacity); + OPT_NUM("animation_duration_move", animation_duration_move); + OPT_NUM("animation_duration_open", animation_duration_open); + OPT_NUM("animation_duration_tag", animation_duration_tag); + OPT_NUM("animation_duration_close", animation_duration_close); + OPT_NUM("animation_duration_focus", animation_duration_focus); + OPT_CURVE("animation_curve_move", animation_curve_move); + OPT_CURVE("animation_curve_open", animation_curve_open); + OPT_CURVE("animation_curve_tag", animation_curve_tag); + OPT_CURVE("animation_curve_close", animation_curve_close); + OPT_CURVE("animation_curve_focus", animation_curve_focus); + OPT_CURVE("animation_curve_opafadein", animation_curve_opafadein); + OPT_CURVE("animation_curve_opafadeout", animation_curve_opafadeout); + OPT_NUM("scroller_structs", scroller_structs); + OPT_NUM("scroller_default_proportion", scroller_default_proportion); + OPT_NUM("scroller_default_proportion_single", + scroller_default_proportion_single); + OPT_NUM("scroller_ignore_proportion_single", + scroller_ignore_proportion_single); + OPT_NUM("scroller_focus_center", scroller_focus_center); + OPT_NUM("scroller_prefer_center", scroller_prefer_center); + OPT_NUM("scroller_prefer_overspread", scroller_prefer_overspread); + OPT_NUM("edge_scroller_pointer_focus", edge_scroller_pointer_focus); + OPT_NUM("edge_scroller_focus_allow_speed", edge_scroller_focus_allow_speed); + OPT_NUM("focus_cross_monitor", focus_cross_monitor); + OPT_NUM("exchange_cross_monitor", exchange_cross_monitor); + OPT_NUM("scratchpad_cross_monitor", scratchpad_cross_monitor); + OPT_NUM("focus_cross_tag", focus_cross_tag); + OPT_NUM("view_current_to_back", view_current_to_back); + OPT_NUM("blur", blur); + OPT_NUM("blur_layer", blur_layer); + OPT_NUM("blur_optimized", blur_optimized); + OPT_NUM("border_radius", border_radius); + OPT_NUM("blur_params_num_passes", blur_params.num_passes); + OPT_NUM("blur_params_radius", blur_params.radius); + OPT_NUM("blur_params_noise", blur_params.noise); + OPT_NUM("blur_params_brightness", blur_params.brightness); + OPT_NUM("blur_params_contrast", blur_params.contrast); + OPT_NUM("blur_params_saturation", blur_params.saturation); + OPT_NUM("shadows", shadows); + OPT_NUM("shadow_only_floating", shadow_only_floating); + OPT_NUM("layer_shadows", layer_shadows); + OPT_NUM("shadows_size", shadows_size); + OPT_NUM("shadows_blur", shadows_blur); + OPT_NUM("shadows_position_x", shadows_position_x); + OPT_NUM("shadows_position_y", shadows_position_y); + OPT_NUM("single_scratchpad", single_scratchpad); + OPT_NUM("xwayland_persistence", xwayland_persistence); + OPT_NUM("syncobj_enable", syncobj_enable); + OPT_NUM("tag_carousel", tag_carousel); + OPT_NUM("drag_tile_refresh_interval", drag_tile_refresh_interval); + OPT_NUM("drag_floating_refresh_interval", drag_floating_refresh_interval); + OPT_NUM("allow_tearing", allow_tearing); + OPT_NUM("allow_shortcuts_inhibit", allow_shortcuts_inhibit); + OPT_NUM("allow_lock_transparent", allow_lock_transparent); + OPT_NUM("no_border_when_single", no_border_when_single); + OPT_NUM("no_radius_when_single", no_radius_when_single); + OPT_NUM("snap_distance", snap_distance); + OPT_NUM("enable_floating_snap", enable_floating_snap); + OPT_NUM("drag_tile_to_tile", drag_tile_to_tile); + OPT_NUM("drag_tile_small", drag_tile_small); + OPT_NUM("swipe_min_threshold", swipe_min_threshold); + OPT_NUM("focused_opacity", focused_opacity); + OPT_NUM("unfocused_opacity", unfocused_opacity); + OPT_STR("xkb_rules_rules", xkb_rules_rules); + OPT_STR("xkb_rules_model", xkb_rules_model); + OPT_STR("xkb_rules_layout", xkb_rules_layout); + OPT_STR("xkb_rules_variant", xkb_rules_variant); + OPT_STR("xkb_rules_options", xkb_rules_options); + OPT_NUM("new_is_master", new_is_master); + OPT_NUM("default_mfact", default_mfact); + OPT_NUM("default_nmaster", default_nmaster); + OPT_NUM("center_master_overspread", center_master_overspread); + OPT_NUM("center_when_single_stack", center_when_single_stack); + OPT_NUM("dwindle_vsplit", dwindle_vsplit); + OPT_NUM("dwindle_hsplit", dwindle_hsplit); + OPT_NUM("dwindle_preserve_split", dwindle_preserve_split); + OPT_NUM("dwindle_smart_split", dwindle_smart_split); + OPT_NUM("dwindle_smart_resize", dwindle_smart_resize); + OPT_NUM("dwindle_drop_simple_split", dwindle_drop_simple_split); + OPT_NUM("dwindle_manual_split", dwindle_manual_split); + OPT_NUM("dwindle_split_ratio", dwindle_split_ratio); + OPT_NUM("hotarea_size", hotarea_size); + OPT_NUM("hotarea_corner", hotarea_corner); + OPT_NUM("enable_hotarea", enable_hotarea); + OPT_NUM("ov_tab_mode", ov_tab_mode); + OPT_NUM("ov_no_resize", ov_no_resize); + OPT_NUM("overviewgappi", overviewgappi); + OPT_NUM("overviewgappo", overviewgappo); + OPT_NUM("cursor_hide_timeout", cursor_hide_timeout); + OPT_NUM("cursor_hide_on_keypress", cursor_hide_on_keypress); + OPT_NUM("axis_bind_apply_timeout", axis_bind_apply_timeout); + OPT_NUM("focus_on_activate", focus_on_activate); + OPT_NUM("numlockon", numlockon); + OPT_NUM("idleinhibit_ignore_visible", idleinhibit_ignore_visible); + OPT_NUM("sloppyfocus", sloppyfocus); + OPT_NUM("warpcursor", warpcursor); + OPT_NUM("drag_corner", drag_corner); + OPT_NUM("drag_warp_cursor", drag_warp_cursor); + OPT_NUM("smartgaps", smartgaps); + OPT_NUM("repeat_rate", repeat_rate); + OPT_NUM("repeat_delay", repeat_delay); + OPT_NUM("disable_trackpad", disable_trackpad); + OPT_NUM("tap_to_click", tap_to_click); + OPT_NUM("tap_and_drag", tap_and_drag); + OPT_NUM("drag_lock", drag_lock); + OPT_NUM("mouse_natural_scrolling", mouse_natural_scrolling); + OPT_NUM("trackpad_natural_scrolling", trackpad_natural_scrolling); + OPT_NUM("cursor_size", cursor_size); + OPT_STRP("cursor_theme", cursor_theme); + OPT_STRP("tab_bar_decorate_font_desc", tabdata.font_desc); + OPT_COLOR("tab_bar_decorate_fg_color", tabdata.fg_color); + OPT_COLOR("tab_bar_decorate_bg_color", tabdata.bg_color); + OPT_COLOR("tab_bar_decorate_focus_fg_color", tabdata.focus_fg_color); + OPT_COLOR("tab_bar_decorate_focus_bg_color", tabdata.focus_bg_color); + OPT_COLOR("tab_bar_decorate_border_color", tabdata.border_color); + OPT_NUM("tab_bar_decorate_border_width", tabdata.border_width); + OPT_NUM("tab_bar_decorate_corner_radius", tabdata.corner_radius); + OPT_NUM("tab_bar_decorate_padding_x", tabdata.padding_x); + OPT_NUM("tab_bar_decorate_padding_y", tabdata.padding_y); + OPT_STRP("jump_label_decorate_font_desc", jumplabeldata.font_desc); + OPT_COLOR("jump_label_decorate_fg_color", jumplabeldata.fg_color); + OPT_COLOR("jump_label_decorate_bg_color", jumplabeldata.bg_color); + OPT_COLOR("jump_label_decorate_focus_fg_color", + jumplabeldata.focus_fg_color); + OPT_COLOR("jump_label_decorate_focus_bg_color", + jumplabeldata.focus_bg_color); + OPT_COLOR("jump_label_decorate_border_color", jumplabeldata.border_color); + OPT_NUM("jump_label_decorate_border_width", jumplabeldata.border_width); + OPT_NUM("jump_label_decorate_corner_radius", jumplabeldata.corner_radius); + OPT_NUM("jump_label_decorate_padding_x", jumplabeldata.padding_x); + OPT_NUM("jump_label_decorate_padding_y", jumplabeldata.padding_y); + OPT_NUM("disable_while_typing", disable_while_typing); + OPT_NUM("left_handed", left_handed); + OPT_NUM("middle_button_emulation", middle_button_emulation); + OPT_NUM("mouse_accel_profile", mouse_accel_profile); + OPT_NUM("mouse_accel_speed", mouse_accel_speed); + OPT_NUM("trackpad_accel_profile", trackpad_accel_profile); + OPT_NUM("trackpad_accel_speed", trackpad_accel_speed); + OPT_NUM("scroll_method", scroll_method); + OPT_NUM("scroll_button", scroll_button); + OPT_NUM("click_method", click_method); + OPT_NUM("send_events_mode", send_events_mode); + OPT_NUM("button_map", button_map); + OPT_NUM("axis_scroll_factor", axis_scroll_factor); + OPT_STRP("tablet_map_to_mon", tablet_map_to_mon); + OPT_NUM("trackpad_scroll_factor", trackpad_scroll_factor); + OPT_NUM("gappih", gappih); + OPT_NUM("gappiv", gappiv); + OPT_NUM("gappoh", gappoh); + OPT_NUM("gappov", gappov); + OPT_NUM("scratchpad_width_ratio", scratchpad_width_ratio); + OPT_NUM("scratchpad_height_ratio", scratchpad_height_ratio); + OPT_NUM("borderpx", borderpx); + OPT_NUM("tab_bar_height", tab_bar_height); + OPT_COLOR("rootcolor", rootcolor); + OPT_COLOR("shadowscolor", shadowscolor); + OPT_COLOR("bordercolor", bordercolor); + OPT_COLOR("dropcolor", dropcolor); + OPT_COLOR("splitcolor", splitcolor); + OPT_COLOR("focuscolor", focuscolor); + OPT_COLOR("maximizescreencolor", maximizescreencolor); + OPT_COLOR("urgentcolor", urgentcolor); + OPT_COLOR("scratchpadcolor", scratchpadcolor); + OPT_COLOR("globalcolor", globalcolor); + OPT_COLOR("overlaycolor", overlaycolor); + +#undef OPT_NUM +#undef OPT_STR +#undef OPT_STRP +#undef OPT_COLOR +#undef OPT_CURVE + + if (strcmp(key, "scroller_proportion_preset") == 0) { + cJSON *arr = cJSON_CreateArray(); + for (int32_t i = 0; i < config->scroller_proportion_preset_count; i++) + cJSON_AddItemToArray( + arr, cJSON_CreateNumber(config->scroller_proportion_preset[i])); + return arr; + } + if (strcmp(key, "circle_layout") == 0) { + cJSON *arr = cJSON_CreateArray(); + for (int32_t i = 0; i < config->circle_layout_count; i++) + cJSON_AddItemToArray(arr, + cJSON_CreateString(config->circle_layout[i])); + return arr; + } + + return NULL; +} + bool parse_config_line(Config *config, const char *line) { char key[256], value[256]; if (sscanf(line, "%255[^=]=%255[^\n]", key, value) != 2) { diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h index 9ac1a7b2..e947f44e 100644 --- a/src/ipc/ipc.h +++ b/src/ipc/ipc.h @@ -237,6 +237,15 @@ static void handle_command(int client_fd, const char *cmd_raw) { if (strcmp(cmd, "get version") == 0) { resp = cJSON_CreateObject(); cJSON_AddStringToObject(resp, "version", VERSION); + } else if (strcmp(cmd, "get cursorpos") == 0) { + resp = cJSON_CreateObject(); + cJSON_AddNumberToObject(resp, "x", cursor->x); + cJSON_AddNumberToObject(resp, "y", cursor->y); + Monitor *m = xytomon(cursor->x, cursor->y); + if (m) + cJSON_AddStringToObject(resp, "monitor", m->wlr_output->name); + else + cJSON_AddNullToObject(resp, "monitor"); } else if (strcmp(cmd, "get keymode") == 0) { resp = cJSON_CreateObject(); cJSON_AddStringToObject(resp, "keymode", keymode.mode); @@ -342,6 +351,16 @@ static void handle_command(int client_fd, const char *cmd_raw) { return; } resp = build_monitor_tags_response(m); + } else if (strncmp(cmd, "get option ", 11) == 0) { + cJSON *val = read_option(&config, cmd + 11); + if (!val) { + send_static_json(client_fd, + "{\"error\":\"unknown or unreadable option\"}\n"); + return; + } + resp = cJSON_CreateObject(); + cJSON_AddStringToObject(resp, "option", cmd + 11); + cJSON_AddItemToObject(resp, "value", val); } else if (strncmp(cmd, "dispatch ", 9) == 0) { char *dispatch_copy = strdup(cmd_raw + 9); char *out = dispatch_copy, *ptr = dispatch_copy;