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

src/cce_ctl.rs (7.9K)

  1 // cce-ctl — IPC client for cce
  2  
  3 use std::env;
  4 use std::io::{Read, Write};
  5 use std::os::unix::net::UnixStream;
  6 use std::process;
  7  
  8 fn get_socket_path() -> String {
  9     match env::var("WAYLAND_DISPLAY") {
 10         Ok(display) => format!("/tmp/cce-{}.sock", display),
 11         Err(_) => "/tmp/cce.sock".to_string(),
 12     }
 13 }
 14  
 15 #[allow(dead_code)]
 16 fn get_windows_path() -> String {
 17     match env::var("WAYLAND_DISPLAY") {
 18         Ok(display) => format!("/tmp/cce-windows-{}", display),
 19         Err(_) => "/tmp/cce-windows".to_string(),
 20     }
 21 }
 22  
 23 fn usage(name: &str, to_stderr: bool) {
 24     let print = |s: &str| {
 25         if to_stderr {
 26             eprintln!("{}", s);
 27         } else {
 28             println!("{}", s);
 29         }
 30     };
 31     print(&format!("usage: {} <command> [args...]", name));
 32     print("");
 33     print("commands:");
 34     print("  layout <gap|gap_top|gap_left|gap_right|gap_bottom|offset|grid_gap|bar_height> <value>");
 35     print("  close");
 36     print("  minimize");
 37     print("  focus-next");
 38     print("  focus-prev");
 39     print("  focus-up | focus-down | focus-left | focus-right");
 40     print("  window-switcher            # open the alt-tab window switcher (cce-cloud overlay)");
 41     print("  fullscreen");
 42     print("  mode-next");
 43     print("  mode-next-shared");
 44     print("  set-mode <floating|tiled|fullscreen> [app_id|id]  # set a window's mode (focused if omitted)");
 45     print("  zoom-in");
 46     print("  zoom-out");
 47     print("  zoom-reset");
 48     print("  pan-left");
 49     print("  pan-right");
 50     print("  pan-up");
 51     print("  pan-down");
 52     print("  overlay-left");
 53     print("  overlay-right");
 54     print("  focus-window <app_id>");
 55     print("  close-window <app_id|id> [title-substring]  # close a specific window");
 56     print("  center-window [<app_id>]   # pan focused/named window on-screen; replies x= y= w= h=");
 57     print("  move-window <square> [<app_id>] # put focused/named window on a desktop square (e.g. C-9)");
 58     print("  move-window-left|-right|-up|-down    # step the focused window one grid cell;");
 59     print("                              # tiled swaps with the tiled window already there,");
 60     print("                              # floating just moves");
 61     print("  place-next <app_id> <x> <y> # one-shot: next map of app_id lands near this layout pos");
 62     print("  place-next-cell <app_id> <x> <y> # one-shot: next map covers the grid square at this pos,");
 63     print("                              # keeping its size and growing away from its neighbours");
 64     print("  overview                   # toggle; the exit lands on the focused window, not the pointer");
 65     print("  windows [--json]           # list windows; --json emits one JSON object per line");
 66     print("  outputs [--json]           # list outputs: mode, scale, logical size, physical mm, px/mm, and where the mm came from");
 67     print("  idle [status]              # idle timeouts: configured seconds, idle time, inhibited/displays_off/sleeping");
 68     print("  idle wake|sleep|display on|display off   # act now: wake darkened outputs, run the sleep command, darken/wake outputs");
 69     print("  idle timeouts <display_off_s> <sleep_s>  # set the timeouts live (0 = off); config.kdl `idle { }` on reload");
 70     print("  status-hide-mode [true|false]");
 71     print("  adjust-position-mode [true|false|query]");
 72     print("  exit [force]               # log out; waits for windows to close, cancels if one stays (a save prompt); force skips the wait");
 73     print("  restart");
 74     print("  restart-compositor        # exit cleanly; cce-display-manager relaunches the session");
 75     print("  reload");
 76     print("  repeat <rate> <delay>");
 77     print("  input <device_name|*> scroll-factor <value>");
 78     print("  config-done");
 79     print("  spawn <command>");
 80     print("  screenshot                          # capture the screen to ~/Pictures/screenshots");
 81     print("  screenshot region <x> <y> <w> <h>   # capture an on-screen region (logical px)");
 82     print("  screenshot window [app_id|id]       # capture a window (focused if omitted; works off-screen)");
 83     print("  debug-buffers [app_id|id]           # dump a window's scene buffers (pos/dest/natural/surface)");
 84     print("  notify <title> [body]");
 85     print("  bind <mods> <keysym> <action> [args...]");
 86     print("  pbind <mods> <button> <action>");
 87     print("  retile");
 88     print("  mode <floating|tiled|fullscreen|popup|overlay> <app_id> [title]");
 89     print("  pointer-location");
 90     print("  camera                             (pan, zoom, and the pan target being eased to)");
 91     print("  pointer-move-to <x> <y>            (layout pixels)");
 92     print("  pointer-move-by <dx> <dy>");
 93     print("  pointer-scroll <dy> [dx] [finger] [natural]");
 94     print("                                     (positive dy scrolls down; 15 = one notch; finger = a");
 95     print("                                      two-finger swipe, ended by finger-stop; natural = from");
 96     print("                                      a natural-scrolling touchpad, deltas already flipped)");
 97     print("  pointer-scroll finger-stop");
 98     print("  pointer-swipe <fingers> <dx> <dy> [steps] | begin <fingers> | update <dx> <dy> | end");
 99     print("                                     (a 3/4-finger touchpad swipe; drives input.kdl gesture");
100     print("                                      chords such as focus_left \"swipe3_left\"; the staged");
101     print("                                      form holds a swipe short of its threshold)");
102     print("  pointer-pinch <scale> [rotation] [steps] | begin | update <scale> [rotation] | end");
103     print("  touchpad-view-regions <x11:ID|id|app_id> clear | <x,y,w,h> ...");
104     print("                                     (limit a touchpad_view_apps drag to these window-local");
105     print("                                      rects; a swipe elsewhere scrolls the app normally)");
106     print("  pointer-click [button]             (left|right|middle|back|forward or evdev code)");
107     print("  pointer-press [button]             (held until pointer-release — drives drags)");
108     print("  pointer-release [button]");
109     print("  migrate-input                      (local: move config.kdl keybindings to input.kdl)");
110     print("  keypress <keycode>                 (evdev code; press+release to the focused client)");
111     print("  key-down <keycode>                 (modifier codes — ctrl 29/97, shift 42/54,");
112     print("  key-up <keycode>                    alt 56/100, super 125/126 — update client");
113     print("                                      xkb state, so e.g. 29+36 lands as ctrl+j)");
114     print("  shortcut bind <session> <id> <trigger>   (portal GlobalShortcuts backend: CTRL+SHIFT+space)");
115     print("  shortcut unbind <session> [<id>] | clear | list");
116 }
117  
118 pub fn run_cce_ctl(args: Vec<String>) {
119     if args.len() < 2 {
120         usage(&args[0], true);
121         process::exit(1);
122     }
123  
124     if args[1] == "--help" || args[1] == "-h" || args[1] == "help" {
125         usage(&args[0], false);
126         return;
127     }
128 
129     // Local file operation — no compositor needed.
130     if args[1] == "migrate-input" {
131         crate::migrate_input::run();
132         return;
133     }
134  
135  
136     // Connect to IPC socket
137     let stream = match UnixStream::connect(get_socket_path()) {
138         Ok(s) => s,
139         Err(e) => {
140             eprintln!("connect: {}", e);
141             process::exit(1);
142         }
143     };
144  
145     let mut stream = stream;
146     // Build command string from args
147     let cmd = args[1..].join(" ") + "\n";
148     if let Err(e) = stream.write_all(cmd.as_bytes()) {
149         eprintln!("write: {}", e);
150         process::exit(1);
151     }
152  
153     // Read response
154     let mut buf = [0u8; 4096];
155     loop {
156         match stream.read(&mut buf) {
157             Ok(0) => break,
158             Ok(n) => {
159                 let s = String::from_utf8_lossy(&buf[..n]);
160                 print!("{}", s);
161             }
162             Err(e) => {
163                 eprintln!("read: {}", e);
164                 break;
165             }
166         }
167     }
168 }