Skip to main content

rmux_client/commands/
window.rs

1use rmux_proto::{
2    KillWindowRequest, LastWindowRequest, LayoutName, LinkWindowRequest, ListWindowsRequest,
3    MoveWindowRequest, MoveWindowTarget, NewWindowRequest, NextWindowRequest,
4    PreviousWindowRequest, RenameWindowRequest, Request, Response, RotateWindowDirection,
5    RotateWindowRequest, SelectCustomLayoutRequest, SelectLayoutRequest, SelectLayoutTarget,
6    SelectOldLayoutRequest, SelectWindowRequest, SessionName, SplitDirection,
7    SplitWindowExtRequest, SplitWindowRequest, SplitWindowTarget, SpreadLayoutRequest,
8    SwapWindowRequest, UnlinkWindowRequest, WindowTarget,
9};
10
11use crate::{connection::Connection, ClientError};
12
13/// Full options for a `split-window` request.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct SplitWindowOptions {
16    /// The exact split target.
17    pub target: SplitWindowTarget,
18    /// Axis on which to split (`Vertical` = side-by-side, `Horizontal` = stacked).
19    pub direction: SplitDirection,
20    /// `true` to insert the new pane *before* the target on the chosen axis
21    /// (tmux `-b`); `false` to insert after (default).
22    pub before: bool,
23    /// Optional per-spawn environment overrides in `NAME=VALUE` form.
24    pub environment: Option<Vec<String>>,
25    /// Optional command argv for the new pane.
26    pub command: Option<Vec<String>>,
27}
28
29impl Connection {
30    /// Sends a `new-window` request over the detached RPC channel.
31    pub fn new_window(
32        &mut self,
33        target: rmux_proto::SessionName,
34        name: Option<String>,
35        detached: bool,
36    ) -> Result<Response, ClientError> {
37        self.new_window_with_environment(target, name, detached, None, None, None)
38    }
39
40    /// Sends a `new-window` request with explicit spawn environment overrides.
41    pub fn new_window_with_environment(
42        &mut self,
43        target: rmux_proto::SessionName,
44        name: Option<String>,
45        detached: bool,
46        environment: Option<Vec<String>>,
47        start_directory: Option<std::path::PathBuf>,
48        command: Option<Vec<String>>,
49    ) -> Result<Response, ClientError> {
50        self.new_window_at_with_environment(
51            target,
52            None,
53            name,
54            detached,
55            environment,
56            start_directory,
57            command,
58            false,
59        )
60    }
61
62    /// Sends a `new-window` request with an optional destination index.
63    #[allow(clippy::too_many_arguments)]
64    pub fn new_window_at_with_environment(
65        &mut self,
66        target: rmux_proto::SessionName,
67        target_window_index: Option<u32>,
68        name: Option<String>,
69        detached: bool,
70        environment: Option<Vec<String>>,
71        start_directory: Option<std::path::PathBuf>,
72        command: Option<Vec<String>>,
73        insert_at_target: bool,
74    ) -> Result<Response, ClientError> {
75        self.roundtrip(&Request::NewWindow(NewWindowRequest {
76            target,
77            name,
78            detached,
79            start_directory,
80            environment,
81            command,
82            process_command: None,
83            target_window_index,
84            insert_at_target,
85        }))
86    }
87
88    /// Sends a `kill-window` request over the detached RPC channel.
89    pub fn kill_window(
90        &mut self,
91        target: WindowTarget,
92        kill_others: bool,
93    ) -> Result<Response, ClientError> {
94        self.roundtrip(&Request::KillWindow(KillWindowRequest {
95            target,
96            kill_all_others: kill_others,
97        }))
98    }
99
100    /// Sends a `select-window` request over the detached RPC channel.
101    pub fn select_window(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
102        self.roundtrip(&Request::SelectWindow(SelectWindowRequest { target }))
103    }
104
105    /// Sends a `rename-window` request over the detached RPC channel.
106    pub fn rename_window(
107        &mut self,
108        target: WindowTarget,
109        new_name: String,
110    ) -> Result<Response, ClientError> {
111        self.roundtrip(&Request::RenameWindow(RenameWindowRequest {
112            target,
113            name: new_name,
114        }))
115    }
116
117    /// Sends a `next-window` request over the detached RPC channel.
118    pub fn next_window(
119        &mut self,
120        target: SessionName,
121        alerts_only: bool,
122    ) -> Result<Response, ClientError> {
123        self.roundtrip(&Request::NextWindow(NextWindowRequest {
124            target,
125            alerts_only,
126        }))
127    }
128
129    /// Sends a `previous-window` request over the detached RPC channel.
130    pub fn previous_window(
131        &mut self,
132        target: SessionName,
133        alerts_only: bool,
134    ) -> Result<Response, ClientError> {
135        self.roundtrip(&Request::PreviousWindow(PreviousWindowRequest {
136            target,
137            alerts_only,
138        }))
139    }
140
141    /// Sends a `last-window` request over the detached RPC channel.
142    pub fn last_window(&mut self, target: SessionName) -> Result<Response, ClientError> {
143        self.roundtrip(&Request::LastWindow(LastWindowRequest { target }))
144    }
145
146    /// Sends a `list-windows` request over the detached RPC channel.
147    pub fn list_windows(
148        &mut self,
149        target: SessionName,
150        format: Option<String>,
151    ) -> Result<Response, ClientError> {
152        self.roundtrip(&Request::ListWindows(ListWindowsRequest { target, format }))
153    }
154
155    /// Sends a `link-window` request over the detached RPC channel.
156    pub fn link_window(
157        &mut self,
158        source: WindowTarget,
159        target: WindowTarget,
160        after: bool,
161        before: bool,
162        kill_destination: bool,
163        detached: bool,
164    ) -> Result<Response, ClientError> {
165        self.roundtrip(&Request::LinkWindow(LinkWindowRequest {
166            source,
167            target,
168            after,
169            before,
170            kill_destination,
171            detached,
172        }))
173    }
174
175    /// Sends a `move-window` request over the detached RPC channel.
176    pub fn move_window(
177        &mut self,
178        source: Option<WindowTarget>,
179        target: MoveWindowTarget,
180        renumber: bool,
181        kill_destination: bool,
182        detached: bool,
183    ) -> Result<Response, ClientError> {
184        self.move_window_with_position(
185            source,
186            target,
187            renumber,
188            kill_destination,
189            detached,
190            false,
191            false,
192        )
193    }
194
195    /// Sends a `move-window` request with optional `-a`/`-b` placement over the detached RPC channel.
196    #[allow(clippy::too_many_arguments)]
197    pub fn move_window_with_position(
198        &mut self,
199        source: Option<WindowTarget>,
200        target: MoveWindowTarget,
201        renumber: bool,
202        kill_destination: bool,
203        detached: bool,
204        after: bool,
205        before: bool,
206    ) -> Result<Response, ClientError> {
207        self.roundtrip(&Request::MoveWindow(MoveWindowRequest {
208            source,
209            target,
210            renumber,
211            kill_destination,
212            detached,
213            after,
214            before,
215        }))
216    }
217
218    /// Sends a `swap-window` request over the detached RPC channel.
219    pub fn swap_window(
220        &mut self,
221        source: WindowTarget,
222        target: WindowTarget,
223        detached: bool,
224    ) -> Result<Response, ClientError> {
225        self.roundtrip(&Request::SwapWindow(SwapWindowRequest {
226            source,
227            target,
228            detached,
229        }))
230    }
231
232    /// Sends a `rotate-window` request over the detached RPC channel.
233    pub fn rotate_window(
234        &mut self,
235        target: WindowTarget,
236        direction: RotateWindowDirection,
237    ) -> Result<Response, ClientError> {
238        self.rotate_window_with_zoom(target, direction, false)
239    }
240
241    /// Sends a `rotate-window` request with zoom save/restore over the detached RPC channel.
242    pub fn rotate_window_with_zoom(
243        &mut self,
244        target: WindowTarget,
245        direction: RotateWindowDirection,
246        restore_zoom: bool,
247    ) -> Result<Response, ClientError> {
248        self.roundtrip(&Request::RotateWindow(RotateWindowRequest {
249            target,
250            direction,
251            restore_zoom,
252        }))
253    }
254
255    /// Sends a `resize-window` request over the detached RPC channel.
256    pub fn resize_window(
257        &mut self,
258        target: WindowTarget,
259        width: Option<u16>,
260        height: Option<u16>,
261        adjustment: Option<rmux_proto::ResizeWindowAdjustment>,
262    ) -> Result<Response, ClientError> {
263        self.roundtrip(&Request::ResizeWindow(rmux_proto::ResizeWindowRequest {
264            target,
265            width,
266            height,
267            adjustment,
268        }))
269    }
270
271    /// Sends an `unlink-window` request over the detached RPC channel.
272    pub fn unlink_window(
273        &mut self,
274        target: WindowTarget,
275        kill_if_last: bool,
276    ) -> Result<Response, ClientError> {
277        self.roundtrip(&Request::UnlinkWindow(UnlinkWindowRequest {
278            target,
279            kill_if_last,
280        }))
281    }
282
283    /// Sends a `respawn-window` request over the detached RPC channel.
284    pub fn respawn_window(
285        &mut self,
286        target: WindowTarget,
287        kill: bool,
288    ) -> Result<Response, ClientError> {
289        self.respawn_window_with_environment(target, kill, None, None, None)
290    }
291
292    /// Sends a `respawn-window` request with explicit spawn environment overrides.
293    pub fn respawn_window_with_environment(
294        &mut self,
295        target: WindowTarget,
296        kill: bool,
297        environment: Option<Vec<String>>,
298        start_directory: Option<std::path::PathBuf>,
299        command: Option<Vec<String>>,
300    ) -> Result<Response, ClientError> {
301        self.roundtrip(&Request::RespawnWindow(rmux_proto::RespawnWindowRequest {
302            target,
303            kill,
304            start_directory,
305            environment,
306            command,
307        }))
308    }
309
310    /// Sends a `split-window` request over the detached RPC channel.
311    pub fn split_window(&mut self, target: SplitWindowTarget) -> Result<Response, ClientError> {
312        self.split_window_with_direction(target, SplitDirection::Vertical)
313    }
314
315    /// Sends a `split-window` request with an explicit direction over the detached RPC channel.
316    pub fn split_window_with_direction(
317        &mut self,
318        target: SplitWindowTarget,
319        direction: SplitDirection,
320    ) -> Result<Response, ClientError> {
321        self.split_window_with_direction_and_environment(target, direction, None)
322    }
323
324    /// Sends a `split-window` request with explicit spawn environment overrides.
325    pub fn split_window_with_direction_and_environment(
326        &mut self,
327        target: SplitWindowTarget,
328        direction: SplitDirection,
329        environment: Option<Vec<String>>,
330    ) -> Result<Response, ClientError> {
331        self.split_window_with_spawn(target, direction, environment, None)
332    }
333
334    /// Sends a `split-window` request with explicit spawn options.
335    ///
336    /// New pane is inserted *after* the target. To insert before (tmux `-b`),
337    /// use [`Connection::split_window_with_options`].
338    pub fn split_window_with_spawn(
339        &mut self,
340        target: SplitWindowTarget,
341        direction: SplitDirection,
342        environment: Option<Vec<String>>,
343        command: Option<Vec<String>>,
344    ) -> Result<Response, ClientError> {
345        self.split_window_with_start_directory(target, direction, environment, None, command)
346    }
347
348    /// Sends a `split-window` request with an optional working-directory override.
349    ///
350    /// New pane is inserted *after* the target. To insert before (tmux `-b`),
351    /// use [`Connection::split_window_with_options`] without a start directory.
352    pub fn split_window_with_start_directory(
353        &mut self,
354        target: SplitWindowTarget,
355        direction: SplitDirection,
356        environment: Option<Vec<String>>,
357        start_directory: Option<std::path::PathBuf>,
358        command: Option<Vec<String>>,
359    ) -> Result<Response, ClientError> {
360        if command.is_some() || start_directory.is_some() {
361            return self.roundtrip(&Request::SplitWindowExt(SplitWindowExtRequest {
362                target,
363                direction,
364                before: false,
365                environment,
366                command,
367                process_command: None,
368                start_directory,
369                keep_alive_on_exit: None,
370                detached: false,
371                size: None,
372                preserve_zoom: false,
373                full_size: false,
374                stdin_payload: None,
375            }));
376        }
377        self.split_window_with_options(SplitWindowOptions {
378            target,
379            direction,
380            before: false,
381            environment,
382            command,
383        })
384    }
385
386    /// Sends a `split-window` request with full options including `before`.
387    pub fn split_window_with_options(
388        &mut self,
389        options: SplitWindowOptions,
390    ) -> Result<Response, ClientError> {
391        let SplitWindowOptions {
392            target,
393            direction,
394            before,
395            environment,
396            command,
397        } = options;
398        if command.is_some() {
399            return self.roundtrip(&Request::SplitWindowExt(SplitWindowExtRequest {
400                target,
401                direction,
402                before,
403                environment,
404                command,
405                process_command: None,
406                start_directory: None,
407                keep_alive_on_exit: None,
408                detached: false,
409                size: None,
410                preserve_zoom: false,
411                full_size: false,
412                stdin_payload: None,
413            }));
414        }
415        self.roundtrip(&Request::SplitWindow(SplitWindowRequest {
416            target,
417            direction,
418            before,
419            environment,
420        }))
421    }
422
423    /// Sends a `select-layout` request over the detached RPC channel.
424    pub fn select_layout(
425        &mut self,
426        target: SelectLayoutTarget,
427        layout: LayoutName,
428    ) -> Result<Response, ClientError> {
429        self.roundtrip(&Request::SelectLayout(SelectLayoutRequest {
430            target,
431            layout,
432        }))
433    }
434
435    /// Sends a `select-layout` custom layout request over the detached RPC channel.
436    pub fn select_custom_layout(
437        &mut self,
438        target: SelectLayoutTarget,
439        layout: String,
440    ) -> Result<Response, ClientError> {
441        self.roundtrip(&Request::SelectCustomLayout(SelectCustomLayoutRequest {
442            target,
443            layout,
444        }))
445    }
446
447    /// Sends a `select-layout -o` request over the detached RPC channel.
448    pub fn select_old_layout(
449        &mut self,
450        target: SelectLayoutTarget,
451    ) -> Result<Response, ClientError> {
452        self.roundtrip(&Request::SelectOldLayout(SelectOldLayoutRequest { target }))
453    }
454
455    /// Sends a `select-layout -E` request over the detached RPC channel.
456    pub fn spread_layout(&mut self, target: SelectLayoutTarget) -> Result<Response, ClientError> {
457        self.roundtrip(&Request::SpreadLayout(SpreadLayoutRequest { target }))
458    }
459
460    /// Sends a `next-layout` request over the detached RPC channel.
461    pub fn next_layout(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
462        self.roundtrip(&Request::NextLayout(rmux_proto::NextLayoutRequest {
463            target,
464        }))
465    }
466
467    /// Sends a `previous-layout` request over the detached RPC channel.
468    pub fn previous_layout(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
469        self.roundtrip(&Request::PreviousLayout(
470            rmux_proto::PreviousLayoutRequest { target },
471        ))
472    }
473}