git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitbdc2bb6aa58937a0b5d8d23340e1697f5e3ca255
parent114ef89b02
authorLucas Galante <[email protected]>
date2026-07-13 21:17
feat(ipc): windows --json output and adjust-position-mode query

`windows --json` emits one JSON object per line (serde_json-escaped, so
titles containing quotes or key=value-looking text survive intact); the
text format stays the default. `adjust-position-mode query` returns
"ok true|false" without toggling, giving clients a way to read the mode
instead of the /tmp/cce-status-interface-adjust-mode sentinel file (which
is still written for older bars). For cce-status-interface's window picker
and adjust-mode sync (its cleanup proposal phase 3).

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/cce_ctl.rs               |  4 ++-
 src/server/window_manager.rs | 60 +++++++++++++++++++++++++++++++-------------
 2 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/src/cce_ctl.rs b/src/cce_ctl.rs
index e1835fd..052d54e 100644
--- a/src/cce_ctl.rs
+++ b/src/cce_ctl.rs
@@ -53,7 +53,9 @@ fn usage(name: &str, to_stderr: bool) {
     print("  focus-window <app_id>");
     print("  center-window [<app_id>]   # pan focused/named window on-screen; replies x= y= w= h=");
     print("  expose");
-    print("  windows");
+    print("  windows [--json]           # list windows; --json emits one JSON object per line");
+    print("  status-hide-mode [true|false]");
+    print("  adjust-position-mode [true|false|query]");
     print("  exit");
     print("  restart");
     print("  reload");
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 821b24f..5357227 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -2869,6 +2869,9 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                 return format!("ok {}\n", enable);
             }
             "adjust-position-mode" => {
+                if parts.get(1).copied() == Some("query") {
+                    return format!("ok {}\n", self.adjust_position_mode);
+                }
                 let enable = if parts.len() >= 2 {
                     match parts[1] {
                         "true" | "on" | "enable" | "1" => true,
@@ -3211,6 +3214,9 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                 "ok\n".to_string()
             }
             "windows" => {
+                // `windows --json` emits one JSON object per line; titles and
+                // app_ids are then properly escaped, unlike the text format.
+                let as_json = parts.get(1).copied() == Some("--json");
                 let mut focused_window: *mut Window = std::ptr::null_mut();
                 let seats_list = &mut (*self.server).input_manager.seats as *mut ffi::wl_list as *mut WlList;
                 let mut curr_seat = (*seats_list).next;
@@ -3229,23 +3235,43 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                     if !w.is_null() && !(*w).closed && !matches!((*w).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) {
                         let app_id = (*w).get_app_id_string().unwrap_or_default();
                         let title = (*w).get_title_string().unwrap_or_default();
-                        out.push_str(&format!(
-                            "window id={} app_id={} title=\"{}\" mode={} x={} y={} w={} h={} vx={:.1} vy={:.1} minimized={} has_parent={} focused={} ssd={}\n",
-                            (*w).ref_key.index,
-                            app_id,
-                            title,
-                            (*w).tiling_mode.as_str(),
-                            (*w).box_geom.x,
-                            (*w).box_geom.y,
-                            (*w).box_geom.width,
-                            (*w).box_geom.height,
-                            (*w).virtual_x,
-                            (*w).virtual_y,
-                            (*w).minimized,
-                            (*w).has_parent,
-                            w == focused_window,
-                            (*w).wm_requested.ssd,
-                        ));
+                        if as_json {
+                            out.push_str(&serde_json::json!({
+                                "id": (*w).ref_key.index,
+                                "app_id": app_id,
+                                "title": title,
+                                "mode": (*w).tiling_mode.as_str(),
+                                "x": (*w).box_geom.x,
+                                "y": (*w).box_geom.y,
+                                "w": (*w).box_geom.width,
+                                "h": (*w).box_geom.height,
+                                "vx": (*w).virtual_x,
+                                "vy": (*w).virtual_y,
+                                "minimized": (*w).minimized,
+                                "has_parent": (*w).has_parent,
+                                "focused": w == focused_window,
+                                "ssd": (*w).wm_requested.ssd,
+                            }).to_string());
+                            out.push('\n');
+                        } else {
+                            out.push_str(&format!(
+                                "window id={} app_id={} title=\"{}\" mode={} x={} y={} w={} h={} vx={:.1} vy={:.1} minimized={} has_parent={} focused={} ssd={}\n",
+                                (*w).ref_key.index,
+                                app_id,
+                                title,
+                                (*w).tiling_mode.as_str(),
+                                (*w).box_geom.x,
+                                (*w).box_geom.y,
+                                (*w).box_geom.width,
+                                (*w).box_geom.height,
+                                (*w).virtual_x,
+                                (*w).virtual_y,
+                                (*w).minimized,
+                                (*w).has_parent,
+                                w == focused_window,
+                                (*w).wm_requested.ssd,
+                            ));
+                        }
                     }
                 }
                 out