feat(ipc): add 'get cursorpos' and 'get option <name>' queries

This commit is contained in:
Ernesto Cruz 2026-06-20 00:23:54 +01:00
parent 8e356692f6
commit fa4f144b9d
5 changed files with 276 additions and 0 deletions

View file

@ -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 <name>` | 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 <name>` | 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)

View file

@ -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

View file

@ -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 <name> Show current value of a "
"config option\n");
printf(" get keymode Show current keymode\n");
printf(" get keyboardlayout Show current keyboard "
"layout\n");

View file

@ -1,3 +1,4 @@
#include <cjson/cJSON.h>
#include <ctype.h>
#include <libgen.h>
#include <stdbool.h>
@ -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) {

View file

@ -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;