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.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(Box::new(
302            rmux_proto::RespawnWindowRequest {
303                target,
304                kill,
305                start_directory,
306                environment,
307                command,
308            },
309        )))
310    }
311
312    /// Sends a `split-window` request over the detached RPC channel.
313    pub fn split_window(&mut self, target: SplitWindowTarget) -> Result<Response, ClientError> {
314        self.split_window_with_direction(target, SplitDirection::Vertical)
315    }
316
317    /// Sends a `split-window` request with an explicit direction over the detached RPC channel.
318    pub fn split_window_with_direction(
319        &mut self,
320        target: SplitWindowTarget,
321        direction: SplitDirection,
322    ) -> Result<Response, ClientError> {
323        self.split_window_with_direction_and_environment(target, direction, None)
324    }
325
326    /// Sends a `split-window` request with explicit spawn environment overrides.
327    pub fn split_window_with_direction_and_environment(
328        &mut self,
329        target: SplitWindowTarget,
330        direction: SplitDirection,
331        environment: Option<Vec<String>>,
332    ) -> Result<Response, ClientError> {
333        self.split_window_with_spawn(target, direction, environment, None)
334    }
335
336    /// Sends a `split-window` request with explicit spawn options.
337    ///
338    /// New pane is inserted *after* the target. To insert before (tmux `-b`),
339    /// use [`Connection::split_window_with_options`].
340    pub fn split_window_with_spawn(
341        &mut self,
342        target: SplitWindowTarget,
343        direction: SplitDirection,
344        environment: Option<Vec<String>>,
345        command: Option<Vec<String>>,
346    ) -> Result<Response, ClientError> {
347        self.split_window_with_start_directory(target, direction, environment, None, command)
348    }
349
350    /// Sends a `split-window` request with an optional working-directory override.
351    ///
352    /// New pane is inserted *after* the target. To insert before (tmux `-b`),
353    /// use [`Connection::split_window_with_options`] without a start directory.
354    pub fn split_window_with_start_directory(
355        &mut self,
356        target: SplitWindowTarget,
357        direction: SplitDirection,
358        environment: Option<Vec<String>>,
359        start_directory: Option<std::path::PathBuf>,
360        command: Option<Vec<String>>,
361    ) -> Result<Response, ClientError> {
362        if command.is_some() || start_directory.is_some() {
363            return self.roundtrip(&Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
364                target,
365                direction,
366                before: false,
367                environment,
368                command,
369                process_command: None,
370                start_directory,
371                keep_alive_on_exit: None,
372                detached: false,
373                size: None,
374                preserve_zoom: false,
375                full_size: false,
376                stdin_payload: None,
377            })));
378        }
379        self.split_window_with_options(SplitWindowOptions {
380            target,
381            direction,
382            before: false,
383            environment,
384            command,
385        })
386    }
387
388    /// Sends a `split-window` request with full options including `before`.
389    pub fn split_window_with_options(
390        &mut self,
391        options: SplitWindowOptions,
392    ) -> Result<Response, ClientError> {
393        let SplitWindowOptions {
394            target,
395            direction,
396            before,
397            environment,
398            command,
399        } = options;
400        if command.is_some() {
401            return self.roundtrip(&Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
402                target,
403                direction,
404                before,
405                environment,
406                command,
407                process_command: None,
408                start_directory: None,
409                keep_alive_on_exit: None,
410                detached: false,
411                size: None,
412                preserve_zoom: false,
413                full_size: false,
414                stdin_payload: None,
415            })));
416        }
417        self.roundtrip(&Request::SplitWindow(SplitWindowRequest {
418            target,
419            direction,
420            before,
421            environment,
422        }))
423    }
424
425    /// Sends a `split-window` request with server-side target resolution.
426    pub fn split_window_target_action(
427        &mut self,
428        request: SplitWindowTargetActionRequest,
429    ) -> Result<Response, ClientError> {
430        self.roundtrip(&Request::SplitWindowTargetAction(Box::new(request)))
431    }
432
433    /// Sends a `select-layout` request over the detached RPC channel.
434    pub fn select_layout(
435        &mut self,
436        target: SelectLayoutTarget,
437        layout: LayoutName,
438    ) -> Result<Response, ClientError> {
439        self.roundtrip(&Request::SelectLayout(SelectLayoutRequest {
440            target,
441            layout,
442        }))
443    }
444
445    /// Sends a `select-layout` custom layout request over the detached RPC channel.
446    pub fn select_custom_layout(
447        &mut self,
448        target: SelectLayoutTarget,
449        layout: String,
450    ) -> Result<Response, ClientError> {
451        self.roundtrip(&Request::SelectCustomLayout(SelectCustomLayoutRequest {
452            target,
453            layout,
454        }))
455    }
456
457    /// Sends a `select-layout -o` request over the detached RPC channel.
458    pub fn select_old_layout(
459        &mut self,
460        target: SelectLayoutTarget,
461    ) -> Result<Response, ClientError> {
462        self.roundtrip(&Request::SelectOldLayout(SelectOldLayoutRequest { target }))
463    }
464
465    /// Sends a `select-layout -E` request over the detached RPC channel.
466    pub fn spread_layout(&mut self, target: SelectLayoutTarget) -> Result<Response, ClientError> {
467        self.roundtrip(&Request::SpreadLayout(SpreadLayoutRequest { target }))
468    }
469
470    /// Sends a `next-layout` request over the detached RPC channel.
471    pub fn next_layout(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
472        self.roundtrip(&Request::NextLayout(rmux_proto::NextLayoutRequest {
473            target,
474        }))
475    }
476
477    /// Sends a `previous-layout` request over the detached RPC channel.
478    pub fn previous_layout(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
479        self.roundtrip(&Request::PreviousLayout(
480            rmux_proto::PreviousLayoutRequest { target },
481        ))
482    }
483}