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 }