Skip to main content

rmux_client/commands/
pane.rs

1use rmux_proto::{
2    BreakPaneRequest, CancelSdkWaitRequest, ClockModeRequest, CopyModeRequest, DisplayPanesRequest,
3    JoinPaneRequest, KillPaneRequest, LastPaneRequest, MovePaneRequest, PaneBroadcastInputRequest,
4    PaneInputRequest, PaneOutputCursorRequest, PaneOutputSubscriptionId,
5    PaneOutputSubscriptionStart, PaneSnapshotRefRequest, PaneTarget, PaneTargetRef,
6    PipePaneRequest, Request, ResizePaneAdjustment, ResizePaneRequest,
7    ResizePaneTargetActionRequest, RespawnPaneRequest, Response, SdkWaitForOutputRefRequest,
8    SdkWaitId, SdkWaitOwnerId, SelectPaneAdjacentRequest, SelectPaneDirection,
9    SelectPaneMarkRequest, SelectPaneRequest, SendKeysExt2Request, SendKeysExtRequest,
10    SendKeysRequest, SendPrefixRequest, SessionName, SubscribePaneOutputRefRequest,
11    SwapPaneDirection, SwapPaneRequest, UnsubscribePaneOutputRequest, WindowTarget,
12    CAPABILITY_SDK_PANE_BROADCAST, CAPABILITY_SDK_PANE_BY_ID, CAPABILITY_SDK_WAITS,
13    CAPABILITY_TARGET_CLIENT_COMMANDS,
14};
15
16use crate::{connection::Connection, ClientError};
17
18impl Connection {
19    /// Sends a `swap-pane` request over the detached RPC channel.
20    pub fn swap_pane(
21        &mut self,
22        source: PaneTarget,
23        target: PaneTarget,
24        detached: bool,
25        preserve_zoom: bool,
26    ) -> Result<Response, ClientError> {
27        self.roundtrip(&Request::SwapPane(SwapPaneRequest {
28            source,
29            target,
30            direction: None,
31            detached,
32            preserve_zoom,
33        }))
34    }
35
36    /// Sends `swap-pane -D` over the detached RPC channel.
37    pub fn swap_pane_with_next(
38        &mut self,
39        target: PaneTarget,
40        detached: bool,
41        preserve_zoom: bool,
42    ) -> Result<Response, ClientError> {
43        self.roundtrip(&Request::SwapPane(SwapPaneRequest {
44            source: target.clone(),
45            target,
46            direction: Some(SwapPaneDirection::Down),
47            detached,
48            preserve_zoom,
49        }))
50    }
51
52    /// Sends `swap-pane -U` over the detached RPC channel.
53    pub fn swap_pane_with_previous(
54        &mut self,
55        target: PaneTarget,
56        detached: bool,
57        preserve_zoom: bool,
58    ) -> Result<Response, ClientError> {
59        self.roundtrip(&Request::SwapPane(SwapPaneRequest {
60            source: target.clone(),
61            target,
62            direction: Some(SwapPaneDirection::Up),
63            detached,
64            preserve_zoom,
65        }))
66    }
67
68    /// Sends a `last-pane` request over the detached RPC channel.
69    pub fn last_pane(&mut self, target: WindowTarget) -> Result<Response, ClientError> {
70        self.last_pane_with_zoom(target, false)
71    }
72
73    /// Sends a `last-pane` request with optional zoom preservation.
74    pub fn last_pane_with_zoom(
75        &mut self,
76        target: WindowTarget,
77        preserve_zoom: bool,
78    ) -> Result<Response, ClientError> {
79        self.last_pane_with_options(target, preserve_zoom, None)
80    }
81
82    /// Sends a `last-pane` request with tmux selection options.
83    pub fn last_pane_with_options(
84        &mut self,
85        target: WindowTarget,
86        preserve_zoom: bool,
87        input_disabled: Option<bool>,
88    ) -> Result<Response, ClientError> {
89        self.roundtrip(&Request::LastPane(LastPaneRequest {
90            target,
91            preserve_zoom,
92            input_disabled,
93        }))
94    }
95
96    /// Sends a `join-pane` request over the detached RPC channel.
97    pub fn join_pane(&mut self, request: JoinPaneRequest) -> Result<Response, ClientError> {
98        self.roundtrip(&Request::JoinPane(request))
99    }
100
101    /// Sends a `move-pane` request over the detached RPC channel.
102    pub fn move_pane(&mut self, request: MovePaneRequest) -> Result<Response, ClientError> {
103        self.roundtrip(&Request::MovePane(request))
104    }
105
106    /// Sends a `break-pane` request over the detached RPC channel.
107    pub fn break_pane(&mut self, request: BreakPaneRequest) -> Result<Response, ClientError> {
108        self.roundtrip(&Request::BreakPane(Box::new(request)))
109    }
110
111    /// Sends a `resize-pane` request over the detached RPC channel.
112    pub fn resize_pane(
113        &mut self,
114        target: PaneTarget,
115        adjustment: ResizePaneAdjustment,
116    ) -> Result<Response, ClientError> {
117        self.roundtrip(&Request::ResizePane(ResizePaneRequest {
118            target,
119            adjustment,
120        }))
121    }
122
123    /// Sends a `resize-pane` request with server-side target resolution.
124    pub fn resize_pane_target_action(
125        &mut self,
126        request: ResizePaneTargetActionRequest,
127    ) -> Result<Response, ClientError> {
128        self.roundtrip(&Request::ResizePaneTargetAction(request))
129    }
130
131    /// Sends a `display-panes` request over the detached RPC channel.
132    pub fn display_panes(
133        &mut self,
134        target: SessionName,
135        duration_ms: Option<u64>,
136        non_blocking: bool,
137        no_command: bool,
138        template: Option<String>,
139    ) -> Result<Response, ClientError> {
140        let request = Request::DisplayPanes(DisplayPanesRequest {
141            target,
142            duration_ms,
143            non_blocking,
144            no_command,
145            template,
146        });
147        if non_blocking {
148            self.roundtrip(&request)
149        } else {
150            self.roundtrip_without_read_timeout(&request)
151        }
152    }
153
154    /// Sends a `pipe-pane` request over the detached RPC channel.
155    pub fn pipe_pane(
156        &mut self,
157        target: PaneTarget,
158        stdin: bool,
159        stdout: bool,
160        once: bool,
161        command: Option<String>,
162    ) -> Result<Response, ClientError> {
163        self.roundtrip(&Request::PipePane(PipePaneRequest {
164            target,
165            stdin,
166            stdout,
167            once,
168            command,
169        }))
170    }
171
172    /// Sends a `respawn-pane` request over the detached RPC channel.
173    pub fn respawn_pane(&mut self, request: RespawnPaneRequest) -> Result<Response, ClientError> {
174        self.roundtrip(&Request::RespawnPane(Box::new(request)))
175    }
176
177    /// Sends a `select-pane` request over the detached RPC channel.
178    pub fn select_pane(&mut self, target: PaneTarget) -> Result<Response, ClientError> {
179        self.select_pane_with_title(target, None)
180    }
181
182    /// Sends a `select-pane` request with an optional title over the detached RPC channel.
183    pub fn select_pane_with_title(
184        &mut self,
185        target: PaneTarget,
186        title: Option<String>,
187    ) -> Result<Response, ClientError> {
188        self.select_pane_with_title_and_zoom(target, title, false)
189    }
190
191    /// Sends a `select-pane` request with optional title and zoom preservation.
192    pub fn select_pane_with_title_and_zoom(
193        &mut self,
194        target: PaneTarget,
195        title: Option<String>,
196        preserve_zoom: bool,
197    ) -> Result<Response, ClientError> {
198        self.select_pane_with_options(target, title, None, None, preserve_zoom)
199    }
200
201    /// Sends a `select-pane` request with optional title, style, input state, and zoom preservation.
202    pub fn select_pane_with_options(
203        &mut self,
204        target: PaneTarget,
205        title: Option<String>,
206        style: Option<String>,
207        input_disabled: Option<bool>,
208        preserve_zoom: bool,
209    ) -> Result<Response, ClientError> {
210        self.roundtrip(&Request::SelectPane(Box::new(SelectPaneRequest {
211            target,
212            title,
213            input_disabled,
214            preserve_zoom,
215            style,
216        })))
217    }
218
219    /// Sends a `select-pane -d` / `-e` input-state request.
220    pub fn select_pane_input(
221        &mut self,
222        target: PaneTarget,
223        disabled: bool,
224    ) -> Result<Response, ClientError> {
225        self.roundtrip(&Request::SelectPane(Box::new(SelectPaneRequest {
226            target,
227            title: None,
228            style: None,
229            input_disabled: Some(disabled),
230            preserve_zoom: false,
231        })))
232    }
233
234    /// Sends a directional `select-pane` request over the detached RPC channel.
235    pub fn select_pane_adjacent(
236        &mut self,
237        target: PaneTarget,
238        direction: SelectPaneDirection,
239    ) -> Result<Response, ClientError> {
240        self.select_pane_adjacent_with_zoom(target, direction, false)
241    }
242
243    /// Sends a directional `select-pane` request with optional zoom preservation.
244    pub fn select_pane_adjacent_with_zoom(
245        &mut self,
246        target: PaneTarget,
247        direction: SelectPaneDirection,
248        preserve_zoom: bool,
249    ) -> Result<Response, ClientError> {
250        self.roundtrip(&Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
251            target,
252            direction,
253            preserve_zoom,
254        }))
255    }
256
257    /// Sends `select-pane -m` or `select-pane -M` over the detached RPC channel.
258    pub fn select_pane_mark(
259        &mut self,
260        target: PaneTarget,
261        clear: bool,
262    ) -> Result<Response, ClientError> {
263        self.select_pane_mark_with_title(target, clear, None)
264    }
265
266    /// Sends `select-pane -m/-M` with an optional title over the detached RPC channel.
267    pub fn select_pane_mark_with_title(
268        &mut self,
269        target: PaneTarget,
270        clear: bool,
271        title: Option<String>,
272    ) -> Result<Response, ClientError> {
273        self.roundtrip(&Request::SelectPaneMark(SelectPaneMarkRequest {
274            target,
275            clear,
276            title,
277        }))
278    }
279
280    /// Sends a `kill-pane` request over the detached RPC channel.
281    pub fn kill_pane(&mut self, target: PaneTarget) -> Result<Response, ClientError> {
282        self.kill_pane_with_options(target, false)
283    }
284
285    /// Sends a `kill-pane` request with extended tmux flags.
286    pub fn kill_pane_with_options(
287        &mut self,
288        target: PaneTarget,
289        kill_all_except: bool,
290    ) -> Result<Response, ClientError> {
291        self.roundtrip(&Request::KillPane(KillPaneRequest {
292            target,
293            kill_all_except,
294        }))
295    }
296
297    /// Sends a `send-keys` request over the detached RPC channel.
298    pub fn send_keys(
299        &mut self,
300        target: PaneTarget,
301        keys: Vec<String>,
302    ) -> Result<Response, ClientError> {
303        self.roundtrip(&Request::SendKeys(SendKeysRequest { target, keys }))
304    }
305
306    /// Sends an extended `send-keys` request over the detached RPC channel.
307    pub fn send_keys_extended(
308        &mut self,
309        request: SendKeysExtRequest,
310    ) -> Result<Response, ClientError> {
311        self.roundtrip(&Request::SendKeysExt(request))
312    }
313
314    /// Sends a target-client aware `send-keys` request over detached RPC.
315    pub fn send_keys_extended_target_client(
316        &mut self,
317        request: SendKeysExt2Request,
318    ) -> Result<Response, ClientError> {
319        if !self.supports_capability(CAPABILITY_TARGET_CLIENT_COMMANDS)? {
320            return Err(ClientError::Protocol(
321                rmux_proto::RmuxError::UnsupportedCapability {
322                    feature: CAPABILITY_TARGET_CLIENT_COMMANDS.to_owned(),
323                    supported: Vec::new(),
324                },
325            ));
326        }
327        self.roundtrip(&Request::SendKeysExt2(Box::new(request)))
328    }
329
330    /// Sends a stable-target SDK pane input request over detached RPC.
331    pub fn pane_input(&mut self, request: PaneInputRequest) -> Result<Response, ClientError> {
332        if !self.supports_capability(CAPABILITY_SDK_PANE_BY_ID)? {
333            return Err(ClientError::Protocol(
334                rmux_proto::RmuxError::UnsupportedCapability {
335                    feature: CAPABILITY_SDK_PANE_BY_ID.to_owned(),
336                    supported: Vec::new(),
337                },
338            ));
339        }
340        self.roundtrip(&Request::PaneInput(request))
341    }
342
343    /// Sends a stable-target SDK pane input broadcast request over detached RPC.
344    pub fn pane_broadcast_input(
345        &mut self,
346        request: PaneBroadcastInputRequest,
347    ) -> Result<Response, ClientError> {
348        if !self.supports_capability(CAPABILITY_SDK_PANE_BROADCAST)? {
349            return Err(ClientError::Protocol(
350                rmux_proto::RmuxError::UnsupportedCapability {
351                    feature: CAPABILITY_SDK_PANE_BROADCAST.to_owned(),
352                    supported: Vec::new(),
353                },
354            ));
355        }
356        self.roundtrip(&Request::PaneBroadcastInput(request))
357    }
358
359    /// Captures a stable-target structured pane snapshot over detached RPC.
360    pub fn pane_snapshot_ref(&mut self, target: PaneTargetRef) -> Result<Response, ClientError> {
361        if !self.supports_capability(CAPABILITY_SDK_PANE_BY_ID)? {
362            return Err(ClientError::Protocol(
363                rmux_proto::RmuxError::UnsupportedCapability {
364                    feature: CAPABILITY_SDK_PANE_BY_ID.to_owned(),
365                    supported: Vec::new(),
366                },
367            ));
368        }
369        self.roundtrip(&Request::PaneSnapshotRef(PaneSnapshotRefRequest { target }))
370    }
371
372    /// Subscribes to stable-target pane output.
373    pub fn subscribe_pane_output_ref(
374        &mut self,
375        target: PaneTargetRef,
376        start: PaneOutputSubscriptionStart,
377    ) -> Result<Response, ClientError> {
378        if !self.supports_capability(CAPABILITY_SDK_PANE_BY_ID)? {
379            return Err(ClientError::Protocol(
380                rmux_proto::RmuxError::UnsupportedCapability {
381                    feature: CAPABILITY_SDK_PANE_BY_ID.to_owned(),
382                    supported: Vec::new(),
383                },
384            ));
385        }
386        self.roundtrip(&Request::SubscribePaneOutputRef(
387            SubscribePaneOutputRefRequest { target, start },
388        ))
389    }
390
391    /// Polls a pane-output subscription cursor.
392    pub fn pane_output_cursor(
393        &mut self,
394        subscription_id: PaneOutputSubscriptionId,
395        max_events: Option<u16>,
396    ) -> Result<Response, ClientError> {
397        self.roundtrip(&Request::PaneOutputCursor(PaneOutputCursorRequest {
398            subscription_id,
399            max_events,
400        }))
401    }
402
403    /// Unsubscribes from pane output.
404    pub fn unsubscribe_pane_output(
405        &mut self,
406        subscription_id: PaneOutputSubscriptionId,
407    ) -> Result<Response, ClientError> {
408        self.roundtrip(&Request::UnsubscribePaneOutput(
409            UnsubscribePaneOutputRequest { subscription_id },
410        ))
411    }
412
413    /// Arms a daemon-backed stable-target SDK byte wait.
414    pub fn sdk_wait_for_output_ref(
415        &mut self,
416        owner_id: SdkWaitOwnerId,
417        wait_id: SdkWaitId,
418        target: PaneTargetRef,
419        bytes: Vec<u8>,
420        start: PaneOutputSubscriptionStart,
421    ) -> Result<Response, ClientError> {
422        if !self.supports_capability(CAPABILITY_SDK_WAITS)? {
423            return Err(ClientError::Protocol(
424                rmux_proto::RmuxError::UnsupportedCapability {
425                    feature: CAPABILITY_SDK_WAITS.to_owned(),
426                    supported: Vec::new(),
427                },
428            ));
429        }
430        self.roundtrip_without_read_timeout(&Request::SdkWaitForOutputRef(
431            SdkWaitForOutputRefRequest {
432                owner_id,
433                wait_id,
434                target,
435                bytes,
436                start,
437            },
438        ))
439    }
440
441    /// Writes a stable-target SDK byte wait request without reading its response.
442    ///
443    /// Callers use this to arm a future-output wait before performing another
444    /// action, then read the response later on the same connection.
445    pub fn arm_sdk_wait_for_output_ref(
446        &mut self,
447        owner_id: SdkWaitOwnerId,
448        wait_id: SdkWaitId,
449        target: PaneTargetRef,
450        bytes: Vec<u8>,
451        start: PaneOutputSubscriptionStart,
452    ) -> Result<(), ClientError> {
453        if !self.supports_capability(CAPABILITY_SDK_WAITS)? {
454            return Err(ClientError::Protocol(
455                rmux_proto::RmuxError::UnsupportedCapability {
456                    feature: CAPABILITY_SDK_WAITS.to_owned(),
457                    supported: Vec::new(),
458                },
459            ));
460        }
461        self.write_request(&Request::SdkWaitForOutputRef(SdkWaitForOutputRefRequest {
462            owner_id,
463            wait_id,
464            target,
465            bytes,
466            start,
467        }))
468    }
469
470    /// Cancels a daemon-backed SDK byte wait.
471    pub fn cancel_sdk_wait(
472        &mut self,
473        owner_id: SdkWaitOwnerId,
474        wait_id: SdkWaitId,
475    ) -> Result<Response, ClientError> {
476        if !self.supports_capability(CAPABILITY_SDK_WAITS)? {
477            return Err(ClientError::Protocol(
478                rmux_proto::RmuxError::UnsupportedCapability {
479                    feature: CAPABILITY_SDK_WAITS.to_owned(),
480                    supported: Vec::new(),
481                },
482            ));
483        }
484        self.roundtrip(&Request::CancelSdkWait(CancelSdkWaitRequest {
485            owner_id,
486            wait_id,
487        }))
488    }
489
490    /// Sends a `send-prefix` request over the detached RPC channel.
491    pub fn send_prefix(
492        &mut self,
493        target: Option<PaneTarget>,
494        secondary: bool,
495    ) -> Result<Response, ClientError> {
496        self.roundtrip(&Request::SendPrefix(SendPrefixRequest {
497            target,
498            secondary,
499        }))
500    }
501
502    /// Sends a `copy-mode` request over the detached RPC channel.
503    pub fn copy_mode(&mut self, request: CopyModeRequest) -> Result<Response, ClientError> {
504        self.roundtrip(&Request::CopyMode(request))
505    }
506
507    /// Sends a `clock-mode` request over the detached RPC channel.
508    pub fn clock_mode(&mut self, target: Option<PaneTarget>) -> Result<Response, ClientError> {
509        self.roundtrip(&Request::ClockMode(ClockModeRequest { target }))
510    }
511}