tmux_rs/
lib.rs

1// Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
2//
3// Permission to use, copy, modify, and distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
12// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
13// OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14#![expect(rustdoc::broken_intra_doc_links, reason = "github markdown callout")]
15#![cfg_attr(doc, doc = include_str!("../README.md"))]
16#![allow(
17    non_camel_case_types,
18    reason = "this lint is here instead of in Cargo.toml because of a bug in rust analyzer"
19)]
20
21use std::{
22    cmp,
23    ffi::{
24        CStr, CString, c_int, c_long, c_longlong, c_short, c_uchar, c_uint, c_ulonglong, c_void,
25    },
26    io::Write as _,
27    mem::{MaybeUninit, size_of, zeroed},
28    ptr::{NonNull, addr_of, addr_of_mut, null, null_mut},
29    sync::{
30        Mutex,
31        atomic::{self, AtomicBool, AtomicU32},
32    },
33};
34
35mod compat;
36use compat::{queue::*, strlcat, strlcpy, strtonum, strtonum_, tree::*, vis_flags};
37
38mod ncurses_;
39use ncurses_::*;
40
41pub(crate) mod libc;
42pub(crate) use libc::errno;
43pub(crate) use libc::*;
44pub(crate) use libc::{free_, memcpy_, memcpy__, streq_};
45
46#[cfg(feature = "sixel")]
47mod image_;
48#[cfg(feature = "sixel")]
49mod image_sixel;
50#[cfg(feature = "sixel")]
51use image_sixel::sixel_image;
52
53#[cfg(feature = "utempter")]
54mod utempter;
55
56// libevent2
57mod event_;
58use event_::*;
59
60#[inline]
61const fn transmute_ptr<T>(value: Option<NonNull<T>>) -> *mut T {
62    match value {
63        Some(ptr) => ptr.as_ptr(),
64        None => null_mut(),
65    }
66}
67
68use compat::imsg::imsg; // TODO move
69
70// use crate::tmux_protocol_h::*;
71
72type bitstr_t = u8;
73
74unsafe fn bit_alloc(nbits: u32) -> *mut u8 {
75    unsafe { libc::calloc(nbits.div_ceil(8) as usize, 1).cast() }
76}
77unsafe fn bit_set(bits: *mut u8, i: u32) {
78    unsafe {
79        let byte_index = i / 8;
80        let bit_index = i % 8;
81        *bits.add(byte_index as usize) |= 1 << bit_index;
82    }
83}
84
85#[inline]
86unsafe fn bit_clear(bits: *mut u8, i: u32) {
87    unsafe {
88        let byte_index = i / 8;
89        let bit_index = i % 8;
90        *bits.add(byte_index as usize) &= !(1 << bit_index);
91    }
92}
93
94/// clear bits start..=stop in bitstring
95unsafe fn bit_nclear(bits: *mut u8, start: u32, stop: u32) {
96    unsafe {
97        // TODO this is written inefficiently, assuming the compiler will optimize it. if it doesn't rewrite it
98        for i in start..=stop {
99            bit_clear(bits, i);
100        }
101    }
102}
103
104unsafe fn bit_test(bits: *const u8, i: u32) -> bool {
105    unsafe {
106        let byte_index = i / 8;
107        let bit_index = i % 8;
108        (*bits.add(byte_index as usize) & (1 << bit_index)) != 0
109    }
110}
111
112const TTY_NAME_MAX: usize = 32;
113
114// discriminant structs
115struct discr_alerts_entry;
116struct discr_all_entry;
117struct discr_by_uri_entry;
118struct discr_by_inner_entry;
119struct discr_entry;
120struct discr_name_entry;
121struct discr_pending_entry;
122struct discr_sentry;
123struct discr_time_entry;
124struct discr_tree_entry;
125struct discr_wentry;
126
127// /usr/include/paths.h
128const _PATH_TTY: *const u8 = c!("/dev/tty");
129
130const _PATH_BSHELL: *const u8 = c!("/bin/sh");
131const _PATH_BSHELL_STR: &str = "/bin/sh";
132
133const _PATH_DEFPATH: *const u8 = c!("/usr/bin:/bin");
134const _PATH_DEV: *const u8 = c!("/dev/");
135const _PATH_DEVNULL: *const u8 = c!("/dev/null");
136const _PATH_VI: &str = "/usr/bin/vi";
137
138const SIZEOF_PATH_DEV: usize = 6;
139
140macro_rules! env_or {
141    ($key:literal, $default:expr) => {
142        match std::option_env!($key) {
143            Some(value) => value,
144            None => $default,
145        }
146    };
147}
148
149const TMUX_VERSION: &str = env_or!("TMUX_VERSION", env!("CARGO_PKG_VERSION"));
150const TMUX_CONF: &str = env_or!(
151    "TMUX_CONF",
152    "/etc/tmux.conf:~/.tmux.conf:$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"
153);
154const TMUX_SOCK: &str = env_or!("TMUX_SOCK", "$TMUX_TMPDIR:/tmp/");
155const TMUX_TERM: &str = env_or!("TMUX_TERM", "screen");
156const TMUX_LOCK_CMD: &str = env_or!("TMUX_LOCK_CMD", "lock -np");
157
158/// Minimum layout cell size, NOT including border lines.
159const PANE_MINIMUM: u32 = 1;
160
161/// Automatic name refresh interval, in microseconds. Must be < 1 second.
162const NAME_INTERVAL: libc::suseconds_t = 500000;
163
164/// Default pixel cell sizes.
165const DEFAULT_XPIXEL: u32 = 16;
166const DEFAULT_YPIXEL: u32 = 32;
167
168enum_try_from!(alert_option, i32, alert_option::ALERT_OTHER);
169/// Alert option values
170#[expect(dead_code, reason = "enum_try_from transmutes from i32 to enum")]
171#[repr(i32)]
172#[derive(Copy, Clone)]
173enum alert_option {
174    ALERT_NONE,
175    ALERT_ANY,
176    ALERT_CURRENT,
177    ALERT_OTHER,
178}
179
180enum_try_from!(visual_option, i32, visual_option::VISUAL_BOTH);
181/// Visual option values
182#[expect(dead_code, reason = "enum_try_from transmutes from i32 to enum")]
183#[repr(i32)]
184#[derive(Copy, Clone, Eq, PartialEq)]
185enum visual_option {
186    VISUAL_OFF,
187    VISUAL_ON,
188    VISUAL_BOTH,
189}
190
191// No key or unknown key.
192const KEYC_NONE: c_ulonglong = 0x000ff000000000;
193const KEYC_UNKNOWN: c_ulonglong = 0x000fe000000000;
194
195// Base for special (that is, not Unicode) keys. An enum must be at most a
196// signed int, so these are based in the highest Unicode PUA.
197const KEYC_BASE: c_ulonglong = 0x0000000010e000;
198const KEYC_USER: c_ulonglong = 0x0000000010f000;
199const KEYC_USER_END: c_ulonglong = KEYC_USER + KEYC_NUSER;
200
201// Key modifier bits
202const KEYC_META: c_ulonglong = 0x00100000000000;
203const KEYC_CTRL: c_ulonglong = 0x00200000000000;
204const KEYC_SHIFT: c_ulonglong = 0x00400000000000;
205
206// Key flag bits.
207const KEYC_LITERAL: c_ulonglong = 0x01000000000000;
208const KEYC_KEYPAD: c_ulonglong = 0x02000000000000;
209const KEYC_CURSOR: c_ulonglong = 0x04000000000000;
210const KEYC_IMPLIED_META: c_ulonglong = 0x08000000000000;
211const KEYC_BUILD_MODIFIERS: c_ulonglong = 0x10000000000000;
212const KEYC_VI: c_ulonglong = 0x20000000000000;
213const KEYC_SENT: c_ulonglong = 0x40000000000000;
214
215// Masks for key bits.
216const KEYC_MASK_MODIFIERS: c_ulonglong = 0x00f00000000000;
217const KEYC_MASK_FLAGS: c_ulonglong = 0xff000000000000;
218const KEYC_MASK_KEY: c_ulonglong = 0x000fffffffffff;
219
220const KEYC_NUSER: c_ulonglong = 1000;
221
222#[expect(non_snake_case)]
223#[inline(always)]
224fn KEYC_IS_MOUSE(key: key_code) -> bool {
225    const KEYC_MOUSE: c_ulonglong = keyc::KEYC_MOUSE as c_ulonglong;
226    const KEYC_BSPACE: c_ulonglong = keyc::KEYC_BSPACE as c_ulonglong;
227
228    (key & KEYC_MASK_KEY) >= KEYC_MOUSE && (key & KEYC_MASK_KEY) < KEYC_BSPACE
229}
230
231#[expect(non_snake_case)]
232#[inline(always)]
233fn KEYC_IS_UNICODE(key: key_code) -> bool {
234    let masked = key & KEYC_MASK_KEY;
235
236    const KEYC_BASE_END: c_ulonglong = keyc::KEYC_BASE_END as c_ulonglong;
237    masked > 0x7f
238        && !(KEYC_BASE..KEYC_BASE_END).contains(&masked)
239        && !(KEYC_USER..KEYC_USER_END).contains(&masked)
240}
241
242const KEYC_CLICK_TIMEOUT: i32 = 300;
243
244/// A single key. This can be ASCII or Unicode or one of the keys between
245/// KEYC_BASE and KEYC_BASE_END.
246type key_code = core::ffi::c_ulonglong;
247
248// skipped C0 control characters
249
250// C0 control characters
251#[repr(u64)]
252#[derive(Copy, Clone)]
253enum c0 {
254    C0_NUL,
255    C0_SOH,
256    C0_STX,
257    C0_ETX,
258    C0_EOT,
259    C0_ENQ,
260    C0_ASC,
261    C0_BEL,
262    C0_BS,
263    C0_HT,
264    C0_LF,
265    C0_VT,
266    C0_FF,
267    C0_CR,
268    C0_SO,
269    C0_SI,
270    C0_DLE,
271    C0_DC1,
272    C0_DC2,
273    C0_DC3,
274    C0_DC4,
275    C0_NAK,
276    C0_SYN,
277    C0_ETB,
278    C0_CAN,
279    C0_EM,
280    C0_SUB,
281    C0_ESC,
282    C0_FS,
283    C0_GS,
284    C0_RS,
285    C0_US,
286}
287
288// idea write a custom top level macro
289// which allows me to annotate a variant
290// that should be converted to mouse key
291// enum mouse_keys {
292// KEYC_MOUSE,
293//
294// #[keyc_mouse_key]
295// MOUSEMOVE,
296// }
297include!("keyc_mouse_key.rs");
298
299enum_try_from!(tty_code_code, u32, tty_code_code::TTYC_XT);
300/// Termcap codes.
301#[repr(u32)]
302#[derive(Copy, Clone)]
303enum tty_code_code {
304    TTYC_ACSC,
305    TTYC_AM,
306    TTYC_AX,
307    TTYC_BCE,
308    TTYC_BEL,
309    TTYC_BIDI,
310    TTYC_BLINK,
311    TTYC_BOLD,
312    TTYC_CIVIS,
313    TTYC_CLEAR,
314    TTYC_CLMG,
315    TTYC_CMG,
316    TTYC_CNORM,
317    TTYC_COLORS,
318    TTYC_CR,
319    TTYC_CS,
320    TTYC_CSR,
321    TTYC_CUB,
322    TTYC_CUB1,
323    TTYC_CUD,
324    TTYC_CUD1,
325    TTYC_CUF,
326    TTYC_CUF1,
327    TTYC_CUP,
328    TTYC_CUU,
329    TTYC_CUU1,
330    TTYC_CVVIS,
331    TTYC_DCH,
332    TTYC_DCH1,
333    TTYC_DIM,
334    TTYC_DL,
335    TTYC_DL1,
336    TTYC_DSBP,
337    TTYC_DSEKS,
338    TTYC_DSFCS,
339    TTYC_DSMG,
340    TTYC_E3,
341    TTYC_ECH,
342    TTYC_ED,
343    TTYC_EL,
344    TTYC_EL1,
345    TTYC_ENACS,
346    TTYC_ENBP,
347    TTYC_ENEKS,
348    TTYC_ENFCS,
349    TTYC_ENMG,
350    TTYC_FSL,
351    TTYC_HLS,
352    TTYC_HOME,
353    TTYC_HPA,
354    TTYC_ICH,
355    TTYC_ICH1,
356    TTYC_IL,
357    TTYC_IL1,
358    TTYC_INDN,
359    TTYC_INVIS,
360    TTYC_KCBT,
361    TTYC_KCUB1,
362    TTYC_KCUD1,
363    TTYC_KCUF1,
364    TTYC_KCUU1,
365    TTYC_KDC2,
366    TTYC_KDC3,
367    TTYC_KDC4,
368    TTYC_KDC5,
369    TTYC_KDC6,
370    TTYC_KDC7,
371    TTYC_KDCH1,
372    TTYC_KDN2,
373    TTYC_KDN3,
374    TTYC_KDN4,
375    TTYC_KDN5,
376    TTYC_KDN6,
377    TTYC_KDN7,
378    TTYC_KEND,
379    TTYC_KEND2,
380    TTYC_KEND3,
381    TTYC_KEND4,
382    TTYC_KEND5,
383    TTYC_KEND6,
384    TTYC_KEND7,
385    TTYC_KF1,
386    TTYC_KF10,
387    TTYC_KF11,
388    TTYC_KF12,
389    TTYC_KF13,
390    TTYC_KF14,
391    TTYC_KF15,
392    TTYC_KF16,
393    TTYC_KF17,
394    TTYC_KF18,
395    TTYC_KF19,
396    TTYC_KF2,
397    TTYC_KF20,
398    TTYC_KF21,
399    TTYC_KF22,
400    TTYC_KF23,
401    TTYC_KF24,
402    TTYC_KF25,
403    TTYC_KF26,
404    TTYC_KF27,
405    TTYC_KF28,
406    TTYC_KF29,
407    TTYC_KF3,
408    TTYC_KF30,
409    TTYC_KF31,
410    TTYC_KF32,
411    TTYC_KF33,
412    TTYC_KF34,
413    TTYC_KF35,
414    TTYC_KF36,
415    TTYC_KF37,
416    TTYC_KF38,
417    TTYC_KF39,
418    TTYC_KF4,
419    TTYC_KF40,
420    TTYC_KF41,
421    TTYC_KF42,
422    TTYC_KF43,
423    TTYC_KF44,
424    TTYC_KF45,
425    TTYC_KF46,
426    TTYC_KF47,
427    TTYC_KF48,
428    TTYC_KF49,
429    TTYC_KF5,
430    TTYC_KF50,
431    TTYC_KF51,
432    TTYC_KF52,
433    TTYC_KF53,
434    TTYC_KF54,
435    TTYC_KF55,
436    TTYC_KF56,
437    TTYC_KF57,
438    TTYC_KF58,
439    TTYC_KF59,
440    TTYC_KF6,
441    TTYC_KF60,
442    TTYC_KF61,
443    TTYC_KF62,
444    TTYC_KF63,
445    TTYC_KF7,
446    TTYC_KF8,
447    TTYC_KF9,
448    TTYC_KHOM2,
449    TTYC_KHOM3,
450    TTYC_KHOM4,
451    TTYC_KHOM5,
452    TTYC_KHOM6,
453    TTYC_KHOM7,
454    TTYC_KHOME,
455    TTYC_KIC2,
456    TTYC_KIC3,
457    TTYC_KIC4,
458    TTYC_KIC5,
459    TTYC_KIC6,
460    TTYC_KIC7,
461    TTYC_KICH1,
462    TTYC_KIND,
463    TTYC_KLFT2,
464    TTYC_KLFT3,
465    TTYC_KLFT4,
466    TTYC_KLFT5,
467    TTYC_KLFT6,
468    TTYC_KLFT7,
469    TTYC_KMOUS,
470    TTYC_KNP,
471    TTYC_KNXT2,
472    TTYC_KNXT3,
473    TTYC_KNXT4,
474    TTYC_KNXT5,
475    TTYC_KNXT6,
476    TTYC_KNXT7,
477    TTYC_KPP,
478    TTYC_KPRV2,
479    TTYC_KPRV3,
480    TTYC_KPRV4,
481    TTYC_KPRV5,
482    TTYC_KPRV6,
483    TTYC_KPRV7,
484    TTYC_KRI,
485    TTYC_KRIT2,
486    TTYC_KRIT3,
487    TTYC_KRIT4,
488    TTYC_KRIT5,
489    TTYC_KRIT6,
490    TTYC_KRIT7,
491    TTYC_KUP2,
492    TTYC_KUP3,
493    TTYC_KUP4,
494    TTYC_KUP5,
495    TTYC_KUP6,
496    TTYC_KUP7,
497    TTYC_MS,
498    TTYC_NOBR,
499    TTYC_OL,
500    TTYC_OP,
501    TTYC_RECT,
502    TTYC_REV,
503    TTYC_RGB,
504    TTYC_RI,
505    TTYC_RIN,
506    TTYC_RMACS,
507    TTYC_RMCUP,
508    TTYC_RMKX,
509    TTYC_SE,
510    TTYC_SETAB,
511    TTYC_SETAF,
512    TTYC_SETAL,
513    TTYC_SETRGBB,
514    TTYC_SETRGBF,
515    TTYC_SETULC,
516    TTYC_SETULC1,
517    TTYC_SGR0,
518    TTYC_SITM,
519    TTYC_SMACS,
520    TTYC_SMCUP,
521    TTYC_SMKX,
522    TTYC_SMOL,
523    TTYC_SMSO,
524    TTYC_SMUL,
525    TTYC_SMULX,
526    TTYC_SMXX,
527    TTYC_SXL,
528    TTYC_SS,
529    TTYC_SWD,
530    TTYC_SYNC,
531    TTYC_TC,
532    TTYC_TSL,
533    TTYC_U8,
534    TTYC_VPA,
535    TTYC_XT,
536}
537
538const WHITESPACE: *const u8 = c!(" ");
539
540enum_try_from!(modekey, i32, modekey::MODEKEY_VI);
541#[repr(i32)]
542#[derive(Copy, Clone, Eq, PartialEq)]
543enum modekey {
544    MODEKEY_EMACS = 0,
545    MODEKEY_VI = 1,
546}
547
548bitflags::bitflags! {
549    /// Grid flags.
550    #[repr(transparent)]
551    #[derive(Copy, Clone, Eq, PartialEq)]
552    struct mode_flag : i32 {
553        const MODE_CURSOR = 0x1;
554        const MODE_INSERT = 0x2;
555        const MODE_KCURSOR = 0x4;
556        const MODE_KKEYPAD = 0x8;
557        const MODE_WRAP = 0x10;
558        const MODE_MOUSE_STANDARD = 0x20;
559        const MODE_MOUSE_BUTTON = 0x40;
560        const MODE_CURSOR_BLINKING = 0x80;
561        const MODE_MOUSE_UTF8 = 0x100;
562        const MODE_MOUSE_SGR = 0x200;
563        const MODE_BRACKETPASTE = 0x400;
564        const MODE_FOCUSON = 0x800;
565        const MODE_MOUSE_ALL = 0x1000;
566        const MODE_ORIGIN = 0x2000;
567        const MODE_CRLF = 0x4000;
568        const MODE_KEYS_EXTENDED = 0x8000;
569        const MODE_CURSOR_VERY_VISIBLE = 0x10000;
570        const MODE_CURSOR_BLINKING_SET = 0x20000;
571        const MODE_KEYS_EXTENDED_2 = 0x40000;
572    }
573}
574
575#[expect(dead_code)]
576const ALL_MODES: i32 = 0xffffff;
577const ALL_MOUSE_MODES: mode_flag = mode_flag::MODE_MOUSE_STANDARD
578    .union(mode_flag::MODE_MOUSE_BUTTON)
579    .union(mode_flag::MODE_MOUSE_ALL);
580const MOTION_MOUSE_MODES: mode_flag = mode_flag::MODE_MOUSE_BUTTON.union(mode_flag::MODE_MOUSE_ALL);
581const CURSOR_MODES: mode_flag = mode_flag::MODE_CURSOR
582    .union(mode_flag::MODE_CURSOR_BLINKING)
583    .union(mode_flag::MODE_CURSOR_VERY_VISIBLE);
584const EXTENDED_KEY_MODES: mode_flag =
585    mode_flag::MODE_KEYS_EXTENDED.union(mode_flag::MODE_KEYS_EXTENDED_2);
586
587// Mouse protocol constants.
588const MOUSE_PARAM_MAX: u32 = 0xff;
589const MOUSE_PARAM_UTF8_MAX: u32 = 0x7ff;
590const MOUSE_PARAM_BTN_OFF: u32 = 0x20;
591const MOUSE_PARAM_POS_OFF: u32 = 0x21;
592
593// A single UTF-8 character.
594type utf8_char = c_uint;
595
596// An expanded UTF-8 character. UTF8_SIZE must be big enough to hold combining
597// characters as well. It can't be more than 32 bytes without changes to how
598// characters are stored.
599const UTF8_SIZE: usize = 21;
600
601#[repr(C)]
602#[derive(Copy, Clone)]
603struct utf8_data {
604    data: [u8; UTF8_SIZE],
605
606    have: u8,
607    size: u8, /* TODO check the codebase for things checking if size == 0, which is the sentinal value */
608    /// 0xff if invalid
609    width: u8,
610}
611
612impl utf8_data {
613    const fn new<const N: usize>(data: [u8; N], have: u8, size: u8, width: u8) -> Self {
614        if N >= UTF8_SIZE {
615            panic!("invalid size");
616        }
617
618        let mut padded_data = [0u8; UTF8_SIZE];
619        let mut i = 0usize;
620        while i < N {
621            padded_data[i] = data[i];
622            i += 1;
623        }
624
625        Self {
626            data: padded_data,
627            have,
628            size,
629            width,
630        }
631    }
632
633    fn initialized_slice(&self) -> &[u8] {
634        &self.data[..self.size as usize]
635    }
636}
637
638#[repr(i32)]
639#[derive(Copy, Clone, Eq, PartialEq)]
640enum utf8_state {
641    UTF8_MORE,
642    UTF8_DONE,
643    UTF8_ERROR,
644}
645
646// Colour flags.
647const COLOUR_FLAG_256: i32 = 0x01000000;
648const COLOUR_FLAG_RGB: i32 = 0x02000000;
649
650/// Special colours.
651#[expect(non_snake_case)]
652#[inline]
653fn COLOUR_DEFAULT(c: i32) -> bool {
654    c == 8 || c == 9
655}
656
657// Replacement palette.
658#[repr(C)]
659#[derive(Copy, Clone)]
660struct colour_palette {
661    fg: i32,
662    bg: i32,
663
664    palette: *mut i32,
665    default_palette: *mut i32,
666}
667
668// Grid attributes. Anything above 0xff is stored in an extended cell.
669bitflags::bitflags! {
670    /// Grid flags.
671    #[repr(transparent)]
672    #[derive(Copy, Clone, Eq, PartialEq)]
673    struct grid_attr : u16 {
674        const GRID_ATTR_BRIGHT = 0x1;
675        const GRID_ATTR_DIM = 0x2;
676        const GRID_ATTR_UNDERSCORE = 0x4;
677        const GRID_ATTR_BLINK = 0x8;
678        const GRID_ATTR_REVERSE = 0x10;
679        const GRID_ATTR_HIDDEN = 0x20;
680        const GRID_ATTR_ITALICS = 0x40;
681        const GRID_ATTR_CHARSET = 0x80; // alternative character set
682        const GRID_ATTR_STRIKETHROUGH = 0x100;
683        const GRID_ATTR_UNDERSCORE_2 = 0x200;
684        const GRID_ATTR_UNDERSCORE_3 = 0x400;
685        const GRID_ATTR_UNDERSCORE_4 = 0x800;
686        const GRID_ATTR_UNDERSCORE_5 = 0x1000;
687        const GRID_ATTR_OVERLINE = 0x2000;
688    }
689}
690
691/// All underscore attributes.
692const GRID_ATTR_ALL_UNDERSCORE: grid_attr = grid_attr::GRID_ATTR_UNDERSCORE
693    .union(grid_attr::GRID_ATTR_UNDERSCORE_2)
694    .union(grid_attr::GRID_ATTR_UNDERSCORE_3)
695    .union(grid_attr::GRID_ATTR_UNDERSCORE_4)
696    .union(grid_attr::GRID_ATTR_UNDERSCORE_5);
697
698bitflags::bitflags! {
699    /// Grid flags.
700    #[repr(transparent)]
701    #[derive(Copy, Clone, Eq, PartialEq)]
702    struct grid_flag : u8 {
703        const FG256 = 0x1;
704        const BG256 = 0x2;
705        const PADDING = 0x4;
706        const EXTENDED = 0x8;
707        const SELECTED = 0x10;
708        const NOPALETTE = 0x20;
709        const CLEARED = 0x40;
710    }
711}
712
713bitflags::bitflags! {
714    /// Grid line flags.
715    #[repr(transparent)]
716    #[derive(Copy, Clone, Eq, PartialEq)]
717    struct grid_line_flag: i32 {
718        const WRAPPED      = 1 << 0; // 0x1
719        const EXTENDED     = 1 << 1; // 0x2
720        const DEAD         = 1 << 2; // 0x4
721        const START_PROMPT = 1 << 3; // 0x8
722        const START_OUTPUT = 1 << 4; // 0x10
723    }
724}
725
726bitflags::bitflags! {
727    /// Grid string flags.
728    #[repr(transparent)]
729    #[derive(Copy, Clone, Eq, PartialEq)]
730    struct grid_string_flags: i32 {
731        const GRID_STRING_WITH_SEQUENCES = 0x1;
732        const GRID_STRING_ESCAPE_SEQUENCES = 0x2;
733        const GRID_STRING_TRIM_SPACES = 0x4;
734        const GRID_STRING_USED_ONLY = 0x8;
735        const GRID_STRING_EMPTY_CELLS = 0x10;
736    }
737}
738
739/// Cell positions.
740#[repr(i32)]
741#[derive(Copy, Clone, Eq, PartialEq)]
742enum cell_type {
743    CELL_INSIDE = 0,
744    CELL_TOPBOTTOM = 1,
745    CELL_LEFTRIGHT = 2,
746    CELL_TOPLEFT = 3,
747    CELL_TOPRIGHT = 4,
748    CELL_BOTTOMLEFT = 5,
749    CELL_BOTTOMRIGHT = 6,
750    CELL_TOPJOIN = 7,
751    CELL_BOTTOMJOIN = 8,
752    CELL_LEFTJOIN = 9,
753    CELL_RIGHTJOIN = 10,
754    CELL_JOIN = 11,
755    CELL_OUTSIDE = 12,
756}
757use cell_type::*; // TODO remove
758
759// Cell borders.
760const CELL_BORDERS: [u8; 13] = [
761    b' ', b'x', b'q', b'l', b'k', b'm', b'j', b'w', b'v', b't', b'u', b'n', b'~',
762];
763const SIMPLE_BORDERS: [u8; 13] = [
764    b' ', b'|', b'-', b'+', b'+', b'+', b'+', b'+', b'+', b'+', b'+', b'+', b'.',
765];
766const PADDED_BORDERS: [u8; 13] = [b' '; 13];
767
768/// Grid cell data.
769#[repr(C)]
770#[derive(Copy, Clone)]
771struct grid_cell {
772    data: utf8_data,
773    attr: grid_attr,
774    flags: grid_flag,
775    fg: i32,
776    bg: i32,
777    us: i32,
778    link: u32,
779}
780
781impl grid_cell {
782    const fn new(
783        data: utf8_data,
784        attr: grid_attr,
785        flags: grid_flag,
786        fg: i32,
787        bg: i32,
788        us: i32,
789        link: u32,
790    ) -> Self {
791        Self {
792            data,
793            attr,
794            flags,
795            fg,
796            bg,
797            us,
798            link,
799        }
800    }
801}
802
803/// Grid extended cell entry.
804#[repr(C)]
805struct grid_extd_entry {
806    data: utf8_char,
807    attr: u16,
808    flags: u8,
809    fg: i32,
810    bg: i32,
811    us: i32,
812    link: u32,
813}
814
815#[derive(Copy, Clone)]
816#[repr(C, align(4))]
817struct grid_cell_entry_data {
818    attr: u8,
819    fg: u8,
820    bg: u8,
821    data: u8,
822}
823
824#[repr(C)]
825union grid_cell_entry_union {
826    offset: u32,
827    data: grid_cell_entry_data,
828}
829
830#[repr(C)]
831struct grid_cell_entry {
832    union_: grid_cell_entry_union,
833    flags: grid_flag,
834}
835
836/// Grid line.
837#[repr(C)]
838struct grid_line {
839    celldata: *mut grid_cell_entry,
840    cellused: u32,
841    cellsize: u32,
842
843    extddata: *mut grid_extd_entry,
844    extdsize: u32,
845
846    flags: grid_line_flag,
847    time: time_t,
848}
849
850const GRID_HISTORY: i32 = 0x1; // scroll lines into history
851
852/// Entire grid of cells.
853#[repr(C)]
854struct grid {
855    flags: i32,
856
857    sx: u32,
858    sy: u32,
859
860    hscrolled: u32,
861    hsize: u32,
862    hlimit: u32,
863
864    linedata: *mut grid_line,
865}
866
867/// Virtual cursor in a grid.
868#[repr(C)]
869struct grid_reader {
870    gd: *mut grid,
871    cx: u32,
872    cy: u32,
873}
874
875/// Style alignment.
876#[repr(i32)]
877#[derive(Copy, Clone, Eq, PartialEq)]
878enum style_align {
879    STYLE_ALIGN_DEFAULT,
880    STYLE_ALIGN_LEFT,
881    STYLE_ALIGN_CENTRE,
882    STYLE_ALIGN_RIGHT,
883    STYLE_ALIGN_ABSOLUTE_CENTRE,
884}
885
886/// Style list.
887#[repr(i32)]
888#[derive(Copy, Clone, Eq, PartialEq)]
889enum style_list {
890    STYLE_LIST_OFF,
891    STYLE_LIST_ON,
892    STYLE_LIST_FOCUS,
893    STYLE_LIST_LEFT_MARKER,
894    STYLE_LIST_RIGHT_MARKER,
895}
896
897/// Style range.
898#[repr(i32)]
899#[derive(Copy, Clone, Eq, PartialEq)]
900enum style_range_type {
901    STYLE_RANGE_NONE,
902    STYLE_RANGE_LEFT,
903    STYLE_RANGE_RIGHT,
904    STYLE_RANGE_PANE,
905    STYLE_RANGE_WINDOW,
906    STYLE_RANGE_SESSION,
907    STYLE_RANGE_USER,
908}
909
910impl_tailq_entry!(style_range, entry, tailq_entry<style_range>);
911// #[derive(crate::compat::TailQEntry)]
912#[repr(C)]
913struct style_range {
914    type_: style_range_type,
915    argument: u32,
916    string: [u8; 16],
917    start: u32,
918    /// not included
919    end: u32,
920
921    // #[entry]
922    entry: tailq_entry<style_range>,
923}
924type style_ranges = tailq_head<style_range>;
925
926/// Style default.
927#[repr(i32)]
928#[derive(Copy, Clone, Eq, PartialEq)]
929enum style_default_type {
930    STYLE_DEFAULT_BASE,
931    STYLE_DEFAULT_PUSH,
932    STYLE_DEFAULT_POP,
933}
934
935/// Style option.
936#[repr(C)]
937#[derive(Copy, Clone)]
938struct style {
939    gc: grid_cell,
940    ignore: i32,
941
942    fill: i32,
943    align: style_align,
944    list: style_list,
945
946    range_type: style_range_type,
947    range_argument: u32,
948    range_string: [u8; 16],
949
950    default_type: style_default_type,
951}
952
953#[cfg(feature = "sixel")]
954impl crate::compat::queue::Entry<image, discr_all_entry> for image {
955    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<image> {
956        unsafe { &raw mut (*this).all_entry }
957    }
958}
959#[cfg(feature = "sixel")]
960impl crate::compat::queue::Entry<image, discr_entry> for image {
961    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<image> {
962        unsafe { &raw mut (*this).entry }
963    }
964}
965#[cfg(feature = "sixel")]
966#[repr(C)]
967#[derive(Copy, Clone)]
968struct image {
969    s: *mut screen,
970    data: *mut sixel_image,
971    fallback: *mut u8,
972    px: u32,
973    py: u32,
974    sx: u32,
975    sy: u32,
976
977    all_entry: tailq_entry<image>,
978    entry: tailq_entry<image>,
979}
980
981#[cfg(feature = "sixel")]
982type images = tailq_head<image>;
983
984/// Cursor style.
985#[repr(i32)]
986#[derive(Copy, Clone, Eq, PartialEq)]
987enum screen_cursor_style {
988    SCREEN_CURSOR_DEFAULT,
989    SCREEN_CURSOR_BLOCK,
990    SCREEN_CURSOR_UNDERLINE,
991    SCREEN_CURSOR_BAR,
992}
993
994/// Virtual screen.
995#[repr(C)]
996#[derive(Clone)]
997struct screen {
998    title: *mut u8,
999    path: *mut u8,
1000    titles: *mut screen_titles,
1001
1002    /// grid data
1003    grid: *mut grid,
1004
1005    /// cursor x
1006    cx: u32,
1007    /// cursor y
1008    cy: u32,
1009
1010    /// cursor style
1011    cstyle: screen_cursor_style,
1012    default_cstyle: screen_cursor_style,
1013    /// cursor colour
1014    ccolour: i32,
1015    /// default cursor colour
1016    default_ccolour: i32,
1017
1018    /// scroll region top
1019    rupper: u32,
1020    /// scroll region bottom
1021    rlower: u32,
1022
1023    mode: mode_flag,
1024    default_mode: mode_flag,
1025
1026    saved_cx: u32,
1027    saved_cy: u32,
1028    saved_grid: *mut grid,
1029    saved_cell: grid_cell,
1030    saved_flags: i32,
1031
1032    tabs: *mut bitstr_t,
1033    sel: *mut screen_sel,
1034
1035    #[cfg(feature = "sixel")]
1036    images: images,
1037
1038    write_list: *mut screen_write_cline,
1039
1040    hyperlinks: *mut hyperlinks,
1041}
1042
1043const SCREEN_WRITE_SYNC: i32 = 0x1;
1044
1045// Screen write context.
1046type screen_write_init_ctx_cb = Option<unsafe fn(*mut screen_write_ctx, *mut tty_ctx)>;
1047#[repr(C)]
1048struct screen_write_ctx {
1049    wp: *mut window_pane,
1050    s: *mut screen,
1051
1052    flags: i32,
1053
1054    init_ctx_cb: screen_write_init_ctx_cb,
1055
1056    arg: *mut c_void,
1057
1058    item: *mut screen_write_citem,
1059    scrolled: u32,
1060    bg: u32,
1061}
1062
1063enum_try_from!(box_lines, i32, box_lines::BOX_LINES_NONE);
1064/// Box border lines option.
1065#[expect(dead_code, reason = "enum_try_from transmutes from i32 to enum")]
1066#[repr(i32)]
1067#[derive(Copy, Clone, Default, Eq, PartialEq)]
1068enum box_lines {
1069    #[default]
1070    BOX_LINES_DEFAULT = -1,
1071    BOX_LINES_SINGLE,
1072    BOX_LINES_DOUBLE,
1073    BOX_LINES_HEAVY,
1074    BOX_LINES_SIMPLE,
1075    BOX_LINES_ROUNDED,
1076    BOX_LINES_PADDED,
1077    BOX_LINES_NONE,
1078}
1079
1080enum_try_from!(pane_lines, i32, pane_lines::PANE_LINES_NUMBER);
1081#[expect(dead_code, reason = "enum_try_from transmutes from i32 to enum")]
1082/// Pane border lines option.
1083#[repr(i32)]
1084#[derive(Copy, Clone, Default, Eq, PartialEq)]
1085enum pane_lines {
1086    #[default]
1087    PANE_LINES_SINGLE,
1088    PANE_LINES_DOUBLE,
1089    PANE_LINES_HEAVY,
1090    PANE_LINES_SIMPLE,
1091    PANE_LINES_NUMBER,
1092}
1093
1094enum_try_from!(
1095    pane_border_indicator,
1096    i32,
1097    pane_border_indicator::PANE_BORDER_BOTH
1098);
1099#[expect(dead_code, reason = "enum_try_from transmutes from i32 to enum")]
1100#[repr(i32)]
1101#[derive(Copy, Clone)]
1102enum pane_border_indicator {
1103    PANE_BORDER_OFF,
1104    PANE_BORDER_COLOUR,
1105    PANE_BORDER_ARROWS,
1106    PANE_BORDER_BOTH,
1107}
1108
1109// Mode returned by window_pane_mode function.
1110const WINDOW_PANE_NO_MODE: i32 = 0;
1111const WINDOW_PANE_COPY_MODE: i32 = 1;
1112const WINDOW_PANE_VIEW_MODE: i32 = 2;
1113
1114// Screen redraw context.
1115#[repr(C)]
1116struct screen_redraw_ctx {
1117    c: *mut client,
1118
1119    statuslines: u32,
1120    statustop: i32,
1121
1122    pane_status: pane_status,
1123    pane_lines: pane_lines,
1124
1125    no_pane_gc: grid_cell,
1126    no_pane_gc_set: i32,
1127
1128    sx: u32,
1129    sy: u32,
1130    ox: u32,
1131    oy: u32,
1132}
1133
1134unsafe fn screen_size_x(s: *const screen) -> u32 {
1135    unsafe { (*(*s).grid).sx }
1136}
1137unsafe fn screen_size_y(s: *const screen) -> u32 {
1138    unsafe { (*(*s).grid).sy }
1139}
1140unsafe fn screen_hsize(s: *const screen) -> u32 {
1141    unsafe { (*(*s).grid).hsize }
1142}
1143unsafe fn screen_hlimit(s: *const screen) -> u32 {
1144    unsafe { (*(*s).grid).hlimit }
1145}
1146
1147// Menu.
1148#[repr(C)]
1149struct menu_item {
1150    name: SyncCharPtr,
1151    key: key_code,
1152    command: SyncCharPtr,
1153}
1154impl menu_item {
1155    const fn new(name: &'static CStr, key: key_code, command: *const u8) -> Self {
1156        Self {
1157            name: SyncCharPtr::new(name),
1158            key,
1159            command: SyncCharPtr(command),
1160        }
1161    }
1162}
1163
1164#[repr(C)]
1165struct menu {
1166    title: *const u8,
1167    items: *mut menu_item,
1168    count: u32,
1169    width: u32,
1170}
1171type menu_choice_cb = Option<unsafe fn(*mut menu, u32, key_code, *mut c_void)>;
1172
1173#[expect(clippy::type_complexity)]
1174/// Window mode. Windows can be in several modes and this is used to call the
1175/// right function to handle input and output.
1176#[repr(C)]
1177struct window_mode {
1178    name: SyncCharPtr,
1179    default_format: SyncCharPtr,
1180
1181    init: unsafe fn(NonNull<window_mode_entry>, *mut cmd_find_state, *mut args) -> *mut screen,
1182    free: unsafe fn(NonNull<window_mode_entry>),
1183    resize: unsafe fn(NonNull<window_mode_entry>, u32, u32),
1184    update: Option<unsafe fn(NonNull<window_mode_entry>)>,
1185    key: Option<
1186        unsafe fn(
1187            NonNull<window_mode_entry>,
1188            *mut client,
1189            *mut session,
1190            *mut winlink,
1191            key_code,
1192            *mut mouse_event,
1193        ),
1194    >,
1195
1196    key_table: Option<unsafe fn(*mut window_mode_entry) -> *const u8>,
1197    command: Option<
1198        unsafe fn(
1199            NonNull<window_mode_entry>,
1200            *mut client,
1201            *mut session,
1202            *mut winlink,
1203            *mut args,
1204            *mut mouse_event,
1205        ),
1206    >,
1207    formats: Option<unsafe fn(*mut window_mode_entry, *mut format_tree)>,
1208}
1209
1210// Active window mode.
1211impl_tailq_entry!(window_mode_entry, entry, tailq_entry<window_mode_entry>);
1212#[repr(C)]
1213struct window_mode_entry {
1214    wp: *mut window_pane,
1215    swp: *mut window_pane,
1216
1217    mode: *const window_mode,
1218    data: *mut c_void,
1219
1220    screen: *mut screen,
1221    prefix: u32,
1222
1223    // #[entry]
1224    entry: tailq_entry<window_mode_entry>,
1225}
1226
1227/// Offsets into pane buffer.
1228#[repr(C)]
1229#[derive(Copy, Clone)]
1230struct window_pane_offset {
1231    used: usize,
1232}
1233
1234impl_tailq_entry!(window_pane_resize, entry, tailq_entry<window_pane_resize>);
1235/// Queued pane resize.
1236#[repr(C)]
1237struct window_pane_resize {
1238    sx: u32,
1239    sy: u32,
1240
1241    osx: u32,
1242    osy: u32,
1243
1244    entry: tailq_entry<window_pane_resize>,
1245}
1246type window_pane_resizes = tailq_head<window_pane_resize>;
1247
1248bitflags::bitflags! {
1249    #[repr(transparent)]
1250    #[derive(Copy, Clone, Eq, PartialEq)]
1251    struct window_pane_flags : i32 {
1252        const PANE_REDRAW = 0x1;
1253        const PANE_DROP = 0x2;
1254        const PANE_FOCUSED = 0x4;
1255        const PANE_VISITED = 0x8;
1256        /* 0x10 unused */
1257        /* 0x20 unused */
1258        const PANE_INPUTOFF = 0x40;
1259        const PANE_CHANGED = 0x80;
1260        const PANE_EXITED = 0x100;
1261        const PANE_STATUSREADY = 0x200;
1262        const PANE_STATUSDRAWN = 0x400;
1263        const PANE_EMPTY = 0x800;
1264        const PANE_STYLECHANGED = 0x1000;
1265        const PANE_UNSEENCHANGES = 0x2000;
1266    }
1267}
1268
1269/// Child window structure.
1270#[repr(C)]
1271struct window_pane {
1272    id: u32,
1273    active_point: u32,
1274
1275    window: *mut window,
1276    options: *mut options,
1277
1278    layout_cell: *mut layout_cell,
1279    saved_layout_cell: *mut layout_cell,
1280
1281    sx: u32,
1282    sy: u32,
1283
1284    xoff: u32,
1285    yoff: u32,
1286
1287    flags: window_pane_flags,
1288
1289    argc: i32,
1290    argv: *mut *mut u8,
1291    shell: *mut u8,
1292    cwd: *mut u8,
1293
1294    pid: pid_t,
1295    tty: [u8; TTY_NAME_MAX],
1296    status: i32,
1297    dead_time: timeval,
1298
1299    fd: i32,
1300    event: *mut bufferevent,
1301
1302    offset: window_pane_offset,
1303    base_offset: usize,
1304
1305    resize_queue: window_pane_resizes,
1306    resize_timer: event,
1307
1308    ictx: *mut input_ctx,
1309
1310    cached_gc: grid_cell,
1311    cached_active_gc: grid_cell,
1312    palette: colour_palette,
1313
1314    pipe_fd: i32,
1315    pipe_event: *mut bufferevent,
1316    pipe_offset: window_pane_offset,
1317
1318    screen: *mut screen,
1319    base: screen,
1320
1321    status_screen: screen,
1322    status_size: usize,
1323
1324    modes: tailq_head<window_mode_entry>,
1325
1326    searchstr: *mut u8,
1327    searchregex: i32,
1328
1329    border_gc_set: i32,
1330    border_gc: grid_cell,
1331
1332    control_bg: i32,
1333    control_fg: i32,
1334
1335    /// link in list of all panes
1336    entry: tailq_entry<window_pane>,
1337    /// link in list of last visited
1338    sentry: tailq_entry<window_pane>,
1339    tree_entry: rb_entry<window_pane>,
1340}
1341type window_panes = tailq_head<window_pane>;
1342type window_pane_tree = rb_head<window_pane>;
1343
1344impl Entry<window_pane, discr_entry> for window_pane {
1345    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<window_pane> {
1346        unsafe { &raw mut (*this).entry }
1347    }
1348}
1349impl Entry<window_pane, discr_sentry> for window_pane {
1350    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<window_pane> {
1351        unsafe { &raw mut (*this).sentry }
1352    }
1353}
1354
1355bitflags::bitflags! {
1356    #[repr(transparent)]
1357    #[derive(Copy, Clone, Eq, PartialEq)]
1358    struct window_flag: i32 {
1359        const BELL = 0x1;
1360        const ACTIVITY = 0x2;
1361        const SILENCE = 0x4;
1362        const ZOOMED = 0x8;
1363        const WASZOOMED = 0x10;
1364        const RESIZE = 0x20;
1365    }
1366}
1367const WINDOW_ALERTFLAGS: window_flag = window_flag::BELL
1368    .union(window_flag::ACTIVITY)
1369    .union(window_flag::SILENCE);
1370
1371/// Window structure.
1372#[repr(C)]
1373struct window {
1374    id: u32,
1375    latest: *mut c_void,
1376
1377    name: *mut u8,
1378    name_event: event,
1379    name_time: timeval,
1380
1381    alerts_timer: event,
1382    offset_timer: event,
1383
1384    activity_time: timeval,
1385
1386    active: *mut window_pane,
1387    last_panes: window_panes,
1388    panes: window_panes,
1389
1390    lastlayout: i32,
1391    layout_root: *mut layout_cell,
1392    saved_layout_root: *mut layout_cell,
1393    old_layout: *mut u8,
1394
1395    sx: u32,
1396    sy: u32,
1397    manual_sx: u32,
1398    manual_sy: u32,
1399    xpixel: u32,
1400    ypixel: u32,
1401
1402    new_sx: u32,
1403    new_sy: u32,
1404    new_xpixel: u32,
1405    new_ypixel: u32,
1406
1407    fill_character: *mut utf8_data,
1408    flags: window_flag,
1409
1410    alerts_queued: i32,
1411    alerts_entry: tailq_entry<window>,
1412
1413    options: *mut options,
1414
1415    references: u32,
1416    winlinks: tailq_head<winlink>,
1417    entry: rb_entry<window>,
1418}
1419type windows = rb_head<window>;
1420// crate::compat::impl_rb_tree_protos!(windows, window);
1421
1422impl crate::compat::queue::Entry<window, discr_alerts_entry> for window {
1423    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<window> {
1424        unsafe { &raw mut (*this).alerts_entry }
1425    }
1426}
1427
1428bitflags::bitflags! {
1429    #[repr(transparent)]
1430    #[derive(Copy, Clone, Eq, PartialEq)]
1431    struct winlink_flags: i32 {
1432        const WINLINK_BELL = 0x1;
1433        const WINLINK_ACTIVITY = 0x2;
1434        const WINLINK_SILENCE = 0x4;
1435        const WINLINK_VISITED = 0x8;
1436    }
1437}
1438const WINLINK_ALERTFLAGS: winlink_flags = winlink_flags::WINLINK_BELL
1439    .union(winlink_flags::WINLINK_ACTIVITY)
1440    .union(winlink_flags::WINLINK_SILENCE);
1441
1442#[repr(C)]
1443#[derive(Copy, Clone)]
1444struct winlink {
1445    idx: i32,
1446    session: *mut session,
1447    window: *mut window,
1448
1449    flags: winlink_flags,
1450
1451    entry: rb_entry<winlink>,
1452
1453    wentry: tailq_entry<winlink>,
1454    sentry: tailq_entry<winlink>,
1455}
1456
1457impl crate::compat::queue::Entry<winlink, discr_wentry> for winlink {
1458    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<winlink> {
1459        unsafe { &raw mut (*this).wentry }
1460    }
1461}
1462
1463impl crate::compat::queue::Entry<winlink, discr_sentry> for winlink {
1464    unsafe fn entry(this: *mut Self) -> *mut tailq_entry<winlink> {
1465        unsafe { &raw mut (*this).sentry }
1466    }
1467}
1468
1469type winlinks = rb_head<winlink>;
1470// crate::compat::impl_rb_tree_protos!(winlinks, winlink);
1471type winlink_stack = tailq_head<winlink>;
1472// crate::compat::impl_rb_tree_protos!(winlink_stack, winlink);
1473
1474enum_try_from!(
1475    window_size_option,
1476    i32,
1477    window_size_option::WINDOW_SIZE_LATEST
1478);
1479/// Window size option.
1480#[repr(i32)]
1481#[derive(Copy, Clone, Eq, PartialEq)]
1482enum window_size_option {
1483    WINDOW_SIZE_LARGEST,
1484    WINDOW_SIZE_SMALLEST,
1485    WINDOW_SIZE_MANUAL,
1486    WINDOW_SIZE_LATEST,
1487}
1488
1489enum_try_from!(pane_status, i32, pane_status::PANE_STATUS_BOTTOM);
1490/// Pane border status option.
1491#[repr(i32)]
1492#[derive(Copy, Clone, Eq, PartialEq)]
1493enum pane_status {
1494    PANE_STATUS_OFF,
1495    PANE_STATUS_TOP,
1496    PANE_STATUS_BOTTOM,
1497}
1498
1499enum_try_from!(layout_type, i32, layout_type::LAYOUT_WINDOWPANE);
1500/// Layout direction.
1501#[repr(i32)]
1502#[derive(Copy, Clone, Eq, PartialEq)]
1503enum layout_type {
1504    LAYOUT_LEFTRIGHT,
1505    LAYOUT_TOPBOTTOM,
1506    LAYOUT_WINDOWPANE,
1507}
1508
1509/// Layout cells queue.
1510type layout_cells = tailq_head<layout_cell>;
1511
1512impl_tailq_entry!(layout_cell, entry, tailq_entry<layout_cell>);
1513/// Layout cell.
1514#[repr(C)]
1515struct layout_cell {
1516    type_: layout_type,
1517
1518    parent: *mut layout_cell,
1519
1520    sx: u32,
1521    sy: u32,
1522
1523    xoff: u32,
1524    yoff: u32,
1525
1526    wp: *mut window_pane,
1527    cells: layout_cells,
1528
1529    entry: tailq_entry<layout_cell>,
1530}
1531
1532bitflags::bitflags! {
1533    #[repr(transparent)]
1534    #[derive(Copy, Clone)]
1535    struct environ_flags: i32 {
1536        const ENVIRON_HIDDEN = 0x1;
1537    }
1538}
1539const ENVIRON_HIDDEN: environ_flags = environ_flags::ENVIRON_HIDDEN;
1540
1541/// Environment variable.
1542#[repr(C)]
1543struct environ_entry {
1544    name: Option<NonNull<u8>>,
1545    value: Option<NonNull<u8>>,
1546
1547    flags: environ_flags,
1548    entry: rb_entry<environ_entry>,
1549}
1550
1551/// Client session.
1552#[repr(C)]
1553struct session_group {
1554    name: *const u8,
1555    sessions: tailq_head<session>,
1556
1557    entry: rb_entry<session_group>,
1558}
1559type session_groups = rb_head<session_group>;
1560
1561const SESSION_PASTING: i32 = 0x1;
1562const SESSION_ALERTED: i32 = 0x2;
1563
1564#[repr(C)]
1565struct session {
1566    id: u32,
1567    name: *mut u8,
1568    cwd: *mut u8,
1569
1570    creation_time: timeval,
1571    last_attached_time: timeval,
1572    activity_time: timeval,
1573    last_activity_time: timeval,
1574
1575    lock_timer: event,
1576
1577    curw: *mut winlink,
1578    lastw: winlink_stack,
1579    windows: winlinks,
1580
1581    statusat: i32,
1582    statuslines: u32,
1583
1584    options: *mut options,
1585
1586    flags: i32,
1587
1588    attached: u32,
1589
1590    tio: *mut termios,
1591
1592    environ: *mut environ,
1593
1594    references: i32,
1595
1596    gentry: tailq_entry<session>,
1597    entry: rb_entry<session>,
1598}
1599type sessions = rb_head<session>;
1600impl_tailq_entry!(session, gentry, tailq_entry<session>);
1601
1602const MOUSE_MASK_BUTTONS: u32 = 195;
1603const MOUSE_MASK_SHIFT: u32 = 4;
1604const MOUSE_MASK_META: u32 = 8;
1605const MOUSE_MASK_CTRL: u32 = 16;
1606const MOUSE_MASK_DRAG: u32 = 32;
1607const MOUSE_MASK_MODIFIERS: u32 = MOUSE_MASK_SHIFT | MOUSE_MASK_META | MOUSE_MASK_CTRL;
1608
1609// Mouse wheel type.
1610const MOUSE_WHEEL_UP: u32 = 64;
1611const MOUSE_WHEEL_DOWN: u32 = 65;
1612
1613// Mouse button type.
1614const MOUSE_BUTTON_1: u32 = 0;
1615const MOUSE_BUTTON_2: u32 = 1;
1616const MOUSE_BUTTON_3: u32 = 2;
1617const MOUSE_BUTTON_6: u32 = 66;
1618const MOUSE_BUTTON_7: u32 = 67;
1619const MOUSE_BUTTON_8: u32 = 128;
1620const MOUSE_BUTTON_9: u32 = 129;
1621const MOUSE_BUTTON_10: u32 = 130;
1622const MOUSE_BUTTON_11: u32 = 131;
1623
1624// Mouse helpers.
1625#[expect(non_snake_case)]
1626#[inline]
1627fn MOUSE_BUTTONS(b: u32) -> u32 {
1628    b & MOUSE_MASK_BUTTONS
1629}
1630#[expect(non_snake_case)]
1631#[inline]
1632fn MOUSE_WHEEL(b: u32) -> bool {
1633    ((b) & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_UP || ((b) & MOUSE_MASK_BUTTONS) == MOUSE_WHEEL_DOWN
1634}
1635#[expect(non_snake_case)]
1636#[inline]
1637fn MOUSE_DRAG(b: u32) -> bool {
1638    b & MOUSE_MASK_DRAG != 0
1639}
1640#[expect(non_snake_case)]
1641#[inline]
1642fn MOUSE_RELEASE(b: u32) -> bool {
1643    b & MOUSE_MASK_BUTTONS == 3
1644}
1645
1646/// Mouse input.
1647#[repr(C)]
1648#[derive(Copy, Clone)]
1649struct mouse_event {
1650    valid: bool,
1651    ignore: i32,
1652
1653    key: key_code,
1654
1655    statusat: i32,
1656    statuslines: u32,
1657
1658    x: u32,
1659    y: u32,
1660    b: u32,
1661
1662    lx: u32,
1663    ly: u32,
1664    lb: u32,
1665
1666    ox: u32,
1667    oy: u32,
1668
1669    s: i32,
1670    w: i32,
1671    wp: i32,
1672
1673    sgr_type: u32,
1674    sgr_b: u32,
1675}
1676
1677/// Key event.
1678#[repr(C)]
1679struct key_event {
1680    key: key_code,
1681    m: mouse_event,
1682}
1683
1684bitflags::bitflags! {
1685    #[repr(transparent)]
1686    #[derive(Copy, Clone)]
1687    struct term_flags: i32 {
1688        const TERM_256COLOURS = 0x1;
1689        const TERM_NOAM = 0x2;
1690        const TERM_DECSLRM = 0x4;
1691        const TERM_DECFRA = 0x8;
1692        const TERM_RGBCOLOURS = 0x10;
1693        const TERM_VT100LIKE = 0x20;
1694        const TERM_SIXEL = 0x40;
1695    }
1696}
1697
1698/// Terminal definition.
1699#[repr(C)]
1700struct tty_term {
1701    name: *mut u8,
1702    tty: *mut tty,
1703    features: i32,
1704
1705    acs: [[u8; 2]; c_uchar::MAX as usize + 1],
1706
1707    codes: *mut tty_code,
1708
1709    flags: term_flags,
1710
1711    entry: list_entry<tty_term>,
1712}
1713type tty_terms = list_head<tty_term>;
1714impl ListEntry<tty_term, discr_entry> for tty_term {
1715    unsafe fn field(this: *mut Self) -> *mut list_entry<tty_term> {
1716        unsafe { &raw mut (*this).entry }
1717    }
1718}
1719
1720bitflags::bitflags! {
1721    #[repr(transparent)]
1722    #[derive(Copy, Clone)]
1723    struct tty_flags: i32 {
1724        const TTY_NOCURSOR = 0x1;
1725        const TTY_FREEZE = 0x2;
1726        const TTY_TIMER = 0x4;
1727        const TTY_NOBLOCK = 0x8;
1728        const TTY_STARTED = 0x10;
1729        const TTY_OPENED = 0x20;
1730        const TTY_OSC52QUERY = 0x40;
1731        const TTY_BLOCK = 0x80;
1732        const TTY_HAVEDA = 0x100; // Primary DA.
1733        const TTY_HAVEXDA = 0x200;
1734        const TTY_SYNCING = 0x400;
1735        const TTY_HAVEDA2 = 0x800; // Secondary DA.
1736    }
1737}
1738const TTY_ALL_REQUEST_FLAGS: tty_flags = tty_flags::TTY_HAVEDA
1739    .union(tty_flags::TTY_HAVEDA2)
1740    .union(tty_flags::TTY_HAVEXDA);
1741
1742/// Client terminal.
1743#[repr(C)]
1744struct tty {
1745    client: *mut client,
1746    start_timer: event,
1747    clipboard_timer: event,
1748    last_requests: time_t,
1749
1750    sx: u32,
1751    sy: u32,
1752
1753    xpixel: u32,
1754    ypixel: u32,
1755
1756    cx: u32,
1757    cy: u32,
1758    cstyle: screen_cursor_style,
1759    ccolour: i32,
1760
1761    oflag: i32,
1762    oox: u32,
1763    ooy: u32,
1764    osx: u32,
1765    osy: u32,
1766
1767    mode: mode_flag,
1768    fg: i32,
1769    bg: i32,
1770
1771    rlower: u32,
1772    rupper: u32,
1773
1774    rleft: u32,
1775    rright: u32,
1776
1777    event_in: event,
1778    in_: *mut evbuffer,
1779    event_out: event,
1780    out: *mut evbuffer,
1781    timer: event,
1782    discarded: usize,
1783
1784    tio: termios,
1785
1786    cell: grid_cell,
1787    last_cell: grid_cell,
1788
1789    flags: tty_flags,
1790
1791    term: *mut tty_term,
1792
1793    mouse_last_x: u32,
1794    mouse_last_y: u32,
1795    mouse_last_b: u32,
1796    mouse_drag_flag: i32,
1797    mouse_drag_update: Option<unsafe fn(*mut client, *mut mouse_event)>,
1798    mouse_drag_release: Option<unsafe fn(*mut client, *mut mouse_event)>,
1799
1800    key_timer: event,
1801    key_tree: *mut tty_key,
1802}
1803
1804type tty_ctx_redraw_cb = Option<unsafe fn(*const tty_ctx)>;
1805type tty_ctx_set_client_cb = Option<unsafe fn(*mut tty_ctx, *mut client) -> i32>;
1806
1807#[repr(C)]
1808struct tty_ctx {
1809    s: *mut screen,
1810
1811    redraw_cb: tty_ctx_redraw_cb,
1812    set_client_cb: tty_ctx_set_client_cb,
1813    arg: *mut c_void,
1814
1815    cell: *const grid_cell,
1816    wrapped: bool,
1817
1818    num: u32,
1819    ptr: *mut c_void,
1820    ptr2: *mut c_void,
1821
1822    allow_invisible_panes: i32,
1823
1824    // Cursor and region position before the screen was updated - this is
1825    // where the command should be applied; the values in the screen have
1826    // already been updated.
1827    ocx: u32,
1828    ocy: u32,
1829
1830    orupper: u32,
1831    orlower: u32,
1832
1833    // Target region (usually pane) offset and size.
1834    xoff: u32,
1835    yoff: u32,
1836    rxoff: u32,
1837    ryoff: u32,
1838    sx: u32,
1839    sy: u32,
1840
1841    // The background colour used for clearing (erasing).
1842    bg: u32,
1843
1844    // The default colours and palette.
1845    defaults: grid_cell,
1846    palette: *const colour_palette,
1847
1848    // Containing region (usually window) offset and size.
1849    bigger: i32,
1850    wox: u32,
1851    woy: u32,
1852    wsx: u32,
1853    wsy: u32,
1854}
1855
1856// Saved message entry.
1857impl_tailq_entry!(message_entry, entry, tailq_entry<message_entry>);
1858#[repr(C)]
1859struct message_entry {
1860    msg: *mut u8,
1861    msg_num: u32,
1862    msg_time: timeval,
1863
1864    entry: tailq_entry<message_entry>,
1865}
1866type message_list = tailq_head<message_entry>;
1867
1868/// Argument type.
1869#[repr(i32)]
1870#[derive(Copy, Clone, Eq, PartialEq)]
1871enum args_type {
1872    ARGS_NONE,
1873    ARGS_STRING,
1874    ARGS_COMMANDS,
1875}
1876
1877#[repr(C)]
1878union args_value_union {
1879    string: *mut u8,
1880    cmdlist: *mut cmd_list,
1881}
1882
1883impl_tailq_entry!(args_value, entry, tailq_entry<args_value>);
1884/// Argument value.
1885#[repr(C)]
1886struct args_value {
1887    type_: args_type,
1888    union_: args_value_union,
1889    cached: *mut u8,
1890    // #[entry]
1891    entry: tailq_entry<args_value>,
1892}
1893type args_tree = rb_head<args_entry>;
1894
1895/// Arguments parsing type.
1896#[repr(C)]
1897#[derive(Eq, PartialEq)]
1898enum args_parse_type {
1899    ARGS_PARSE_INVALID,
1900    ARGS_PARSE_STRING,
1901    ARGS_PARSE_COMMANDS_OR_STRING,
1902    #[expect(dead_code)]
1903    ARGS_PARSE_COMMANDS,
1904}
1905
1906type args_parse_cb = Option<unsafe fn(*mut args, u32, *mut *mut u8) -> args_parse_type>;
1907#[repr(C)]
1908struct args_parse {
1909    template: SyncCharPtr,
1910    lower: i32,
1911    upper: i32,
1912    cb: args_parse_cb,
1913}
1914
1915impl args_parse {
1916    const fn new(template: &'static CStr, lower: i32, upper: i32, cb: args_parse_cb) -> Self {
1917        Self {
1918            template: SyncCharPtr::new(template),
1919            lower,
1920            upper,
1921            cb,
1922        }
1923    }
1924}
1925
1926/// Command find structures.
1927#[repr(C)]
1928#[derive(Copy, Clone, Default)]
1929enum cmd_find_type {
1930    #[default]
1931    CMD_FIND_PANE,
1932    CMD_FIND_WINDOW,
1933    CMD_FIND_SESSION,
1934}
1935
1936#[repr(C)]
1937#[derive(Copy, Clone)]
1938struct cmd_find_state {
1939    flags: cmd_find_flags,
1940    current: *mut cmd_find_state,
1941
1942    s: *mut session,
1943    wl: *mut winlink,
1944    w: *mut window,
1945    wp: *mut window_pane,
1946    idx: i32,
1947}
1948
1949bitflags::bitflags! {
1950    // Command find flags.
1951    #[repr(transparent)]
1952    #[derive(Copy, Clone, Default, Eq, PartialEq)]
1953    struct cmd_find_flags: i32 {
1954        const CMD_FIND_PREFER_UNATTACHED = 0x1;
1955        const CMD_FIND_QUIET = 0x2;
1956        const CMD_FIND_WINDOW_INDEX = 0x4;
1957        const CMD_FIND_DEFAULT_MARKED = 0x8;
1958        const CMD_FIND_EXACT_SESSION = 0x10;
1959        const CMD_FIND_EXACT_WINDOW = 0x20;
1960        const CMD_FIND_CANFAIL = 0x40;
1961    }
1962}
1963
1964/// List of commands.
1965#[repr(C)]
1966struct cmd_list {
1967    references: i32,
1968    group: u32,
1969    list: *mut cmds,
1970}
1971
1972// Command return values.
1973#[repr(i32)]
1974#[derive(Copy, Clone, Eq, PartialEq)]
1975enum cmd_retval {
1976    CMD_RETURN_ERROR = -1,
1977    CMD_RETURN_NORMAL = 0,
1978    CMD_RETURN_WAIT,
1979    CMD_RETURN_STOP,
1980}
1981
1982// Command parse result.
1983#[repr(i32)]
1984#[derive(Copy, Clone, Default, Eq, PartialEq)]
1985enum cmd_parse_status {
1986    #[default]
1987    CMD_PARSE_ERROR,
1988    CMD_PARSE_SUCCESS,
1989}
1990
1991type cmd_parse_result = Result<*mut cmd_list /* cmdlist */, *mut u8 /* error */>;
1992
1993bitflags::bitflags! {
1994    #[repr(transparent)]
1995    #[derive(Copy, Clone, Eq, PartialEq)]
1996    struct cmd_parse_input_flags: i32 {
1997        const CMD_PARSE_QUIET = 0x1;
1998        const CMD_PARSE_PARSEONLY = 0x2;
1999        const CMD_PARSE_NOALIAS = 0x4;
2000        const CMD_PARSE_VERBOSE = 0x8;
2001        const CMD_PARSE_ONEGROUP = 0x10;
2002    }
2003}
2004
2005#[repr(transparent)]
2006struct AtomicCmdParseInputFlags(std::sync::atomic::AtomicI32);
2007impl From<cmd_parse_input_flags> for AtomicCmdParseInputFlags {
2008    fn from(value: cmd_parse_input_flags) -> Self {
2009        Self(std::sync::atomic::AtomicI32::new(value.bits()))
2010    }
2011}
2012impl AtomicCmdParseInputFlags {
2013    fn intersects(&self, rhs: cmd_parse_input_flags) -> bool {
2014        cmd_parse_input_flags::from_bits(self.0.load(std::sync::atomic::Ordering::SeqCst))
2015            .unwrap()
2016            .intersects(rhs)
2017    }
2018}
2019impl std::ops::BitOrAssign<cmd_parse_input_flags> for &AtomicCmdParseInputFlags {
2020    fn bitor_assign(&mut self, rhs: cmd_parse_input_flags) {
2021        self.0
2022            .fetch_or(rhs.bits(), std::sync::atomic::Ordering::SeqCst);
2023    }
2024}
2025impl std::ops::BitAndAssign<cmd_parse_input_flags> for &AtomicCmdParseInputFlags {
2026    fn bitand_assign(&mut self, rhs: cmd_parse_input_flags) {
2027        self.0
2028            .fetch_and(rhs.bits(), std::sync::atomic::Ordering::SeqCst);
2029    }
2030}
2031
2032#[repr(C)]
2033struct cmd_parse_input<'a> {
2034    flags: AtomicCmdParseInputFlags,
2035
2036    file: Option<&'a str>,
2037    line: AtomicU32, // work around borrow checker
2038
2039    item: *mut cmdq_item,
2040    c: *mut client,
2041    fs: cmd_find_state,
2042}
2043
2044bitflags::bitflags! {
2045    /// Command queue flags.
2046    #[repr(transparent)]
2047    #[derive(Copy, Clone, Eq, PartialEq)]
2048    struct cmdq_state_flags: i32 {
2049        const CMDQ_STATE_REPEAT = 0x1;
2050        const CMDQ_STATE_CONTROL = 0x2;
2051        const CMDQ_STATE_NOHOOKS = 0x4;
2052    }
2053}
2054
2055// Command queue callback.
2056type cmdq_cb = Option<unsafe fn(*mut cmdq_item, *mut c_void) -> cmd_retval>;
2057
2058// Command definition flag.
2059#[repr(C)]
2060#[derive(Copy, Clone, Default)]
2061struct cmd_entry_flag {
2062    flag: u8,
2063    type_: cmd_find_type,
2064    flags: cmd_find_flags,
2065}
2066
2067impl cmd_entry_flag {
2068    const fn new(flag: u8, type_: cmd_find_type, flags: cmd_find_flags) -> Self {
2069        Self { flag, type_, flags }
2070    }
2071
2072    const fn zeroed() -> Self {
2073        Self {
2074            flag: b'\0',
2075            type_: cmd_find_type::CMD_FIND_PANE,
2076            flags: cmd_find_flags::empty(),
2077        }
2078    }
2079}
2080
2081bitflags::bitflags! {
2082    #[repr(transparent)]
2083    #[derive(Copy, Clone, Eq, PartialEq)]
2084    struct cmd_flag: i32 {
2085        const CMD_STARTSERVER = 0x1;
2086        const CMD_READONLY = 0x2;
2087        const CMD_AFTERHOOK = 0x4;
2088        const CMD_CLIENT_CFLAG = 0x8;
2089        const CMD_CLIENT_TFLAG = 0x10;
2090        const CMD_CLIENT_CANFAIL = 0x20;
2091    }
2092}
2093
2094// Command definition.
2095#[repr(C)]
2096struct cmd_entry {
2097    name: SyncCharPtr,
2098    alias: SyncCharPtr,
2099
2100    args: args_parse,
2101    usage: SyncCharPtr,
2102
2103    source: cmd_entry_flag,
2104    target: cmd_entry_flag,
2105
2106    flags: cmd_flag,
2107
2108    exec: unsafe fn(*mut cmd, *mut cmdq_item) -> cmd_retval,
2109}
2110
2111// Status line.
2112const STATUS_LINES_LIMIT: usize = 5;
2113#[repr(C)]
2114struct status_line_entry {
2115    expanded: *mut u8,
2116    ranges: style_ranges,
2117}
2118#[repr(C)]
2119struct status_line {
2120    timer: event,
2121
2122    screen: screen,
2123    active: *mut screen,
2124    references: c_int,
2125
2126    style: grid_cell,
2127    entries: [status_line_entry; STATUS_LINES_LIMIT],
2128}
2129
2130enum_try_from!(prompt_type, u32, prompt_type::PROMPT_TYPE_WINDOW_TARGET);
2131/// Prompt type.
2132const PROMPT_NTYPES: u32 = 4;
2133#[repr(u32)]
2134#[derive(Copy, Clone, Default, Eq, PartialEq)]
2135enum prompt_type {
2136    #[default]
2137    PROMPT_TYPE_COMMAND = 0,
2138    PROMPT_TYPE_SEARCH,
2139    PROMPT_TYPE_TARGET,
2140    PROMPT_TYPE_WINDOW_TARGET,
2141    PROMPT_TYPE_INVALID = 0xff,
2142}
2143
2144// File in client.
2145type client_file_cb = Option<unsafe fn(*mut client, *mut u8, i32, i32, *mut evbuffer, *mut c_void)>;
2146#[repr(C)]
2147struct client_file {
2148    c: *mut client,
2149    peer: *mut tmuxpeer,
2150    tree: *mut client_files,
2151
2152    references: i32,
2153    stream: i32,
2154
2155    path: *mut u8,
2156    buffer: *mut evbuffer,
2157    event: *mut bufferevent,
2158
2159    fd: i32,
2160    error: i32,
2161    closed: i32,
2162
2163    cb: client_file_cb,
2164    data: *mut c_void,
2165
2166    entry: rb_entry<client_file>,
2167}
2168type client_files = rb_head<client_file>;
2169RB_GENERATE!(client_files, client_file, entry, discr_entry, file_cmp);
2170
2171// Client window.
2172#[repr(C)]
2173struct client_window {
2174    window: u32,
2175    pane: *mut window_pane,
2176
2177    sx: u32,
2178    sy: u32,
2179
2180    entry: rb_entry<client_window>,
2181}
2182type client_windows = rb_head<client_window>;
2183RB_GENERATE!(
2184    client_windows,
2185    client_window,
2186    entry,
2187    discr_entry,
2188    server_client_window_cmp
2189);
2190
2191// Visible areas not obstructed by overlays.
2192const OVERLAY_MAX_RANGES: usize = 3;
2193#[repr(C)]
2194struct overlay_ranges {
2195    px: [u32; OVERLAY_MAX_RANGES],
2196    nx: [u32; OVERLAY_MAX_RANGES],
2197}
2198
2199type prompt_input_cb = Option<unsafe fn(*mut client, NonNull<c_void>, *const u8, i32) -> i32>;
2200type prompt_free_cb = Option<unsafe fn(NonNull<c_void>)>;
2201
2202type overlay_check_cb =
2203    Option<unsafe fn(*mut client, *mut c_void, u32, u32, u32, *mut overlay_ranges)>;
2204type overlay_mode_cb =
2205    Option<unsafe fn(*mut client, *mut c_void, *mut u32, *mut u32) -> *mut screen>;
2206type overlay_draw_cb = Option<unsafe fn(*mut client, *mut c_void, *mut screen_redraw_ctx)>;
2207type overlay_key_cb = Option<unsafe fn(*mut client, *mut c_void, *mut key_event) -> i32>;
2208type overlay_free_cb = Option<unsafe fn(*mut client, *mut c_void)>;
2209type overlay_resize_cb = Option<unsafe fn(*mut client, *mut c_void)>;
2210
2211bitflags::bitflags! {
2212    #[repr(transparent)]
2213    #[derive(Copy, Clone, Eq, PartialEq)]
2214    struct client_flag: u64 {
2215        const TERMINAL           = 0x0000000001u64;
2216        const LOGIN              = 0x0000000002u64;
2217        const EXIT               = 0x0000000004u64;
2218        const REDRAWWINDOW       = 0x0000000008u64;
2219        const REDRAWSTATUS       = 0x0000000010u64;
2220        const REPEAT             = 0x0000000020u64;
2221        const SUSPENDED          = 0x0000000040u64;
2222        const ATTACHED           = 0x0000000080u64;
2223        const EXITED             = 0x0000000100u64;
2224        const DEAD               = 0x0000000200u64;
2225        const REDRAWBORDERS      = 0x0000000400u64;
2226        const READONLY           = 0x0000000800u64;
2227        const NOSTARTSERVER      = 0x0000001000u64;
2228        const CONTROL            = 0x0000002000u64;
2229        const CONTROLCONTROL     = 0x0000004000u64;
2230        const FOCUSED            = 0x0000008000u64;
2231        const UTF8               = 0x0000010000u64;
2232        const IGNORESIZE         = 0x0000020000u64;
2233        const IDENTIFIED         = 0x0000040000u64;
2234        const STATUSFORCE        = 0x0000080000u64;
2235        const DOUBLECLICK        = 0x0000100000u64;
2236        const TRIPLECLICK        = 0x0000200000u64;
2237        const SIZECHANGED        = 0x0000400000u64;
2238        const STATUSOFF          = 0x0000800000u64;
2239        const REDRAWSTATUSALWAYS = 0x0001000000u64;
2240        const REDRAWOVERLAY      = 0x0002000000u64;
2241        const CONTROL_NOOUTPUT   = 0x0004000000u64;
2242        const DEFAULTSOCKET      = 0x0008000000u64;
2243        const STARTSERVER        = 0x0010000000u64;
2244        const REDRAWPANES        = 0x0020000000u64;
2245        const NOFORK             = 0x0040000000u64;
2246        const ACTIVEPANE         = 0x0080000000u64;
2247        const CONTROL_PAUSEAFTER = 0x0100000000u64;
2248        const CONTROL_WAITEXIT   = 0x0200000000u64;
2249        const WINDOWSIZECHANGED  = 0x0400000000u64;
2250        const CLIPBOARDBUFFER    = 0x0800000000u64;
2251        const BRACKETPASTING     = 0x1000000000u64;
2252    }
2253}
2254
2255const CLIENT_ALLREDRAWFLAGS: client_flag = client_flag::REDRAWWINDOW
2256    .union(client_flag::REDRAWSTATUS)
2257    .union(client_flag::REDRAWSTATUSALWAYS)
2258    .union(client_flag::REDRAWBORDERS)
2259    .union(client_flag::REDRAWOVERLAY)
2260    .union(client_flag::REDRAWPANES);
2261const CLIENT_UNATTACHEDFLAGS: client_flag = client_flag::DEAD
2262    .union(client_flag::SUSPENDED)
2263    .union(client_flag::EXIT);
2264const CLIENT_NODETACHFLAGS: client_flag = client_flag::DEAD.union(client_flag::EXIT);
2265const CLIENT_NOSIZEFLAGS: client_flag = client_flag::DEAD
2266    .union(client_flag::SUSPENDED)
2267    .union(client_flag::EXIT);
2268
2269const PROMPT_SINGLE: i32 = 0x1;
2270const PROMPT_NUMERIC: i32 = 0x2;
2271const PROMPT_INCREMENTAL: i32 = 0x4;
2272const PROMPT_NOFORMAT: i32 = 0x8;
2273const PROMPT_KEY: i32 = 0x8;
2274
2275impl_tailq_entry!(client, entry, tailq_entry<client>);
2276#[repr(C)]
2277struct client {
2278    name: *const u8,
2279    peer: *mut tmuxpeer,
2280    queue: *mut cmdq_list,
2281
2282    windows: client_windows,
2283
2284    control_state: *mut control_state,
2285    pause_age: c_uint,
2286
2287    pid: pid_t,
2288    fd: c_int,
2289    out_fd: c_int,
2290    event: event,
2291    retval: c_int,
2292
2293    creation_time: timeval,
2294    activity_time: timeval,
2295
2296    environ: *mut environ,
2297    jobs: *mut format_job_tree,
2298
2299    title: *mut u8,
2300    path: *mut u8,
2301    cwd: *const u8,
2302
2303    term_name: *mut u8,
2304    term_features: c_int,
2305    term_type: *mut u8,
2306    term_caps: *mut *mut u8,
2307    term_ncaps: c_uint,
2308
2309    ttyname: *mut u8,
2310    tty: tty,
2311
2312    written: usize,
2313    discarded: usize,
2314    redraw: usize,
2315
2316    repeat_timer: event,
2317
2318    click_timer: event,
2319    click_button: c_uint,
2320    click_event: mouse_event,
2321
2322    status: status_line,
2323
2324    flags: client_flag,
2325
2326    exit_type: exit_type,
2327    exit_msgtype: msgtype,
2328    exit_session: *mut u8,
2329    exit_message: *mut u8,
2330
2331    keytable: *mut key_table,
2332
2333    redraw_panes: u64,
2334
2335    message_ignore_keys: c_int,
2336    message_ignore_styles: c_int,
2337    message_string: *mut u8,
2338    message_timer: event,
2339
2340    prompt_string: *mut u8,
2341    prompt_buffer: *mut utf8_data,
2342    prompt_last: *mut u8,
2343    prompt_index: usize,
2344    prompt_inputcb: prompt_input_cb,
2345    prompt_freecb: prompt_free_cb,
2346    prompt_data: *mut c_void,
2347    prompt_hindex: [c_uint; 4],
2348    prompt_mode: prompt_mode,
2349    prompt_saved: *mut utf8_data,
2350
2351    prompt_flags: c_int,
2352    prompt_type: prompt_type,
2353    prompt_cursor: c_int,
2354
2355    session: *mut session,
2356    last_session: *mut session,
2357
2358    references: c_int,
2359
2360    pan_window: *mut c_void,
2361    pan_ox: c_uint,
2362    pan_oy: c_uint,
2363
2364    overlay_check: overlay_check_cb,
2365    overlay_mode: overlay_mode_cb,
2366    overlay_draw: overlay_draw_cb,
2367    overlay_key: overlay_key_cb,
2368    overlay_free: overlay_free_cb,
2369    overlay_resize: overlay_resize_cb,
2370    overlay_data: *mut c_void,
2371    overlay_timer: event,
2372
2373    files: client_files,
2374
2375    clipboard_panes: *mut c_uint,
2376    clipboard_npanes: c_uint,
2377
2378    // #[entry]
2379    entry: tailq_entry<client>,
2380}
2381type clients = tailq_head<client>;
2382
2383/// Control mode subscription type.
2384#[repr(i32)]
2385enum control_sub_type {
2386    CONTROL_SUB_SESSION,
2387    CONTROL_SUB_PANE,
2388    CONTROL_SUB_ALL_PANES,
2389    CONTROL_SUB_WINDOW,
2390    CONTROL_SUB_ALL_WINDOWS,
2391}
2392
2393const KEY_BINDING_REPEAT: i32 = 0x1;
2394
2395/// Key binding and key table.
2396#[repr(C)]
2397struct key_binding {
2398    key: key_code,
2399    cmdlist: *mut cmd_list,
2400    note: *mut u8,
2401
2402    flags: i32,
2403
2404    entry: rb_entry<key_binding>,
2405}
2406type key_bindings = rb_head<key_binding>;
2407
2408#[repr(C)]
2409struct key_table {
2410    name: *mut u8,
2411    activity_time: timeval,
2412    key_bindings: key_bindings,
2413    default_key_bindings: key_bindings,
2414
2415    references: u32,
2416
2417    entry: rb_entry<key_table>,
2418}
2419type key_tables = rb_head<key_table>;
2420
2421// Option data.
2422type options_array = rb_head<options_array_item>;
2423
2424#[repr(C)]
2425#[derive(Copy, Clone)]
2426union options_value {
2427    string: *mut u8,
2428    number: c_longlong,
2429    style: style,
2430    array: options_array,
2431    cmdlist: *mut cmd_list,
2432}
2433
2434// Option table entries.
2435#[repr(i32)]
2436#[derive(Clone, Copy, PartialEq, Eq)]
2437enum options_table_type {
2438    OPTIONS_TABLE_STRING,
2439    OPTIONS_TABLE_NUMBER,
2440    OPTIONS_TABLE_KEY,
2441    OPTIONS_TABLE_COLOUR,
2442    OPTIONS_TABLE_FLAG,
2443    OPTIONS_TABLE_CHOICE,
2444    OPTIONS_TABLE_COMMAND,
2445}
2446
2447const OPTIONS_TABLE_NONE: i32 = 0;
2448const OPTIONS_TABLE_SERVER: i32 = 0x1;
2449const OPTIONS_TABLE_SESSION: i32 = 0x2;
2450const OPTIONS_TABLE_WINDOW: i32 = 0x4;
2451const OPTIONS_TABLE_PANE: i32 = 0x8;
2452
2453const OPTIONS_TABLE_IS_ARRAY: i32 = 0x1;
2454const OPTIONS_TABLE_IS_HOOK: i32 = 0x2;
2455const OPTIONS_TABLE_IS_STYLE: i32 = 0x4;
2456
2457#[repr(C)]
2458struct options_table_entry {
2459    name: *const u8,
2460    alternative_name: *mut u8,
2461    type_: options_table_type,
2462    scope: i32,
2463    flags: i32,
2464    minimum: u32,
2465    maximum: u32,
2466
2467    choices: *const *const u8,
2468
2469    default_str: Option<&'static str>,
2470    default_num: c_longlong,
2471    default_arr: *const *const u8,
2472
2473    separator: *const u8,
2474    pattern: *const u8,
2475
2476    text: *const u8,
2477    unit: *const u8,
2478}
2479
2480#[repr(C)]
2481struct options_name_map_str {
2482    from: &'static str,
2483    to: &'static str,
2484}
2485impl options_name_map_str {
2486    const fn new(from: &'static str, to: &'static str) -> Self {
2487        Self { from, to }
2488    }
2489}
2490
2491#[repr(C)]
2492struct options_name_map {
2493    from: *const u8,
2494    to: *const u8,
2495}
2496impl options_name_map {
2497    const fn new(from: *const u8, to: *const u8) -> Self {
2498        Self { from, to }
2499    }
2500}
2501
2502// Common command usages.
2503const CMD_TARGET_PANE_USAGE: &CStr = c"[-t target-pane]";
2504const CMD_TARGET_WINDOW_USAGE: &CStr = c"[-t target-window]";
2505// const CMD_TARGET_SESSION_USAGE: &CStr = c"[-t target-session]";
2506// const CMD_TARGET_CLIENT_USAGE: &CStr = c"[-t target-client]";
2507// const CMD_SRCDST_PANE_USAGE: &CStr = c"[-s src-pane] [-t dst-pane]";
2508// const CMD_SRCDST_WINDOW_USAGE: &CStr = c"[-s src-window] [-t dst-window]";
2509// const CMD_SRCDST_SESSION_USAGE: &CStr = c"[-s src-session] [-t dst-session]";
2510// const CMD_SRCDST_CLIENT_USAGE: &CStr = c"[-s src-client] [-t dst-client]";
2511const CMD_BUFFER_USAGE: &CStr = c"[-b buffer-name]";
2512
2513bitflags::bitflags! {
2514    #[repr(transparent)]
2515    #[derive(Copy, Clone, Eq, PartialEq)]
2516    struct spawn_flags: i32 {
2517        const SPAWN_KILL = 0x1;
2518        const SPAWN_DETACHED = 0x2;
2519        const SPAWN_RESPAWN = 0x4;
2520        const SPAWN_BEFORE = 0x8;
2521        const SPAWN_NONOTIFY = 0x10;
2522        const SPAWN_FULLSIZE = 0x20;
2523        const SPAWN_EMPTY = 0x40;
2524        const SPAWN_ZOOM = 0x80;
2525    }
2526}
2527
2528// TODO inline these and remove the definitions
2529const SPAWN_KILL: spawn_flags = spawn_flags::SPAWN_KILL;
2530const SPAWN_DETACHED: spawn_flags = spawn_flags::SPAWN_DETACHED;
2531const SPAWN_RESPAWN: spawn_flags = spawn_flags::SPAWN_RESPAWN;
2532const SPAWN_BEFORE: spawn_flags = spawn_flags::SPAWN_BEFORE;
2533const SPAWN_NONOTIFY: spawn_flags = spawn_flags::SPAWN_NONOTIFY;
2534const SPAWN_FULLSIZE: spawn_flags = spawn_flags::SPAWN_FULLSIZE;
2535const SPAWN_EMPTY: spawn_flags = spawn_flags::SPAWN_EMPTY;
2536const SPAWN_ZOOM: spawn_flags = spawn_flags::SPAWN_ZOOM;
2537
2538/// Spawn common context.
2539#[repr(C)]
2540struct spawn_context {
2541    item: *mut cmdq_item,
2542
2543    s: *mut session,
2544    wl: *mut winlink,
2545    tc: *mut client,
2546
2547    wp0: *mut window_pane,
2548    lc: *mut layout_cell,
2549
2550    name: *const u8,
2551    argv: *mut *mut u8,
2552    argc: i32,
2553    environ: *mut environ,
2554
2555    idx: i32,
2556    cwd: *const u8,
2557
2558    flags: spawn_flags,
2559}
2560
2561/// Mode tree sort order.
2562#[repr(C)]
2563struct mode_tree_sort_criteria {
2564    field: u32,
2565    reversed: bool,
2566}
2567
2568const WINDOW_MINIMUM: u32 = PANE_MINIMUM;
2569const WINDOW_MAXIMUM: u32 = 10_000;
2570
2571#[repr(i32)]
2572enum exit_type {
2573    #[expect(dead_code)]
2574    CLIENT_EXIT_RETURN,
2575    CLIENT_EXIT_SHUTDOWN,
2576    CLIENT_EXIT_DETACH,
2577}
2578
2579#[repr(i32)]
2580#[derive(Copy, Clone, Eq, PartialEq)]
2581enum prompt_mode {
2582    PROMPT_ENTRY,
2583    PROMPT_COMMAND,
2584}
2585
2586mod tmux;
2587
2588pub use crate::tmux::tmux_main;
2589use crate::tmux::{
2590    GLOBAL_ENVIRON, GLOBAL_OPTIONS, GLOBAL_S_OPTIONS, GLOBAL_W_OPTIONS, PTM_FD, SHELL_COMMAND,
2591    SOCKET_PATH, START_TIME, checkshell_, find_cwd, find_home, get_timer, getversion, setblocking,
2592    shell_argv0,
2593};
2594
2595mod proc;
2596use crate::proc::{
2597    proc_add_peer, proc_clear_signals, proc_exit, proc_flush_peer, proc_fork_and_daemon,
2598    proc_get_peer_uid, proc_kill_peer, proc_loop, proc_remove_peer, proc_send, proc_set_signals,
2599    proc_start, proc_toggle_log, tmuxpeer, tmuxproc,
2600};
2601
2602mod cfg_;
2603use crate::cfg_::{
2604    CFG_CLIENT, CFG_FILES, CFG_FINISHED, CFG_QUIET, cfg_print_causes, cfg_show_causes,
2605    load_cfg_from_buffer, start_cfg,
2606};
2607
2608mod paste;
2609use crate::paste::{
2610    paste_add, paste_buffer, paste_buffer_created, paste_buffer_data, paste_buffer_data_,
2611    paste_buffer_name, paste_buffer_order, paste_free, paste_get_name, paste_get_top,
2612    paste_is_empty, paste_make_sample, paste_rename, paste_replace, paste_set, paste_walk,
2613};
2614
2615mod format;
2616use crate::format::format_add;
2617use crate::format::{
2618    FORMAT_NONE, FORMAT_PANE, FORMAT_WINDOW, format_add_cb, format_add_tv, format_create,
2619    format_create_defaults, format_create_from_state, format_create_from_target, format_defaults,
2620    format_defaults_pane, format_defaults_paste_buffer, format_defaults_window, format_each,
2621    format_expand, format_expand_time, format_flags, format_free, format_get_pane,
2622    format_grid_hyperlink, format_grid_line, format_grid_word, format_job_tree, format_log_debug,
2623    format_lost_client, format_merge, format_pretty_time, format_single, format_single_from_state,
2624    format_single_from_target, format_skip, format_tidy_jobs, format_tree, format_true,
2625};
2626
2627mod format_draw_;
2628use crate::format_draw_::{format_draw, format_trim_left, format_trim_right, format_width};
2629
2630mod notify;
2631use crate::notify::{
2632    notify_client, notify_hook, notify_pane, notify_paste_buffer, notify_session,
2633    notify_session_window, notify_window, notify_winlink,
2634};
2635
2636mod options_;
2637use crate::options_::options_set_string;
2638use crate::options_::{
2639    options, options_array_assign, options_array_clear, options_array_first, options_array_get,
2640    options_array_item, options_array_item_index, options_array_item_value, options_array_next,
2641    options_array_set, options_create, options_default, options_default_to_string, options_empty,
2642    options_entry, options_first, options_free, options_from_string, options_get, options_get_,
2643    options_get_number, options_get_number_, options_get_only, options_get_parent,
2644    options_get_string, options_get_string_, options_is_array, options_is_string, options_match,
2645    options_name, options_next, options_owner, options_parse_get, options_push_changes,
2646    options_remove_or_default, options_scope_from_flags, options_scope_from_name,
2647    options_set_number, options_set_parent, options_string_to_style, options_table_entry,
2648    options_to_string,
2649};
2650
2651mod options_table;
2652use crate::options_table::{OPTIONS_OTHER_NAMES, OPTIONS_TABLE};
2653
2654bitflags::bitflags! {
2655    #[repr(transparent)]
2656    #[derive(Copy, Clone, Eq, PartialEq)]
2657    struct job_flag: i32 {
2658        const JOB_NOWAIT = 1;
2659        const JOB_KEEPWRITE = 2;
2660        const JOB_PTY = 4;
2661        const JOB_DEFAULTSHELL = 8;
2662    }
2663}
2664mod job_;
2665use crate::job_::{
2666    job, job_check_died, job_free, job_get_data, job_get_event, job_get_status, job_kill_all,
2667    job_print_summary, job_resize, job_run, job_still_running, job_transfer,
2668};
2669
2670mod environ_;
2671use crate::environ_::{
2672    environ, environ_clear, environ_copy, environ_create, environ_find, environ_first,
2673    environ_for_session, environ_free, environ_next, environ_push, environ_put, environ_unset,
2674    environ_update,
2675};
2676use crate::environ_::{environ_log, environ_set};
2677
2678mod tty_;
2679use crate::tty_::{
2680    tty_attributes, tty_cell, tty_clipboard_query, tty_close, tty_cmd_alignmenttest, tty_cmd_cell,
2681    tty_cmd_cells, tty_cmd_clearcharacter, tty_cmd_clearendofscreen, tty_cmd_clearscreen,
2682    tty_cmd_clearstartofscreen, tty_cmd_deletecharacter, tty_cmd_deleteline,
2683    tty_cmd_insertcharacter, tty_cmd_insertline, tty_cmd_rawstring, tty_cmd_reverseindex,
2684    tty_cmd_scrolldown, tty_cmd_scrollup, tty_cmd_setselection, tty_cmd_syncstart, tty_create_log,
2685    tty_cursor, tty_default_colours, tty_draw_line, tty_free, tty_init, tty_margin_off, tty_open,
2686    tty_putc, tty_putcode, tty_putn, tty_puts, tty_raw, tty_region_off, tty_repeat_requests,
2687    tty_reset, tty_resize, tty_send_requests, tty_set_path, tty_set_selection, tty_set_size,
2688    tty_set_title, tty_start_tty, tty_stop_tty, tty_sync_end, tty_sync_start,
2689    tty_update_client_offset, tty_update_features, tty_update_mode, tty_update_window_offset,
2690    tty_window_bigger, tty_window_offset, tty_write,
2691};
2692
2693mod tty_term_;
2694use crate::tty_term_::{
2695    TTY_TERMS, tty_code, tty_term_apply, tty_term_apply_overrides, tty_term_create,
2696    tty_term_describe, tty_term_flag, tty_term_free, tty_term_free_list, tty_term_has,
2697    tty_term_ncodes, tty_term_number, tty_term_read_list, tty_term_string, tty_term_string_i,
2698    tty_term_string_ii, tty_term_string_iii, tty_term_string_s, tty_term_string_ss,
2699};
2700
2701mod tty_features;
2702use crate::tty_features::{
2703    tty_add_features, tty_apply_features, tty_default_features, tty_get_features,
2704};
2705
2706mod tty_acs;
2707use crate::tty_acs::{
2708    tty_acs_double_borders, tty_acs_get, tty_acs_heavy_borders, tty_acs_needed,
2709    tty_acs_reverse_get, tty_acs_rounded_borders,
2710};
2711
2712mod tty_keys;
2713use crate::tty_keys::{tty_key, tty_keys_build, tty_keys_colours, tty_keys_free, tty_keys_next};
2714
2715mod arguments;
2716
2717// unsafe fn args_get(_: *mut args, _: c_uchar) -> *const c_char;
2718unsafe fn args_get_(args: *mut args, flag: char) -> *const u8 {
2719    debug_assert!(flag.is_ascii());
2720    unsafe { args_get(args, flag as u8) }
2721}
2722
2723use crate::arguments::{
2724    args, args_command_state, args_copy, args_count, args_create, args_entry, args_escape,
2725    args_first, args_first_value, args_free, args_free_value, args_free_values, args_from_vector,
2726    args_get, args_has, args_has_count, args_make_commands, args_make_commands_free,
2727    args_make_commands_get_command, args_make_commands_now, args_make_commands_prepare, args_next,
2728    args_next_value, args_parse, args_percentage, args_percentage_and_expand, args_print, args_set,
2729    args_string, args_string_percentage, args_strtonum, args_strtonum_and_expand, args_to_vector,
2730    args_value, args_values,
2731};
2732
2733mod cmd_;
2734use crate::cmd_::cmd_attach_session::cmd_attach_session;
2735use crate::cmd_::cmd_find::{
2736    cmd_find_best_client, cmd_find_clear_state, cmd_find_client, cmd_find_copy_state,
2737    cmd_find_empty_state, cmd_find_from_client, cmd_find_from_mouse, cmd_find_from_nothing,
2738    cmd_find_from_pane, cmd_find_from_session, cmd_find_from_session_window, cmd_find_from_window,
2739    cmd_find_from_winlink, cmd_find_from_winlink_pane, cmd_find_target, cmd_find_valid_state,
2740};
2741use crate::cmd_::cmd_log_argv;
2742use crate::cmd_::{
2743    CMD_TABLE, cmd, cmd_append_argv, cmd_copy_argv, cmd_free_argv, cmd_get_alias, cmd_get_args,
2744    cmd_get_entry, cmd_get_group, cmd_get_source, cmd_list_all_have, cmd_list_any_have,
2745    cmd_list_append, cmd_list_append_all, cmd_list_copy, cmd_list_first, cmd_list_free,
2746    cmd_list_move, cmd_list_new, cmd_list_next, cmd_list_print, cmd_mouse_at, cmd_mouse_pane,
2747    cmd_mouse_window, cmd_pack_argv, cmd_parse, cmd_print, cmd_stringify_argv,
2748    cmd_template_replace, cmd_unpack_argv, cmds,
2749};
2750
2751mod cmd_parse;
2752use crate::cmd_::cmd_queue::{
2753    cmdq_add_format, cmdq_add_formats, cmdq_append, cmdq_continue, cmdq_copy_state, cmdq_error,
2754    cmdq_free, cmdq_free_state, cmdq_get_callback, cmdq_get_client, cmdq_get_command,
2755    cmdq_get_current, cmdq_get_error, cmdq_get_event, cmdq_get_flags, cmdq_get_name,
2756    cmdq_get_source, cmdq_get_state, cmdq_get_target, cmdq_get_target_client, cmdq_guard,
2757    cmdq_insert_after, cmdq_insert_hook, cmdq_item, cmdq_list, cmdq_merge_formats, cmdq_new,
2758    cmdq_new_state, cmdq_next, cmdq_print, cmdq_print_data, cmdq_running, cmdq_state,
2759};
2760use crate::cmd_::cmd_wait_for::cmd_wait_for_flush;
2761use crate::cmd_parse::{
2762    cmd_parse_and_append, cmd_parse_from_arguments, cmd_parse_from_buffer, cmd_parse_from_file,
2763    cmd_parse_from_string, cmd_parse_state, *,
2764};
2765
2766mod client_;
2767use crate::client_::client_main;
2768
2769mod key_bindings_;
2770use crate::key_bindings_::{
2771    key_bindings_add, key_bindings_dispatch, key_bindings_first, key_bindings_first_table,
2772    key_bindings_get, key_bindings_get_default, key_bindings_get_table, key_bindings_init,
2773    key_bindings_next, key_bindings_next_table, key_bindings_remove, key_bindings_remove_table,
2774    key_bindings_reset, key_bindings_unref_table,
2775};
2776
2777mod key_string;
2778use crate::key_string::{key_string_lookup_key, key_string_lookup_string};
2779
2780mod alerts;
2781use crate::alerts::{alerts_check_session, alerts_queue, alerts_reset_all};
2782
2783mod file;
2784use crate::file::{
2785    file_can_print, file_cancel, file_cmp, file_error, file_fire_done, file_print,
2786    file_print_buffer, file_read, file_read_cancel, file_read_data, file_read_done, file_read_open,
2787    file_write, file_write_close, file_write_data, file_write_left, file_write_open,
2788    file_write_ready,
2789};
2790
2791mod server;
2792use crate::server::{
2793    CLIENTS, CURRENT_TIME, MARKED_PANE, SERVER_PROC, server_add_accept, server_add_message,
2794    server_check_marked, server_clear_marked, server_is_marked, server_set_marked, server_start,
2795    server_update_socket,
2796};
2797
2798mod server_client;
2799use crate::server_client::{
2800    server_client_add_client_window, server_client_check_nested, server_client_clear_overlay,
2801    server_client_create, server_client_detach, server_client_exec,
2802    server_client_get_client_window, server_client_get_cwd, server_client_get_flags,
2803    server_client_get_key_table, server_client_get_pane, server_client_handle_key,
2804    server_client_how_many, server_client_loop, server_client_lost, server_client_open,
2805    server_client_overlay_range, server_client_print, server_client_remove_pane,
2806    server_client_set_flags, server_client_set_key_table, server_client_set_overlay,
2807    server_client_set_pane, server_client_set_session, server_client_suspend, server_client_unref,
2808    server_client_window_cmp,
2809};
2810
2811mod server_fn;
2812use crate::server_fn::{
2813    server_check_unattached, server_destroy_pane, server_destroy_session, server_kill_pane,
2814    server_kill_window, server_link_window, server_lock, server_lock_client, server_lock_session,
2815    server_redraw_client, server_redraw_session, server_redraw_session_group, server_redraw_window,
2816    server_redraw_window_borders, server_renumber_all, server_status_client, server_status_session,
2817    server_status_session_group, server_status_window, server_unlink_window, server_unzoom_window,
2818};
2819
2820mod status;
2821use crate::status::{
2822    STATUS_PROMPT_HLIST, STATUS_PROMPT_HSIZE, status_at_line, status_free, status_get_range,
2823    status_init, status_line_size, status_message_clear, status_message_redraw, status_message_set,
2824    status_prompt_clear, status_prompt_key, status_prompt_load_history, status_prompt_redraw,
2825    status_prompt_save_history, status_prompt_set, status_prompt_type, status_prompt_type_string,
2826    status_prompt_update, status_redraw, status_timer_start, status_timer_start_all,
2827    status_update_cache,
2828};
2829
2830mod resize;
2831use crate::resize::{
2832    default_window_size, recalculate_size, recalculate_sizes, recalculate_sizes_now, resize_window,
2833};
2834
2835mod input;
2836use crate::input::{
2837    input_ctx, input_free, input_init, input_parse_buffer, input_parse_pane, input_parse_screen,
2838    input_pending, input_reply_clipboard, input_reset,
2839};
2840
2841mod input_keys;
2842use crate::input_keys::{input_key, input_key_build, input_key_get_mouse, input_key_pane};
2843
2844mod colour;
2845use crate::colour::{
2846    colour_256to16, colour_find_rgb, colour_force_rgb, colour_fromstring, colour_fromstring_,
2847    colour_join_rgb, colour_palette_clear, colour_palette_free, colour_palette_from_option,
2848    colour_palette_get, colour_palette_init, colour_palette_set, colour_parse_x11,
2849    colour_split_rgb, colour_tostring,
2850};
2851
2852mod attributes;
2853use crate::attributes::{attributes_fromstring, attributes_tostring};
2854
2855mod grid_;
2856use crate::grid_::{
2857    GRID_DEFAULT_CELL, grid_adjust_lines, grid_cells_equal, grid_cells_look_equal, grid_clear,
2858    grid_clear_history, grid_clear_lines, grid_collect_history, grid_compare, grid_create,
2859    grid_destroy, grid_duplicate_lines, grid_empty_line, grid_get_cell, grid_get_line,
2860    grid_line_length, grid_move_cells, grid_move_lines, grid_peek_line, grid_reflow,
2861    grid_remove_history, grid_scroll_history, grid_scroll_history_region, grid_set_cell,
2862    grid_set_cells, grid_set_padding, grid_string_cells, grid_unwrap_position, grid_wrap_position,
2863};
2864
2865mod grid_reader_;
2866use crate::grid_reader_::{
2867    grid_reader_cursor_back_to_indentation, grid_reader_cursor_end_of_line,
2868    grid_reader_cursor_jump, grid_reader_cursor_jump_back, grid_reader_cursor_left,
2869    grid_reader_cursor_next_word, grid_reader_cursor_next_word_end,
2870    grid_reader_cursor_previous_word, grid_reader_cursor_right, grid_reader_cursor_start_of_line,
2871    grid_reader_get_cursor, grid_reader_in_set, grid_reader_start,
2872};
2873
2874mod grid_view;
2875use crate::grid_view::{
2876    grid_view_clear, grid_view_clear_history, grid_view_delete_cells, grid_view_delete_lines,
2877    grid_view_delete_lines_region, grid_view_get_cell, grid_view_insert_cells,
2878    grid_view_insert_lines, grid_view_insert_lines_region, grid_view_scroll_region_down,
2879    grid_view_scroll_region_up, grid_view_set_cell, grid_view_set_cells, grid_view_set_padding,
2880    grid_view_string_cells,
2881};
2882
2883mod screen_write;
2884use crate::screen_write::{
2885    screen_write_alignmenttest, screen_write_alternateoff, screen_write_alternateon,
2886    screen_write_backspace, screen_write_box, screen_write_carriagereturn, screen_write_cell,
2887    screen_write_citem, screen_write_clearcharacter, screen_write_clearendofline,
2888    screen_write_clearendofscreen, screen_write_clearhistory, screen_write_clearline,
2889    screen_write_clearscreen, screen_write_clearstartofline, screen_write_clearstartofscreen,
2890    screen_write_cline, screen_write_collect_add, screen_write_collect_end,
2891    screen_write_cursordown, screen_write_cursorleft, screen_write_cursormove,
2892    screen_write_cursorright, screen_write_cursorup, screen_write_deletecharacter,
2893    screen_write_deleteline, screen_write_fast_copy, screen_write_free_list,
2894    screen_write_fullredraw, screen_write_hline, screen_write_insertcharacter,
2895    screen_write_insertline, screen_write_linefeed, screen_write_make_list, screen_write_menu,
2896    screen_write_mode_clear, screen_write_mode_set, screen_write_preview, screen_write_putc,
2897    screen_write_rawstring, screen_write_reset, screen_write_reverseindex, screen_write_scrolldown,
2898    screen_write_scrollregion, screen_write_scrollup, screen_write_setselection,
2899    screen_write_start, screen_write_start_callback, screen_write_start_pane, screen_write_stop,
2900    screen_write_vline,
2901};
2902use crate::screen_write::{
2903    screen_write_nputs, screen_write_puts, screen_write_strlen, screen_write_text,
2904    screen_write_vnputs, screen_write_vnputs_,
2905};
2906
2907mod screen_redraw;
2908use crate::screen_redraw::{screen_redraw_pane, screen_redraw_screen};
2909
2910mod screen_;
2911use crate::screen_::{
2912    screen_alternate_off, screen_alternate_on, screen_check_selection, screen_clear_selection,
2913    screen_free, screen_hide_selection, screen_init, screen_mode_to_string, screen_pop_title,
2914    screen_push_title, screen_reinit, screen_reset_hyperlinks, screen_reset_tabs, screen_resize,
2915    screen_resize_cursor, screen_sel, screen_select_cell, screen_set_cursor_colour,
2916    screen_set_cursor_style, screen_set_path, screen_set_selection, screen_set_title,
2917    screen_titles,
2918};
2919
2920mod window_;
2921use crate::window_::{
2922    ALL_WINDOW_PANES, WINDOWS, window_add_pane, window_add_ref, window_count_panes, window_create,
2923    window_destroy_panes, window_find_by_id, window_find_by_id_str, window_find_string,
2924    window_get_active_at, window_has_pane, window_lost_pane, window_pane_at_index,
2925    window_pane_default_cursor, window_pane_destroy_ready, window_pane_exited,
2926    window_pane_find_by_id, window_pane_find_by_id_str, window_pane_find_down,
2927    window_pane_find_left, window_pane_find_right, window_pane_find_up, window_pane_get_new_data,
2928    window_pane_index, window_pane_key, window_pane_mode, window_pane_next_by_number,
2929    window_pane_previous_by_number, window_pane_reset_mode, window_pane_reset_mode_all,
2930    window_pane_resize, window_pane_search, window_pane_send_resize, window_pane_set_event,
2931    window_pane_set_mode, window_pane_stack_remove, window_pane_start_input,
2932    window_pane_update_used_data, window_pane_visible, window_pop_zoom, window_printable_flags,
2933    window_push_zoom, window_redraw_active_switch, window_remove_pane, window_remove_ref,
2934    window_resize, window_set_active_pane, window_set_fill_character, window_set_name,
2935    window_unzoom, window_update_activity, window_update_focus, window_zoom, winlink_add,
2936    winlink_clear_flags, winlink_count, winlink_find_by_index, winlink_find_by_window,
2937    winlink_find_by_window_id, winlink_next, winlink_next_by_number, winlink_previous,
2938    winlink_previous_by_number, winlink_remove, winlink_set_window, winlink_shuffle_up,
2939    winlink_stack_push, winlink_stack_remove,
2940};
2941
2942mod layout;
2943use crate::layout::{
2944    layout_assign_pane, layout_close_pane, layout_count_cells, layout_create_cell,
2945    layout_destroy_cell, layout_fix_offsets, layout_fix_panes, layout_free, layout_free_cell,
2946    layout_init, layout_make_leaf, layout_make_node, layout_print_cell, layout_resize,
2947    layout_resize_adjust, layout_resize_layout, layout_resize_pane, layout_resize_pane_to,
2948    layout_search_by_border, layout_set_size, layout_split_pane, layout_spread_cell,
2949    layout_spread_out,
2950};
2951
2952mod layout_custom;
2953use crate::layout_custom::{layout_dump, layout_parse};
2954
2955mod layout_set;
2956use crate::layout_set::{
2957    layout_set_lookup, layout_set_next, layout_set_previous, layout_set_select,
2958};
2959
2960mod mode_tree;
2961use crate::mode_tree::{
2962    mode_tree_add, mode_tree_build, mode_tree_collapse_current, mode_tree_count_tagged,
2963    mode_tree_data, mode_tree_down, mode_tree_draw, mode_tree_draw_as_parent,
2964    mode_tree_each_tagged, mode_tree_expand, mode_tree_expand_current, mode_tree_free,
2965    mode_tree_get_current, mode_tree_get_current_name, mode_tree_item, mode_tree_key,
2966    mode_tree_no_tag, mode_tree_remove, mode_tree_resize, mode_tree_run_command,
2967    mode_tree_set_current, mode_tree_start, mode_tree_up, mode_tree_zoom,
2968};
2969
2970mod window_buffer;
2971use crate::window_buffer::WINDOW_BUFFER_MODE;
2972
2973mod window_tree;
2974use crate::window_tree::WINDOW_TREE_MODE;
2975
2976mod window_clock;
2977use crate::window_clock::{WINDOW_CLOCK_MODE, WINDOW_CLOCK_TABLE};
2978
2979mod window_client;
2980use crate::window_client::WINDOW_CLIENT_MODE;
2981
2982mod window_copy;
2983use crate::window_copy::window_copy_add;
2984use crate::window_copy::{
2985    WINDOW_COPY_MODE, WINDOW_VIEW_MODE, window_copy_get_line, window_copy_get_word,
2986    window_copy_pagedown, window_copy_pageup, window_copy_start_drag,
2987};
2988
2989mod window_customize;
2990use crate::window_customize::WINDOW_CUSTOMIZE_MODE;
2991
2992mod names;
2993use crate::names::{check_window_name, default_window_name, parse_window_name};
2994
2995mod control;
2996use crate::control::control_write;
2997use crate::control::{
2998    control_add_sub, control_all_done, control_continue_pane, control_discard, control_pane_offset,
2999    control_pause_pane, control_ready, control_remove_sub, control_reset_offsets,
3000    control_set_pane_off, control_set_pane_on, control_start, control_state, control_stop,
3001    control_write_output,
3002};
3003
3004mod control_notify;
3005use crate::control_notify::{
3006    control_notify_client_detached, control_notify_client_session_changed,
3007    control_notify_pane_mode_changed, control_notify_paste_buffer_changed,
3008    control_notify_paste_buffer_deleted, control_notify_session_closed,
3009    control_notify_session_created, control_notify_session_renamed,
3010    control_notify_session_window_changed, control_notify_window_layout_changed,
3011    control_notify_window_linked, control_notify_window_pane_changed,
3012    control_notify_window_renamed, control_notify_window_unlinked,
3013};
3014
3015mod session_;
3016use crate::session_::{
3017    NEXT_SESSION_ID, SESSIONS, session_add_ref, session_alive, session_attach, session_check_name,
3018    session_create, session_destroy, session_detach, session_find, session_find_by_id,
3019    session_find_by_id_str, session_group_add, session_group_attached_count,
3020    session_group_contains, session_group_count, session_group_find, session_group_new,
3021    session_group_synchronize_from, session_group_synchronize_to, session_has, session_is_linked,
3022    session_last, session_next, session_next_session, session_previous, session_previous_session,
3023    session_remove_ref, session_renumber_windows, session_select, session_set_current,
3024    session_update_activity,
3025};
3026
3027mod utf8;
3028use crate::utf8::{
3029    utf8_append, utf8_build_one, utf8_copy, utf8_cstrhas, utf8_cstrwidth, utf8_from_data,
3030    utf8_fromcstr, utf8_fromwc, utf8_in_table, utf8_isvalid, utf8_open, utf8_padcstr,
3031    utf8_rpadcstr, utf8_sanitize, utf8_set, utf8_stravis, utf8_stravisx, utf8_strlen, utf8_strvis,
3032    utf8_strwidth, utf8_to_data, utf8_tocstr, utf8_towc,
3033};
3034
3035mod osdep;
3036use crate::osdep::{osdep_event_init, osdep_get_cwd, osdep_get_name};
3037
3038mod utf8_combined;
3039use crate::utf8_combined::{utf8_has_zwj, utf8_is_modifier, utf8_is_vs, utf8_is_zwj};
3040
3041#[macro_use] // log_debug
3042mod log;
3043use crate::log::{fatal, fatalx, log_add_level, log_close, log_get_level, log_open, log_toggle};
3044use crate::log::{fatalx_, log_debug};
3045
3046bitflags::bitflags! {
3047    #[repr(transparent)]
3048    #[derive(Copy, Clone, Eq, PartialEq)]
3049    struct menu_flags: i32 {
3050        const MENU_NOMOUSE = 0x1;
3051        const MENU_TAB = 0x2;
3052        const MENU_STAYOPEN = 0x4;
3053    }
3054}
3055const MENU_NOMOUSE: menu_flags = menu_flags::MENU_NOMOUSE;
3056const MENU_TAB: menu_flags = menu_flags::MENU_TAB;
3057const MENU_STAYOPEN: menu_flags = menu_flags::MENU_STAYOPEN;
3058
3059mod menu_;
3060use crate::menu_::{
3061    menu_add_item, menu_add_items, menu_check_cb, menu_create, menu_data, menu_display,
3062    menu_draw_cb, menu_free, menu_free_cb, menu_key_cb, menu_mode_cb, menu_prepare,
3063};
3064
3065const POPUP_CLOSEEXIT: i32 = 0x1;
3066const POPUP_CLOSEEXITZERO: i32 = 0x2;
3067const POPUP_INTERNAL: i32 = 0x4;
3068mod popup;
3069use crate::popup::{popup_display, popup_editor};
3070
3071mod style_;
3072use crate::style_::{style_add, style_apply, style_copy, style_parse, style_set, style_tostring};
3073
3074mod spawn;
3075use crate::spawn::{spawn_pane, spawn_window};
3076
3077mod regsub;
3078use crate::regsub::regsub;
3079
3080// TODO
3081// image.c
3082// image-sixel.c
3083
3084mod server_acl;
3085use crate::server_acl::{
3086    server_acl_display, server_acl_get_uid, server_acl_init, server_acl_join,
3087    server_acl_user_allow, server_acl_user_allow_write, server_acl_user_deny,
3088    server_acl_user_deny_write, server_acl_user_find,
3089};
3090
3091mod hyperlinks_;
3092use crate::hyperlinks_::{
3093    hyperlinks, hyperlinks_copy, hyperlinks_free, hyperlinks_get, hyperlinks_init, hyperlinks_put,
3094    hyperlinks_reset,
3095};
3096
3097mod xmalloc;
3098use crate::xmalloc::{
3099    format_nul, xcalloc, xcalloc_, xcalloc1, xmalloc, xmalloc_, xrealloc, xrealloc_, xreallocarray,
3100    xreallocarray_, xrecallocarray, xsnprintf_, xstrdup, xstrdup_, xstrdup__, xstrdup___, xstrndup,
3101};
3102
3103mod tmux_protocol;
3104use crate::tmux_protocol::{
3105    PROTOCOL_VERSION, msg_command, msg_read_cancel, msg_read_data, msg_read_done, msg_read_open,
3106    msg_write_close, msg_write_data, msg_write_open, msg_write_ready, msgtype,
3107};
3108
3109unsafe impl Sync for SyncCharPtr {}
3110#[repr(transparent)]
3111#[derive(Copy, Clone)]
3112struct SyncCharPtr(*const u8);
3113impl SyncCharPtr {
3114    const fn new(value: &'static CStr) -> Self {
3115        Self(value.as_ptr().cast())
3116    }
3117    const fn from_ptr(value: *const u8) -> Self {
3118        Self(value)
3119    }
3120    const fn null() -> Self {
3121        Self(null())
3122    }
3123    const fn as_ptr(&self) -> *const u8 {
3124        self.0
3125    }
3126    const fn is_null(&self) -> bool {
3127        self.0.is_null()
3128    }
3129}
3130
3131unsafe fn _s(ptr: impl ToU8Ptr) -> DisplayCStrPtr {
3132    DisplayCStrPtr(ptr.to_u8_ptr())
3133}
3134trait ToU8Ptr {
3135    fn to_u8_ptr(self) -> *const u8;
3136}
3137impl ToU8Ptr for *mut u8 {
3138    fn to_u8_ptr(self) -> *const u8 {
3139        self.cast()
3140    }
3141}
3142impl ToU8Ptr for *const u8 {
3143    fn to_u8_ptr(self) -> *const u8 {
3144        self
3145    }
3146}
3147impl ToU8Ptr for *mut i8 {
3148    fn to_u8_ptr(self) -> *const u8 {
3149        self.cast()
3150    }
3151}
3152impl ToU8Ptr for *const i8 {
3153    fn to_u8_ptr(self) -> *const u8 {
3154        self.cast()
3155    }
3156}
3157impl ToU8Ptr for SyncCharPtr {
3158    fn to_u8_ptr(self) -> *const u8 {
3159        self.as_ptr()
3160    }
3161}
3162// TODO struct should have some sort of lifetime
3163/// Display wrapper for a *c_char pointer
3164#[repr(transparent)]
3165struct DisplayCStrPtr(*const u8);
3166impl std::fmt::Display for DisplayCStrPtr {
3167    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3168        if self.0.is_null() {
3169            return f.write_str("(null)");
3170        }
3171
3172        // TODO alignment
3173
3174        let len = if let Some(width) = f.precision() {
3175            unsafe { libc::strnlen(self.0, width) }
3176        } else if let Some(width) = f.width() {
3177            unsafe { libc::strnlen(self.0, width) }
3178        } else {
3179            unsafe { libc::strlen(self.0) }
3180        };
3181
3182        let s: &[u8] = unsafe { std::slice::from_raw_parts(self.0, len) };
3183        let s = std::str::from_utf8(s).unwrap_or("%s-invalid-utf8");
3184        f.write_str(s)
3185    }
3186}
3187
3188// TOOD make usable in const context
3189// https://stackoverflow.com/a/63904992
3190macro_rules! function_name {
3191    () => {{
3192        fn f() {}
3193        fn type_name_of<T>(_: T) -> &'static str {
3194            std::any::type_name::<T>()
3195        }
3196        let name = type_name_of(f);
3197
3198        // Find and cut the rest of the path
3199        match &name[..name.len() - 3].rfind(':') {
3200            Some(pos) => &name[pos + 1..name.len() - 3],
3201            None => &name[..name.len() - 3],
3202        }
3203    }};
3204}
3205pub(crate) use function_name;
3206
3207const fn concat_array<const N: usize, const M: usize, const O: usize, T: Copy>(
3208    a1: [T; N],
3209    a2: [T; M],
3210) -> [T; O] {
3211    let mut out: [MaybeUninit<T>; O] = [MaybeUninit::uninit(); O];
3212
3213    let mut i: usize = 0;
3214    while i < a1.len() {
3215        out[i].write(a1[i]);
3216        i += 1;
3217    }
3218    while i < a1.len() + a2.len() {
3219        out[i].write(a2[i - a1.len()]);
3220        i += 1;
3221    }
3222
3223    assert!(a1.len() + a2.len() == out.len());
3224    assert!(i == out.len());
3225
3226    unsafe { std::mem::transmute_copy(&out) }
3227    // TODO once stabilized switch to:
3228    // unsafe { MaybeUninit::array_assume_init(out) }
3229}
3230
3231pub(crate) fn i32_to_ordering(value: i32) -> std::cmp::Ordering {
3232    match value {
3233        ..0 => std::cmp::Ordering::Less,
3234        0 => std::cmp::Ordering::Equal,
3235        1.. => std::cmp::Ordering::Greater,
3236    }
3237}
3238
3239pub(crate) unsafe fn cstr_to_str<'a>(ptr: *const u8) -> &'a str {
3240    unsafe {
3241        let len = libc::strlen(ptr);
3242
3243        let bytes = std::slice::from_raw_parts(ptr.cast::<u8>(), len);
3244
3245        std::str::from_utf8(bytes).expect("bad cstr_to_str")
3246    }
3247}
3248
3249// ideally we could just use c string literal until we transition to &str everywhere
3250// unfortunately, some platforms people use have
3251macro_rules! c {
3252    ($s:literal) => {{
3253        const S: &str = concat!($s, "\0");
3254        #[allow(clippy::allow_attributes)]
3255        #[allow(unused_unsafe)]
3256        unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(S.as_bytes()) }
3257            .as_ptr()
3258            .cast::<u8>()
3259    }};
3260}
3261pub(crate) use c;
3262
3263macro_rules! enum_try_from {
3264    ($enum_ty:ty, $repr:ty, $last_variant:expr) => {
3265        impl TryFrom<$repr> for $enum_ty {
3266            type Error = ();
3267            fn try_from(value: $repr) -> Result<Self, ()> {
3268                if value <= $last_variant as $repr {
3269                    unsafe { Ok(std::mem::transmute::<$repr, Self>(value)) }
3270                } else {
3271                    Err(())
3272                }
3273            }
3274        }
3275    };
3276}
3277pub(crate) use enum_try_from;
3278
3279macro_rules! impl_ord {
3280    ($ty:ty as $func:ident) => {
3281        impl Ord for $ty {
3282            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
3283                $func(&self, &other)
3284            }
3285        }
3286        impl PartialEq for $ty {
3287            fn eq(&self, other: &Self) -> bool {
3288                self.cmp(other).is_eq()
3289            }
3290        }
3291        impl Eq for $ty {}
3292        impl PartialOrd for $ty {
3293            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
3294                Some(self.cmp(other))
3295            }
3296        }
3297    };
3298}
3299pub(crate) use impl_ord;
3300
3301macro_rules! const_unwrap_result {
3302    ($e:expr) => {
3303        match $e {
3304            Ok(value) => value,
3305            _ => panic!("const_unwrap_result"),
3306        }
3307    };
3308}
3309pub(crate) use const_unwrap_result;
3310
3311macro_rules! cstring_concat {
3312    ($($e:expr),* $(,)?) => {
3313        const_unwrap_result!(::core::ffi::CStr::from_bytes_with_nul(concat!($($e),*, "\0").as_bytes()))
3314    };
3315}
3316pub(crate) use cstring_concat;