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