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, SplitWindowTargetActionRequest,
8    SpreadLayoutRequest, 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` = stacked, `Horizontal` = side-by-side).
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(Box::new(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.list_windows_with_options(target, format, None, None, false)
153    }
154
155    /// Sends a `list-windows` request with filtering and ordering options.
156    pub fn list_windows_with_options(
157        &mut self,
158        target: SessionName,
159        format: Option<String>,
160        filter: Option<String>,
161        sort_order: Option<String>,
162        reversed: bool,
163    ) -> Result<Response, ClientError> {
164        self.roundtrip(&Request::ListWindows(Box::new(ListWindowsRequest {
165            target,
166            format,
167            filter,
168            sort_order,
169            reversed,
170        })))
171    }
172
173    /// Sends a `link-window` request over the detached RPC channel.
174    pub fn link_window(
175        &mut self,
176        source: WindowTarget,
177        target: WindowTarget,
178        after: bool,
179        before: bool,
180        kill_destination: bool,
181        detached: bool,
182    ) -> Result<Response, ClientError> {
183        self.roundtrip(&Request::LinkWindow(LinkWindowRequest {
184            source,
185            target,
186            after,
187            before,
188            kill_destination,
189            detached,
190        }))
191    }
192
193    /// Sends a `move-window` request over the detached RPC channel.
194    pub fn move_window(
195        &mut self,
196        source: Option<WindowTarget>,
197        target: MoveWindowTarget,
198        renumber: bool,
199        kill_destination: bool,
200        detached: bool,
201    ) -> Result<Response, ClientError> {
202        self.move_window_with_position(
203            source,
204            target,
205            renumber,
206            kill_destination,
207            detached,
208            false,
209            false,
210        )
211    }
212
213    /// Sends a `move-window` request with optional `-a`/`-b` placement over the detached RPC channel.
214    #[allow(clippy::too_many_arguments)]
215    pub fn move_window_with_position(
216        &mut self,
217        source: Option<WindowTarget>,
218        target: MoveWindowTarget,
219        renumber: bool,
220        kill_destination: bool,
221        detached: bool,
222        after: bool,
223        before: bool,
224    ) -> Result<Response, ClientError> {
225        self.roundtrip(&Request::MoveWindow(MoveWindowRequest {
226            source,
227            target,
228            renumber,
229            kill_destination,
230            detached,
231            after,
232            before,
233        }))
234    }
235
236    /// Sends a `swap-window` request over the detached RPC channel.
237    pub fn swap_window(
238        &mut self,
239        source: WindowTarget,
240        target: WindowTarget,
241        detached: bool,
242    ) -> Result<Response, ClientError> {
243        self.roundtrip(&Request::SwapWindow(SwapWindowRequest {
244            source,
245            target,
246            detached,
247        }))
248    }
249
250    /// Sends a `rotate-window` request over the detached RPC channel.
251    pub fn rotate_window(
252        &mut self,
253        target: WindowTarget,
254        direction: RotateWindowDirection,
255    ) -> Result<Response, ClientError> {
256        self.rotate_window_with_zoom(target, direction, false)
257    }
258
259    /// Sends a `rotate-window` request with zoom save/restore over the detached RPC channel.
260    pub fn rotate_window_with_zoom(
261        &mut self,
262        target: WindowTarget,
263        direction: RotateWindowDirection,
264        restore_zoom: bool,
265    ) -> Result<Response, ClientError> {
266        self.roundtrip(&Request::RotateWindow(RotateWindowRequest {
267            target,
268            direction,
269            restore_zoom,
270        }))
271    }
272
273    /// Sends a `resize-window` request over the detached RPC channel.
274    pub fn resize_window(
275        &mut self,
276        target: WindowTarget,
277        width: Option<u16>,
278        height: Option<u16>,
279        adjustment: Option<rmux_proto::ResizeWindowAdjustment>,
280    ) -> Result<Response, ClientError> {
281        self.roundtrip(&Request::ResizeWindow(rmux_proto::ResizeWindowRequest {
282            target,
283            width,
284            height,
285            adjustment,
286        }))
287    }
288
289    /// Sends an `unlink-window` request over the detached RPC channel.
290    pub fn unlink_window(
291        &mut self,
292        target: WindowTarget,
293        kill_if_last: bool,
294    ) -> Result<Response, ClientError> {
295        self.roundtrip(&Request::UnlinkWindow(UnlinkWindowRequest {
296            target,
297            kill_if_last,
298        }))
299    }
300
301    /// Sends a `respawn-window` request over the detached RPC channel.
302    pub fn respawn_window(
303        &mut self,
304        target: WindowTarget,
305        kill: bool,
306    ) -> Result<Response, ClientError> {
307        self.respawn_window_with_environment(target, kill, None, None, None)
308    }
309
310    /// Sends a `respawn-window` request with explicit spawn environment overrides.
311    pub fn respawn_window_with_environment(
312        &mut self,
313        target: WindowTarget,
314        kill: bool,
315        environment: Option<Vec<String>>,
316        start_directory: Option<std::path::PathBuf>,
317        command: Option<Vec<String>>,
318    ) -> Result<Response, ClientError> {
319        self.roundtrip(&Request::RespawnWindow(Box::new(
320            rmux_proto::RespawnWindowRequest {
321                target,
322                kill,
323                start_directory,
324                environment,
325                command,
326            },
327        )))
328    }
329
330    /// Sends a `split-window` request over the detached RPC channel.
331    pub fn split_window(&mut self, target: SplitWindowTarget) -> Result<Response, ClientError> {
332        self.split_window_with_direction(target, SplitDirection::Vertical)
333    }
334
335    /// Sends a `split-window` request with an explicit direction over the detached RPC channel.
336    pub fn split_window_with_direction(
337        &mut self,
338        target: SplitWindowTarget,
339        direction: SplitDirection,
340    ) -> Result<Response, ClientError> {
341        self.split_window_with_direction_and_environment(target, direction, None)
342    }
343
344    /// Sends a `split-window` request with explicit spawn environment overrides.
345    pub fn split_window_with_direction_and_environment(
346        &mut self,
347        target: SplitWindowTarget,
348        direction: SplitDirection,
349        environment: Option<Vec<String>>,
350    ) -> Result<Response, ClientError> {
351        self.split_window_with_spawn(target, direction, environment, None)
352    }
353
354    /// Sends a `split-window` request with explicit spawn options.
355    ///
356    /// New pane is inserted *after* the target. To insert before (tmux `-b`),
357    /// use [`Connection::split_window_with_options`].
358    pub fn split_window_with_spawn(
359        &mut self,
360        target: SplitWindowTarget,
361        direction: SplitDirection,
362        environment: Option<Vec<String>>,
363        command: Option<Vec<String>>,
364    ) -> Result<Response, ClientError> {
365        self.split_window_with_start_directory(target, direction, environment, None, command)
366    }
367
368    /// Sends a `split-window` request with an optional working-directory override.
369    ///
370    /// New pane is inserted *after* the target. To insert before (tmux `-b`),
371    /// use [`Connection::split_window_with_options`] without a start directory.
372    pub fn split_window_with_start_directory(
373        &mut self,
374        target: SplitWindowTarget,
375        direction: SplitDirection,
376        environment: Option<Vec<String>>,
377        start_directory: Option<std::path::PathBuf>,
378        command: Option<Vec<String>>,
379    ) -> Result<Response, ClientError> {
380        if command.is_some() || start_directory.is_some() {
381            return self.roundtrip(&Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
382                target,
383                direction,
384                before: false,
385                environment,
386                command,
387                process_command: None,
388                start_directory,
389                keep_alive_on_exit: None,
390                detached: false,
391                size: None,
392                preserve_zoom: false,
393                full_size: false,
394                stdin_payload: None,
395            })));
396        }
397        self.split_window_with_options(SplitWindowOptions {
398            target,
399            direction,
400            before: false,
401            environment,
402            command,
403        })
404    }
405
406    /// Sends a `split-window` request with full options including `before`.
407    pub fn split_window_with_options(
408        &mut self,
409        options: SplitWindowOptions,
410    ) -> Result<Response, ClientError> {
411        let SplitWindowOptions {
412            target,
413            direction,
414            before,
415            environment,
416            command,
417        } = options;
418        if command.is_some() {
419            return self.roundtrip(&Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
420                target,
421                direction,
422                before,
423                environment,
424                command,
425                process_command: None,
426                start_directory: None,
427                keep_alive_on_exit: None,
428                detached: false,
429                size: None,
430                preserve_zoom: false,
431                full_size: false,
432                stdin_payload: None,
433            })));
434        }
435        self.roundtrip(&Request::SplitWindow(SplitWindowRequest {
436            target,
437            direction,
438            before,
439            environment,
440        }))
441    }
442
443    /// Sends a `split-window` request with server-side target resolution.
444    pub fn split_window_target_action(
445        &mut self,
446        request: SplitWindowTargetActionRequest,
447    ) -> Result<Response, ClientError> {
448        self.roundtrip(&Request::SplitWindowTargetAction(Box::new(request)))
449    }
450
451    /// Sends a `select-layout` request over the detached RPC channel.
452    pub fn select_layout(
453        &mut self,
454        target: SelectLayoutTarget,
455        layout: LayoutName,
456    ) -> Result<Response, ClientError> {
457        self.roundtrip(&Request::SelectLayout(SelectLayoutRequest {
458            target,
459            layout,
460        }))
461    }
462
463    /// Sends a `select-layout` custom layout request over the detached RPC channel.
464    pub fn select_custom_layout(
465        &mut self,
466        target: SelectLayoutTarget,
467        layout: String,
468    ) -> Result<Response, ClientError> {
469        self.roundtrip(&Request::SelectCustomLayout(SelectCustomLayoutRequest {
470            target,
471            layout,
472        }))
473    }
474
475    /// Sends a `select-layout -o` request over the detached RPC channel.
476    pub fn select_old_layout(
477        &mut self,
478        target: SelectLayoutTarget,
479    ) -> Result<Response, ClientError> {
480        self.roundtrip(&Request::SelectOldLayout(SelectOldLayoutRequest { target }))
481    }
482
483    /// Sends a `select-layout -E` request over the detached RPC channel.
484    pub fn spread_layout(&mut self, target: SelectLayoutTarget) -> Result<Response, ClientError> {
485        self.roundtrip(&Request::SpreadLayout(SpreadLayoutRequest { target }))
486    }
487
488    /// Sends a `next-layout` request over the detached RPC channel.
489    pub fn next_layout(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
490        self.roundtrip(&Request::NextLayout(rmux_proto::NextLayoutRequest {
491            target,
492        }))
493    }
494
495    /// Sends a `previous-layout` request over the detached RPC channel.
496    pub fn previous_layout(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
497        self.roundtrip(&Request::PreviousLayout(
498            rmux_proto::PreviousLayoutRequest { target },
499        ))
500    }
501}