rustix/termios/
types.rs

1use crate::backend::c;
2use crate::backend::termios::types;
3#[cfg(target_os = "nto")]
4use crate::ffi;
5use crate::{backend, io};
6use bitflags::bitflags;
7
8/// `struct termios` for use with [`tcgetattr`] and [`tcsetattr`].
9///
10/// [`tcgetattr`]: crate::termios::tcgetattr
11/// [`tcsetattr`]: crate::termios::tcsetattr
12#[repr(C)]
13#[derive(Clone)]
14pub struct Termios {
15    /// How is input interpreted?
16    #[doc(alias = "c_iflag")]
17    pub input_modes: InputModes,
18
19    /// How is output translated?
20    #[doc(alias = "c_oflag")]
21    pub output_modes: OutputModes,
22
23    /// Low-level configuration flags.
24    #[doc(alias = "c_cflag")]
25    pub control_modes: ControlModes,
26
27    /// High-level configuration flags.
28    #[doc(alias = "c_lflag")]
29    pub local_modes: LocalModes,
30
31    /// Line discipline.
32    #[doc(alias = "c_line")]
33    #[cfg(not(all(linux_raw, any(target_arch = "powerpc", target_arch = "powerpc64"))))]
34    #[cfg(any(
35        linux_like,
36        target_env = "newlib",
37        target_os = "fuchsia",
38        target_os = "haiku",
39        target_os = "redox"
40    ))]
41    pub line_discipline: u8,
42
43    /// How are various special control codes handled?
44    #[doc(alias = "c_cc")]
45    #[cfg(not(target_os = "haiku"))]
46    pub special_codes: SpecialCodes,
47
48    #[cfg(target_os = "nto")]
49    pub(crate) __reserved: [ffi::c_uint; 3],
50
51    /// Line discipline.
52    // On PowerPC, this field comes after `c_cc`.
53    #[doc(alias = "c_line")]
54    #[cfg(all(linux_raw, any(target_arch = "powerpc", target_arch = "powerpc64")))]
55    pub line_discipline: c::cc_t,
56
57    /// See the `input_speed` and `set_input_seed` functions.
58    ///
59    /// On Linux and BSDs, this is the arbitrary integer speed value. On all
60    /// other platforms, this is the encoded speed value.
61    #[cfg(not(any(solarish, all(libc, target_env = "newlib"), target_os = "aix")))]
62    pub(crate) input_speed: c::speed_t,
63
64    /// See the `output_speed` and `set_output_seed` functions.
65    ///
66    /// On Linux and BSDs, this is the integer speed value. On all other
67    /// platforms, this is the encoded speed value.
68    #[cfg(not(any(solarish, all(libc, target_env = "newlib"), target_os = "aix")))]
69    pub(crate) output_speed: c::speed_t,
70
71    /// How are various special control codes handled?
72    #[doc(alias = "c_cc")]
73    #[cfg(target_os = "haiku")]
74    pub special_codes: SpecialCodes,
75}
76
77impl Termios {
78    /// `cfmakeraw(self)`—Set a `Termios` value to the settings for “raw” mode.
79    ///
80    /// In raw mode, input is available a byte at a time, echoing is disabled,
81    /// and special terminal input and output codes are disabled.
82    #[cfg(not(target_os = "nto"))]
83    #[doc(alias = "cfmakeraw")]
84    #[inline]
85    pub fn make_raw(&mut self) {
86        backend::termios::syscalls::cfmakeraw(self)
87    }
88
89    /// Return the input communication speed.
90    ///
91    /// Unlike the `c_ispeed` field in glibc and others, this returns the
92    /// integer value of the speed, rather than the `B*` encoded constant
93    /// value.
94    #[doc(alias = "c_ispeed")]
95    #[doc(alias = "cfgetispeed")]
96    #[doc(alias = "cfgetspeed")]
97    #[inline]
98    pub fn input_speed(&self) -> u32 {
99        // On Linux and BSDs, `input_speed` is the arbitrary integer speed.
100        #[cfg(any(linux_kernel, bsd))]
101        {
102            debug_assert!(u32::try_from(self.input_speed).is_ok());
103            self.input_speed as u32
104        }
105
106        // On illumos, `input_speed` is not present.
107        #[cfg(any(solarish, all(libc, target_env = "newlib"), target_os = "aix"))]
108        unsafe {
109            speed::decode(c::cfgetispeed(crate::utils::as_ptr(self).cast())).unwrap()
110        }
111
112        // On other platforms, it's the encoded speed.
113        #[cfg(not(any(
114            linux_kernel,
115            bsd,
116            solarish,
117            all(libc, target_env = "newlib"),
118            target_os = "aix"
119        )))]
120        {
121            speed::decode(self.input_speed).unwrap()
122        }
123    }
124
125    /// Return the output communication speed.
126    ///
127    /// Unlike the `c_ospeed` field in glibc and others, this returns the
128    /// arbitrary integer value of the speed, rather than the `B*` encoded
129    /// constant value.
130    #[inline]
131    pub fn output_speed(&self) -> u32 {
132        // On Linux and BSDs, `output_speed` is the arbitrary integer speed.
133        #[cfg(any(linux_kernel, bsd))]
134        {
135            debug_assert!(u32::try_from(self.output_speed).is_ok());
136            self.output_speed as u32
137        }
138
139        // On illumos, `output_speed` is not present.
140        #[cfg(any(solarish, all(libc, target_env = "newlib"), target_os = "aix"))]
141        unsafe {
142            speed::decode(c::cfgetospeed(crate::utils::as_ptr(self).cast())).unwrap()
143        }
144
145        // On other platforms, it's the encoded speed.
146        #[cfg(not(any(
147            linux_kernel,
148            bsd,
149            solarish,
150            all(libc, target_env = "newlib"),
151            target_os = "aix"
152        )))]
153        {
154            speed::decode(self.output_speed).unwrap()
155        }
156    }
157
158    /// Set the input and output communication speeds.
159    ///
160    /// Unlike the `c_ispeed` and `c_ospeed` fields in glibc and others, this
161    /// takes the arbitrary integer value of the speed, rather than the `B*`
162    /// encoded constant value. Not all implementations support all integer
163    /// values; use the constants in the [`speed`] module for likely-supported
164    /// speeds.
165    #[cfg(not(target_os = "nto"))]
166    #[doc(alias = "cfsetspeed")]
167    #[doc(alias = "CBAUD")]
168    #[doc(alias = "CBAUDEX")]
169    #[doc(alias = "CIBAUD")]
170    #[doc(alias = "CIBAUDEX")]
171    #[inline]
172    pub fn set_speed(&mut self, new_speed: u32) -> io::Result<()> {
173        backend::termios::syscalls::set_speed(self, new_speed)
174    }
175
176    /// Set the input communication speed.
177    ///
178    /// Unlike the `c_ispeed` field in glibc and others, this takes the
179    /// arbitrary integer value of the speed, rather than the `B*` encoded
180    /// constant value. Not all implementations support all integer values; use
181    /// the constants in the [`speed`] module for known-supported speeds.
182    ///
183    /// On some platforms, changing the input speed changes the output speed to
184    /// the same speed.
185    #[doc(alias = "c_ispeed")]
186    #[doc(alias = "cfsetispeed")]
187    #[doc(alias = "CIBAUD")]
188    #[doc(alias = "CIBAUDEX")]
189    #[inline]
190    pub fn set_input_speed(&mut self, new_speed: u32) -> io::Result<()> {
191        backend::termios::syscalls::set_input_speed(self, new_speed)
192    }
193
194    /// Set the output communication speed.
195    ///
196    /// Unlike the `c_ospeed` field in glibc and others, this takes the
197    /// arbitrary integer value of the speed, rather than the `B*` encoded
198    /// constant value. Not all implementations support all integer values; use
199    /// the constants in the [`speed`] module for known-supported speeds.
200    ///
201    /// On some platforms, changing the output speed changes the input speed to
202    /// the same speed.
203    #[doc(alias = "c_ospeed")]
204    #[doc(alias = "cfsetospeed")]
205    #[doc(alias = "CBAUD")]
206    #[doc(alias = "CBAUDEX")]
207    #[inline]
208    pub fn set_output_speed(&mut self, new_speed: u32) -> io::Result<()> {
209        backend::termios::syscalls::set_output_speed(self, new_speed)
210    }
211}
212
213impl core::fmt::Debug for Termios {
214    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
215        let mut d = f.debug_struct("Termios");
216        d.field("input_modes", &self.input_modes);
217        d.field("output_modes", &self.output_modes);
218
219        // This includes any bits set in the `CBAUD` and `CIBAUD` ranges, which
220        // is a little ugly, because we also decode those bits for the speeds
221        // below. However, it seems better to print them here than to hide
222        // them, because hiding them would make the `Termios` debug output
223        // appear to disagree with the `ControlModes` debug output for the same
224        // value, which could be confusing.
225        d.field("control_modes", &self.control_modes);
226
227        d.field("local_modes", &self.local_modes);
228        #[cfg(any(
229            linux_like,
230            target_env = "newlib",
231            target_os = "fuchsia",
232            target_os = "haiku",
233            target_os = "redox"
234        ))]
235        {
236            d.field("line_discipline", &SpecialCode(self.line_discipline));
237        }
238        d.field("special_codes", &self.special_codes);
239        d.field("input_speed", &self.input_speed());
240        d.field("output_speed", &self.output_speed());
241        d.finish()
242    }
243}
244
245bitflags! {
246    /// Flags controlling terminal input.
247    #[repr(transparent)]
248    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
249    pub struct InputModes: types::tcflag_t {
250        /// `IGNBRK`
251        const IGNBRK = c::IGNBRK;
252
253        /// `BRKINT`
254        const BRKINT = c::BRKINT;
255
256        /// `IGNPAR`
257        const IGNPAR = c::IGNPAR;
258
259        /// `PARMRK`
260        const PARMRK = c::PARMRK;
261
262        /// `INPCK`
263        const INPCK = c::INPCK;
264
265        /// `ISTRIP`
266        const ISTRIP = c::ISTRIP;
267
268        /// `INLCR`
269        const INLCR = c::INLCR;
270
271        /// `IGNCR`
272        const IGNCR = c::IGNCR;
273
274        /// `ICRNL`
275        const ICRNL = c::ICRNL;
276
277        /// `IUCLC`
278        #[cfg(any(linux_kernel, solarish, target_os = "aix", target_os = "haiku", target_os = "nto"))]
279        const IUCLC = c::IUCLC;
280
281        /// `IXON`
282        const IXON = c::IXON;
283
284        /// `IXANY`
285        #[cfg(not(target_os = "redox"))]
286        const IXANY = c::IXANY;
287
288        /// `IXOFF`
289        const IXOFF = c::IXOFF;
290
291        /// `IMAXBEL`
292        #[cfg(not(any(target_os = "haiku", target_os = "redox")))]
293        const IMAXBEL = c::IMAXBEL;
294
295        /// `IUTF8`
296        #[cfg(not(any(
297            freebsdlike,
298            netbsdlike,
299            solarish,
300            target_os = "aix",
301            target_os = "emscripten",
302            target_os = "haiku",
303            target_os = "hurd",
304            target_os = "redox",
305        )))]
306        const IUTF8 = c::IUTF8;
307
308        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
309        const _ = !0;
310    }
311}
312
313bitflags! {
314    /// Flags controlling terminal output.
315    #[repr(transparent)]
316    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
317    pub struct OutputModes: types::tcflag_t {
318        /// `OPOST`
319        const OPOST = c::OPOST;
320
321        /// `OLCUC`
322        #[cfg(not(any(
323            apple,
324            freebsdlike,
325            target_os = "aix",
326            target_os = "netbsd",
327            target_os = "redox",
328        )))]
329        const OLCUC = c::OLCUC;
330
331        /// `ONLCR`
332        const ONLCR = c::ONLCR;
333
334        /// `OCRNL`
335        const OCRNL = c::OCRNL;
336
337        /// `ONOCR`
338        const ONOCR = c::ONOCR;
339
340        /// `ONLRET`
341        const ONLRET = c::ONLRET;
342
343        /// `OFILL`
344        #[cfg(not(bsd))]
345        const OFILL = c::OFILL;
346
347        /// `OFDEL`
348        #[cfg(not(bsd))]
349        const OFDEL = c::OFDEL;
350
351        /// `NLDLY`
352        #[cfg(not(any(bsd, solarish, target_os = "redox")))]
353        const NLDLY = c::NLDLY;
354
355        /// `NL0`
356        #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
357        const NL0 = c::NL0;
358
359        /// `NL1`
360        #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
361        const NL1 = c::NL1;
362
363        /// `CRDLY`
364        #[cfg(not(any(bsd, solarish, target_os = "redox")))]
365        const CRDLY = c::CRDLY;
366
367        /// `CR0`
368        #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
369        const CR0 = c::CR0;
370
371        /// `CR1`
372        #[cfg(not(any(
373            target_env = "musl",
374            bsd,
375            solarish,
376            target_os = "emscripten",
377            target_os = "fuchsia",
378            target_os = "redox",
379        )))]
380        const CR1 = c::CR1;
381
382        /// `CR2`
383        #[cfg(not(any(
384            target_env = "musl",
385            bsd,
386            solarish,
387            target_os = "emscripten",
388            target_os = "fuchsia",
389            target_os = "redox",
390        )))]
391        const CR2 = c::CR2;
392
393        /// `CR3`
394        #[cfg(not(any(
395            target_env = "musl",
396            bsd,
397            solarish,
398            target_os = "emscripten",
399            target_os = "fuchsia",
400            target_os = "redox",
401        )))]
402        const CR3 = c::CR3;
403
404        /// `TABDLY`
405        #[cfg(not(any(
406            netbsdlike,
407            solarish,
408            target_os = "dragonfly",
409            target_os = "redox",
410        )))]
411        const TABDLY = c::TABDLY;
412
413        /// `TAB0`
414        #[cfg(not(any(
415            netbsdlike,
416            solarish,
417            target_os = "dragonfly",
418            target_os = "fuchsia",
419            target_os = "redox",
420        )))]
421        const TAB0 = c::TAB0;
422
423        /// `TAB1`
424        #[cfg(not(any(
425            target_env = "musl",
426            bsd,
427            solarish,
428            target_os = "emscripten",
429            target_os = "fuchsia",
430            target_os = "redox",
431        )))]
432        const TAB1 = c::TAB1;
433
434        /// `TAB2`
435        #[cfg(not(any(
436            target_env = "musl",
437            bsd,
438            solarish,
439            target_os = "emscripten",
440            target_os = "fuchsia",
441            target_os = "redox",
442        )))]
443        const TAB2 = c::TAB2;
444
445        /// `TAB3`
446        #[cfg(not(any(
447            target_env = "musl",
448            bsd,
449            solarish,
450            target_os = "emscripten",
451            target_os = "fuchsia",
452            target_os = "redox",
453        )))]
454        const TAB3 = c::TAB3;
455
456        /// `XTABS`
457        #[cfg(not(any(
458            bsd,
459            solarish,
460            target_os = "aix",
461            target_os = "haiku",
462            target_os = "redox",
463        )))]
464        const XTABS = c::XTABS;
465
466        /// `BSDLY`
467        #[cfg(not(any(bsd, solarish, target_os = "redox")))]
468        const BSDLY = c::BSDLY;
469
470        /// `BS0`
471        #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
472        const BS0 = c::BS0;
473
474        /// `BS1`
475        #[cfg(not(any(
476            target_env = "musl",
477            bsd,
478            solarish,
479            target_os = "emscripten",
480            target_os = "fuchsia",
481            target_os = "redox",
482        )))]
483        const BS1 = c::BS1;
484
485        /// `FFDLY`
486        #[cfg(not(any(bsd, solarish, target_os = "redox")))]
487        const FFDLY = c::FFDLY;
488
489        /// `FF0`
490        #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
491        const FF0 = c::FF0;
492
493        /// `FF1`
494        #[cfg(not(any(
495            target_env = "musl",
496            bsd,
497            solarish,
498            target_os = "emscripten",
499            target_os = "fuchsia",
500            target_os = "redox",
501        )))]
502        const FF1 = c::FF1;
503
504        /// `VTDLY`
505        #[cfg(not(any(bsd, solarish, target_os = "redox")))]
506        const VTDLY = c::VTDLY;
507
508        /// `VT0`
509        #[cfg(not(any(bsd, solarish, target_os = "fuchsia", target_os = "redox")))]
510        const VT0 = c::VT0;
511
512        /// `VT1`
513        #[cfg(not(any(
514            target_env = "musl",
515            bsd,
516            solarish,
517            target_os = "emscripten",
518            target_os = "fuchsia",
519            target_os = "redox",
520        )))]
521        const VT1 = c::VT1;
522
523        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
524        const _ = !0;
525    }
526}
527
528bitflags! {
529    /// Flags controlling special terminal modes.
530    ///
531    /// `CBAUD`, `CBAUDEX`, `CIBAUD`, `CIBAUDEX`, and various `B*` speed
532    /// constants are often included in the control modes, however rustix
533    /// handles them separately, in [`Termios::set_speed`] and related
534    /// functions. If you see extra bits in the `Debug` output, they're
535    /// probably these flags.
536    #[repr(transparent)]
537    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
538    pub struct ControlModes: types::tcflag_t {
539        /// `CSIZE`
540        const CSIZE = c::CSIZE;
541
542        /// `CS5`
543        const CS5 = c::CS5;
544
545        /// `CS6`
546        const CS6 = c::CS6;
547
548        /// `CS7`
549        const CS7 = c::CS7;
550
551        /// `CS8`
552        const CS8 = c::CS8;
553
554        /// `CSTOPB`
555        const CSTOPB = c::CSTOPB;
556
557        /// `CREAD`
558        const CREAD = c::CREAD;
559
560        /// `PARENB`
561        const PARENB = c::PARENB;
562
563        /// `PARODD`
564        const PARODD = c::PARODD;
565
566        /// `HUPCL`
567        const HUPCL = c::HUPCL;
568
569        /// `CLOCAL`
570        const CLOCAL = c::CLOCAL;
571
572        /// `CRTSCTS`
573        #[cfg(not(any(target_os = "aix", target_os = "nto", target_os = "redox")))]
574        const CRTSCTS = c::CRTSCTS;
575
576        /// `CMSPAR`
577        #[cfg(not(any(
578            bsd,
579            solarish,
580            target_os = "aix",
581            target_os = "emscripten",
582            target_os = "haiku",
583            target_os = "hurd",
584            target_os = "nto",
585            target_os = "redox",
586        )))]
587        const CMSPAR = c::CMSPAR;
588
589        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
590        const _ = !0;
591    }
592}
593
594bitflags! {
595    /// Flags controlling “local” terminal modes.
596    #[repr(transparent)]
597    #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
598    pub struct LocalModes: types::tcflag_t {
599        /// `XCASE`
600        #[cfg(any(linux_kernel, target_arch = "s390x", target_os = "haiku"))]
601        const XCASE = c::XCASE;
602
603        /// `ECHOCTL`
604        #[cfg(not(target_os = "redox"))]
605        const ECHOCTL = c::ECHOCTL;
606
607        /// `ECHOPRT`
608        #[cfg(not(any(target_os = "cygwin", target_os = "nto", target_os = "redox")))]
609        const ECHOPRT = c::ECHOPRT;
610
611        /// `ECHOKE`
612        #[cfg(not(target_os = "redox"))]
613        const ECHOKE = c::ECHOKE;
614
615        /// `FLUSHO`
616        #[cfg(not(any(target_os = "nto", target_os = "redox")))]
617        const FLUSHO = c::FLUSHO;
618
619        /// `PENDIN`
620        #[cfg(not(any(target_os = "cygwin", target_os = "nto", target_os = "redox")))]
621        const PENDIN = c::PENDIN;
622
623        /// `EXTPROC`
624        #[cfg(not(any(
625            target_os = "aix",
626            target_os = "cygwin",
627            target_os = "haiku",
628            target_os = "nto",
629            target_os = "redox",
630        )))]
631        const EXTPROC = c::EXTPROC;
632
633        /// `ISIG`
634        const ISIG = c::ISIG;
635
636        /// `ICANON`—A flag for the `c_lflag` field of [`Termios`] indicating
637        /// canonical mode.
638        const ICANON = c::ICANON;
639
640        /// `ECHO`
641        const ECHO = c::ECHO;
642
643        /// `ECHOE`
644        const ECHOE = c::ECHOE;
645
646        /// `ECHOK`
647        const ECHOK = c::ECHOK;
648
649        /// `ECHONL`
650        const ECHONL = c::ECHONL;
651
652        /// `NOFLSH`
653        const NOFLSH = c::NOFLSH;
654
655        /// `TOSTOP`
656        const TOSTOP = c::TOSTOP;
657
658        /// `IEXTEN`
659        const IEXTEN = c::IEXTEN;
660
661        /// <https://docs.rs/bitflags/*/bitflags/#externally-defined-flags>
662        const _ = !0;
663    }
664}
665
666/// Speeds for use with [`Termios::set_speed`], [`Termios::set_input_speed`],
667/// and [`Termios::set_output_speed`].
668///
669/// Unlike in some platforms' libc APIs, these always have the same numerical
670/// value as their names; for example, `B50` has the value `50`, and so on.
671/// Consequently, it's not necessary to use them. They are provided here
672/// because they help identify speeds which are likely to be supported, on
673/// platforms and devices which don't support arbitrary speeds.
674pub mod speed {
675    #[cfg(not(bsd))]
676    use crate::backend::c;
677
678    /// `B0`
679    pub const B0: u32 = 0;
680
681    /// `B50`
682    pub const B50: u32 = 50;
683
684    /// `B75`
685    pub const B75: u32 = 75;
686
687    /// `B110`
688    pub const B110: u32 = 110;
689
690    /// `B134`
691    pub const B134: u32 = 134;
692
693    /// `B150`
694    pub const B150: u32 = 150;
695
696    /// `B200`
697    pub const B200: u32 = 200;
698
699    /// `B300`
700    pub const B300: u32 = 300;
701
702    /// `B600`
703    pub const B600: u32 = 600;
704
705    /// `B1200`
706    pub const B1200: u32 = 1200;
707
708    /// `B1800`
709    pub const B1800: u32 = 1800;
710
711    /// `B2400`
712    pub const B2400: u32 = 2400;
713
714    /// `B4800`
715    pub const B4800: u32 = 4800;
716
717    /// `B9600`
718    pub const B9600: u32 = 9600;
719
720    /// `B19200`
721    #[doc(alias = "EXTA")]
722    pub const B19200: u32 = 19200;
723
724    /// `B38400`
725    #[doc(alias = "EXTB")]
726    pub const B38400: u32 = 38400;
727
728    /// `B57600`
729    #[cfg(not(target_os = "aix"))]
730    pub const B57600: u32 = 57600;
731
732    /// `B115200`
733    #[cfg(not(target_os = "aix"))]
734    pub const B115200: u32 = 115_200;
735
736    /// `B230400`
737    #[cfg(not(target_os = "aix"))]
738    pub const B230400: u32 = 230_400;
739
740    /// `B460800`
741    #[cfg(not(any(
742        apple,
743        target_os = "aix",
744        target_os = "dragonfly",
745        target_os = "haiku",
746        target_os = "openbsd"
747    )))]
748    pub const B460800: u32 = 460_800;
749
750    /// `B500000`
751    #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))]
752    pub const B500000: u32 = 500_000;
753
754    /// `B576000`
755    #[cfg(not(any(bsd, solarish, target_os = "aix", target_os = "haiku")))]
756    pub const B576000: u32 = 576_000;
757
758    /// `B921600`
759    #[cfg(not(any(
760        apple,
761        target_os = "aix",
762        target_os = "dragonfly",
763        target_os = "haiku",
764        target_os = "openbsd"
765    )))]
766    pub const B921600: u32 = 921_600;
767
768    /// `B1000000`
769    #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
770    pub const B1000000: u32 = 1_000_000;
771
772    /// `B1152000`
773    #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
774    pub const B1152000: u32 = 1_152_000;
775
776    /// `B1500000`
777    #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
778    pub const B1500000: u32 = 1_500_000;
779
780    /// `B2000000`
781    #[cfg(not(any(bsd, target_os = "aix", target_os = "haiku", target_os = "solaris")))]
782    pub const B2000000: u32 = 2_000_000;
783
784    /// `B2500000`
785    #[cfg(not(any(
786        target_arch = "sparc",
787        target_arch = "sparc64",
788        bsd,
789        target_os = "aix",
790        target_os = "haiku",
791        target_os = "solaris",
792    )))]
793    pub const B2500000: u32 = 2_500_000;
794
795    /// `B3000000`
796    #[cfg(not(any(
797        target_arch = "sparc",
798        target_arch = "sparc64",
799        bsd,
800        target_os = "aix",
801        target_os = "haiku",
802        target_os = "solaris",
803    )))]
804    pub const B3000000: u32 = 3_000_000;
805
806    /// `B3500000`
807    #[cfg(not(any(
808        target_arch = "sparc",
809        target_arch = "sparc64",
810        bsd,
811        target_os = "aix",
812        target_os = "haiku",
813        target_os = "solaris",
814    )))]
815    pub const B3500000: u32 = 3_500_000;
816
817    /// `B4000000`
818    #[cfg(not(any(
819        target_arch = "sparc",
820        target_arch = "sparc64",
821        bsd,
822        target_os = "aix",
823        target_os = "haiku",
824        target_os = "solaris",
825    )))]
826    pub const B4000000: u32 = 4_000_000;
827
828    /// Translate from a `c::speed_t` code to an arbitrary integer speed value
829    /// `u32`.
830    ///
831    /// On BSD platforms, integer speed values are already the same as their
832    /// encoded values.
833    ///
834    /// On Linux on PowerPC, `TCGETS`/`TCSETS` support the `c_ispeed` and
835    /// `c_ospeed` fields.
836    ///
837    /// On Linux on architectures other than PowerPC, `TCGETS`/`TCSETS` don't
838    /// support the `c_ispeed` and `c_ospeed` fields, so we have to fall back
839    /// to `TCGETS2`/`TCSETS2` to support them.
840    #[cfg(not(any(
841        bsd,
842        all(linux_kernel, any(target_arch = "powerpc", target_arch = "powerpc64"))
843    )))]
844    pub(crate) const fn decode(encoded_speed: c::speed_t) -> Option<u32> {
845        match encoded_speed {
846            c::B0 => Some(0),
847            c::B50 => Some(50),
848            c::B75 => Some(75),
849            c::B110 => Some(110),
850            c::B134 => Some(134),
851            c::B150 => Some(150),
852            c::B200 => Some(200),
853            c::B300 => Some(300),
854            c::B600 => Some(600),
855            c::B1200 => Some(1200),
856            c::B1800 => Some(1800),
857            c::B2400 => Some(2400),
858            c::B4800 => Some(4800),
859            c::B9600 => Some(9600),
860            c::B19200 => Some(19200),
861            c::B38400 => Some(38400),
862            #[cfg(not(target_os = "aix"))]
863            c::B57600 => Some(57600),
864            #[cfg(not(target_os = "aix"))]
865            c::B115200 => Some(115_200),
866            #[cfg(not(any(target_os = "aix", target_os = "nto")))]
867            c::B230400 => Some(230_400),
868            #[cfg(not(any(
869                apple,
870                target_os = "aix",
871                target_os = "dragonfly",
872                target_os = "haiku",
873                target_os = "nto",
874                target_os = "openbsd"
875            )))]
876            c::B460800 => Some(460_800),
877            #[cfg(not(any(
878                bsd,
879                solarish,
880                target_os = "aix",
881                target_os = "haiku",
882                target_os = "nto"
883            )))]
884            c::B500000 => Some(500_000),
885            #[cfg(not(any(
886                bsd,
887                solarish,
888                target_os = "aix",
889                target_os = "haiku",
890                target_os = "nto"
891            )))]
892            c::B576000 => Some(576_000),
893            #[cfg(not(any(
894                apple,
895                target_os = "aix",
896                target_os = "dragonfly",
897                target_os = "haiku",
898                target_os = "nto",
899                target_os = "openbsd"
900            )))]
901            c::B921600 => Some(921_600),
902            #[cfg(not(any(
903                bsd,
904                target_os = "aix",
905                target_os = "haiku",
906                target_os = "nto",
907                target_os = "solaris"
908            )))]
909            c::B1000000 => Some(1_000_000),
910            #[cfg(not(any(
911                bsd,
912                target_os = "aix",
913                target_os = "haiku",
914                target_os = "nto",
915                target_os = "solaris"
916            )))]
917            c::B1152000 => Some(1_152_000),
918            #[cfg(not(any(
919                bsd,
920                target_os = "aix",
921                target_os = "haiku",
922                target_os = "nto",
923                target_os = "solaris"
924            )))]
925            c::B1500000 => Some(1_500_000),
926            #[cfg(not(any(
927                bsd,
928                target_os = "aix",
929                target_os = "haiku",
930                target_os = "nto",
931                target_os = "solaris"
932            )))]
933            c::B2000000 => Some(2_000_000),
934            #[cfg(not(any(
935                target_arch = "sparc",
936                target_arch = "sparc64",
937                bsd,
938                target_os = "aix",
939                target_os = "haiku",
940                target_os = "nto",
941                target_os = "solaris",
942            )))]
943            c::B2500000 => Some(2_500_000),
944            #[cfg(not(any(
945                target_arch = "sparc",
946                target_arch = "sparc64",
947                bsd,
948                target_os = "aix",
949                target_os = "haiku",
950                target_os = "nto",
951                target_os = "solaris",
952            )))]
953            c::B3000000 => Some(3_000_000),
954            #[cfg(not(any(
955                target_arch = "sparc",
956                target_arch = "sparc64",
957                bsd,
958                target_os = "aix",
959                target_os = "cygwin",
960                target_os = "haiku",
961                target_os = "nto",
962                target_os = "solaris",
963            )))]
964            c::B3500000 => Some(3_500_000),
965            #[cfg(not(any(
966                target_arch = "sparc",
967                target_arch = "sparc64",
968                bsd,
969                target_os = "aix",
970                target_os = "cygwin",
971                target_os = "haiku",
972                target_os = "nto",
973                target_os = "solaris",
974            )))]
975            c::B4000000 => Some(4_000_000),
976            _ => None,
977        }
978    }
979
980    /// Translate from an arbitrary `u32` arbitrary integer speed value to a
981    /// `c::speed_t` code.
982    #[cfg(not(bsd))]
983    pub(crate) const fn encode(speed: u32) -> Option<c::speed_t> {
984        match speed {
985            0 => Some(c::B0),
986            50 => Some(c::B50),
987            75 => Some(c::B75),
988            110 => Some(c::B110),
989            134 => Some(c::B134),
990            150 => Some(c::B150),
991            200 => Some(c::B200),
992            300 => Some(c::B300),
993            600 => Some(c::B600),
994            1200 => Some(c::B1200),
995            1800 => Some(c::B1800),
996            2400 => Some(c::B2400),
997            4800 => Some(c::B4800),
998            9600 => Some(c::B9600),
999            19200 => Some(c::B19200),
1000            38400 => Some(c::B38400),
1001            #[cfg(not(target_os = "aix"))]
1002            57600 => Some(c::B57600),
1003            #[cfg(not(target_os = "aix"))]
1004            115_200 => Some(c::B115200),
1005            #[cfg(not(any(target_os = "aix", target_os = "nto")))]
1006            230_400 => Some(c::B230400),
1007            #[cfg(not(any(
1008                apple,
1009                target_os = "aix",
1010                target_os = "dragonfly",
1011                target_os = "haiku",
1012                target_os = "nto",
1013                target_os = "openbsd",
1014            )))]
1015            460_800 => Some(c::B460800),
1016            #[cfg(not(any(
1017                bsd,
1018                solarish,
1019                target_os = "aix",
1020                target_os = "haiku",
1021                target_os = "nto"
1022            )))]
1023            500_000 => Some(c::B500000),
1024            #[cfg(not(any(
1025                bsd,
1026                solarish,
1027                target_os = "aix",
1028                target_os = "haiku",
1029                target_os = "nto"
1030            )))]
1031            576_000 => Some(c::B576000),
1032            #[cfg(not(any(
1033                apple,
1034                target_os = "aix",
1035                target_os = "dragonfly",
1036                target_os = "haiku",
1037                target_os = "nto",
1038                target_os = "openbsd"
1039            )))]
1040            921_600 => Some(c::B921600),
1041            #[cfg(not(any(
1042                bsd,
1043                target_os = "aix",
1044                target_os = "haiku",
1045                target_os = "nto",
1046                target_os = "solaris"
1047            )))]
1048            1_000_000 => Some(c::B1000000),
1049            #[cfg(not(any(
1050                bsd,
1051                target_os = "aix",
1052                target_os = "haiku",
1053                target_os = "nto",
1054                target_os = "solaris"
1055            )))]
1056            1_152_000 => Some(c::B1152000),
1057            #[cfg(not(any(
1058                bsd,
1059                target_os = "aix",
1060                target_os = "haiku",
1061                target_os = "nto",
1062                target_os = "solaris"
1063            )))]
1064            1_500_000 => Some(c::B1500000),
1065            #[cfg(not(any(
1066                bsd,
1067                target_os = "aix",
1068                target_os = "haiku",
1069                target_os = "nto",
1070                target_os = "solaris"
1071            )))]
1072            2_000_000 => Some(c::B2000000),
1073            #[cfg(not(any(
1074                target_arch = "sparc",
1075                target_arch = "sparc64",
1076                bsd,
1077                target_os = "aix",
1078                target_os = "haiku",
1079                target_os = "nto",
1080                target_os = "solaris",
1081            )))]
1082            2_500_000 => Some(c::B2500000),
1083            #[cfg(not(any(
1084                target_arch = "sparc",
1085                target_arch = "sparc64",
1086                bsd,
1087                target_os = "aix",
1088                target_os = "haiku",
1089                target_os = "nto",
1090                target_os = "solaris",
1091            )))]
1092            3_000_000 => Some(c::B3000000),
1093            #[cfg(not(any(
1094                target_arch = "sparc",
1095                target_arch = "sparc64",
1096                bsd,
1097                target_os = "aix",
1098                target_os = "cygwin",
1099                target_os = "haiku",
1100                target_os = "nto",
1101                target_os = "solaris",
1102            )))]
1103            3_500_000 => Some(c::B3500000),
1104            #[cfg(not(any(
1105                target_arch = "sparc",
1106                target_arch = "sparc64",
1107                bsd,
1108                target_os = "aix",
1109                target_os = "cygwin",
1110                target_os = "haiku",
1111                target_os = "nto",
1112                target_os = "solaris",
1113            )))]
1114            4_000_000 => Some(c::B4000000),
1115            _ => None,
1116        }
1117    }
1118}
1119
1120/// An array indexed by [`SpecialCodeIndex`] indicating the current values of
1121/// various special control codes.
1122#[repr(transparent)]
1123#[derive(Clone)]
1124pub struct SpecialCodes(pub(crate) [c::cc_t; c::NCCS as usize]);
1125
1126impl core::ops::Index<SpecialCodeIndex> for SpecialCodes {
1127    type Output = u8;
1128
1129    fn index(&self, index: SpecialCodeIndex) -> &Self::Output {
1130        &self.0[index.0]
1131    }
1132}
1133
1134impl core::ops::IndexMut<SpecialCodeIndex> for SpecialCodes {
1135    fn index_mut(&mut self, index: SpecialCodeIndex) -> &mut Self::Output {
1136        &mut self.0[index.0]
1137    }
1138}
1139
1140impl core::fmt::Debug for SpecialCodes {
1141    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1142        write!(f, "SpecialCodes {{")?;
1143        let mut first = true;
1144        for i in 0..self.0.len() {
1145            if first {
1146                write!(f, " ")?;
1147            } else {
1148                write!(f, ", ")?;
1149            }
1150            first = false;
1151            let index = SpecialCodeIndex(i);
1152            write!(f, "{:?}: {:?}", index, SpecialCode(self[index]))?;
1153        }
1154        if !first {
1155            write!(f, " ")?;
1156        }
1157        write!(f, "}}")
1158    }
1159}
1160
1161/// A newtype for pretty printing.
1162struct SpecialCode(u8);
1163
1164impl core::fmt::Debug for SpecialCode {
1165    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1166        if self.0 == 0 {
1167            write!(f, "<undef>")
1168        } else if self.0 < 0x20 {
1169            write!(f, "^{}", (self.0 + 0x40) as char)
1170        } else if self.0 == 0x7f {
1171            write!(f, "^?")
1172        } else if self.0 >= 0x80 {
1173            write!(f, "M-")?;
1174            SpecialCode(self.0 - 0x80).fmt(f)
1175        } else {
1176            write!(f, "{}", (self.0 as char))
1177        }
1178    }
1179}
1180
1181/// Indices for use with [`Termios::special_codes`].
1182#[derive(Copy, Clone, Eq, PartialEq, Hash)]
1183pub struct SpecialCodeIndex(usize);
1184
1185#[rustfmt::skip]
1186impl SpecialCodeIndex {
1187    /// `VINTR`
1188    pub const VINTR: Self = Self(c::VINTR as usize);
1189
1190    /// `VQUIT`
1191    pub const VQUIT: Self = Self(c::VQUIT as usize);
1192
1193    /// `VERASE`
1194    pub const VERASE: Self = Self(c::VERASE as usize);
1195
1196    /// `VKILL`
1197    pub const VKILL: Self = Self(c::VKILL as usize);
1198
1199    /// `VEOF`
1200    pub const VEOF: Self = Self(c::VEOF as usize);
1201
1202    /// `VTIME`
1203    pub const VTIME: Self = Self(c::VTIME as usize);
1204
1205    /// `VMIN`
1206    pub const VMIN: Self = Self(c::VMIN as usize);
1207
1208    /// `VSWTC`
1209    #[cfg(not(any(
1210        bsd,
1211        solarish,
1212        target_os = "aix",
1213        target_os = "haiku",
1214        target_os = "hurd",
1215        target_os = "nto",
1216    )))]
1217    pub const VSWTC: Self = Self(c::VSWTC as usize);
1218
1219    /// `VSTART`
1220    pub const VSTART: Self = Self(c::VSTART as usize);
1221
1222    /// `VSTOP`
1223    pub const VSTOP: Self = Self(c::VSTOP as usize);
1224
1225    /// `VSUSP`
1226    pub const VSUSP: Self = Self(c::VSUSP as usize);
1227
1228    /// `VEOL`
1229    pub const VEOL: Self = Self(c::VEOL as usize);
1230
1231    /// `VREPRINT`
1232    #[cfg(not(target_os = "haiku"))]
1233    pub const VREPRINT: Self = Self(c::VREPRINT as usize);
1234
1235    /// `VDISCARD`
1236    #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
1237    pub const VDISCARD: Self = Self(c::VDISCARD as usize);
1238
1239    /// `VWERASE`
1240    #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
1241    pub const VWERASE: Self = Self(c::VWERASE as usize);
1242
1243    /// `VLNEXT`
1244    #[cfg(not(target_os = "haiku"))]
1245    pub const VLNEXT: Self = Self(c::VLNEXT as usize);
1246
1247    /// `VEOL2`
1248    pub const VEOL2: Self = Self(c::VEOL2 as usize);
1249
1250    /// `VSWTCH`
1251    #[cfg(any(solarish, target_os = "haiku", target_os = "nto"))]
1252    pub const VSWTCH: Self = Self(c::VSWTCH as usize);
1253
1254    /// `VDSUSP`
1255    #[cfg(any(
1256        bsd,
1257        solarish,
1258        target_os = "aix",
1259        target_os = "hurd",
1260        target_os = "nto"
1261    ))]
1262    pub const VDSUSP: Self = Self(c::VDSUSP as usize);
1263
1264    /// `VSTATUS`
1265    #[cfg(any(bsd, target_os = "hurd", target_os = "illumos"))]
1266    pub const VSTATUS: Self = Self(c::VSTATUS as usize);
1267
1268    /// `VERASE2`
1269    #[cfg(any(freebsdlike, target_os = "illumos"))]
1270    pub const VERASE2: Self = Self(c::VERASE2 as usize);
1271}
1272
1273impl core::fmt::Debug for SpecialCodeIndex {
1274    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1275        match *self {
1276            Self::VINTR => write!(f, "VINTR"),
1277            Self::VQUIT => write!(f, "VQUIT"),
1278            Self::VERASE => write!(f, "VERASE"),
1279            Self::VKILL => write!(f, "VKILL"),
1280            #[cfg(not(any(
1281                solarish,
1282                all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")),
1283                target_os = "aix",
1284                target_os = "haiku",
1285            )))]
1286            Self::VEOF => write!(f, "VEOF"),
1287            #[cfg(not(any(
1288                solarish,
1289                all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")),
1290                target_os = "aix",
1291                target_os = "haiku",
1292            )))]
1293            Self::VTIME => write!(f, "VTIME"),
1294            #[cfg(not(any(
1295                solarish,
1296                all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")),
1297                target_os = "aix",
1298                target_os = "haiku",
1299            )))]
1300            Self::VMIN => write!(f, "VMIN"),
1301
1302            // On Solarish platforms, Linux on SPARC, AIX, and Haiku, `VMIN`
1303            // and `VTIME` have the same value as `VEOF` and `VEOL`.
1304            #[cfg(any(
1305                solarish,
1306                all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")),
1307                target_os = "aix",
1308                target_os = "haiku",
1309            ))]
1310            Self::VMIN => write!(f, "VMIN/VEOF"),
1311            #[cfg(any(
1312                solarish,
1313                all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")),
1314                target_os = "aix",
1315                target_os = "haiku",
1316            ))]
1317            Self::VTIME => write!(f, "VTIME/VEOL"),
1318
1319            #[cfg(not(any(
1320                bsd,
1321                solarish,
1322                target_os = "aix",
1323                target_os = "haiku",
1324                target_os = "hurd",
1325                target_os = "nto",
1326            )))]
1327            Self::VSWTC => write!(f, "VSWTC"),
1328            Self::VSTART => write!(f, "VSTART"),
1329            Self::VSTOP => write!(f, "VSTOP"),
1330            Self::VSUSP => write!(f, "VSUSP"),
1331            #[cfg(not(any(
1332                solarish,
1333                all(linux_kernel, any(target_arch = "sparc", target_arch = "sparc64")),
1334                target_os = "aix",
1335                target_os = "haiku",
1336            )))]
1337            Self::VEOL => write!(f, "VEOL"),
1338            #[cfg(not(target_os = "haiku"))]
1339            Self::VREPRINT => write!(f, "VREPRINT"),
1340            #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
1341            Self::VDISCARD => write!(f, "VDISCARD"),
1342            #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
1343            Self::VWERASE => write!(f, "VWERASE"),
1344            #[cfg(not(target_os = "haiku"))]
1345            Self::VLNEXT => write!(f, "VLNEXT"),
1346            Self::VEOL2 => write!(f, "VEOL2"),
1347            #[cfg(any(solarish, target_os = "haiku", target_os = "nto"))]
1348            Self::VSWTCH => write!(f, "VSWTCH"),
1349            #[cfg(any(
1350                bsd,
1351                solarish,
1352                target_os = "aix",
1353                target_os = "hurd",
1354                target_os = "nto"
1355            ))]
1356            Self::VDSUSP => write!(f, "VDSUSP"),
1357            #[cfg(any(bsd, target_os = "hurd", target_os = "illumos"))]
1358            Self::VSTATUS => write!(f, "VSTATUS"),
1359            #[cfg(any(freebsdlike, target_os = "illumos"))]
1360            Self::VERASE2 => write!(f, "VERASE2"),
1361
1362            _ => write!(f, "unknown"),
1363        }
1364    }
1365}
1366
1367/// `TCSA*` values for use with [`tcsetattr`].
1368///
1369/// [`tcsetattr`]: crate::termios::tcsetattr
1370#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1371#[repr(u32)]
1372pub enum OptionalActions {
1373    /// `TCSANOW`—Make the change immediately.
1374    #[doc(alias = "TCSANOW")]
1375    Now = c::TCSANOW as u32,
1376
1377    /// `TCSADRAIN`—Make the change after all output has been transmitted.
1378    #[doc(alias = "TCSADRAIN")]
1379    Drain = c::TCSADRAIN as u32,
1380
1381    /// `TCSAFLUSH`—Discard any pending input and then make the change
1382    /// after all output has been transmitted.
1383    #[doc(alias = "TCSAFLUSH")]
1384    Flush = c::TCSAFLUSH as u32,
1385}
1386
1387/// `TC*` values for use with [`tcflush`].
1388///
1389/// [`tcflush`]: crate::termios::tcflush
1390#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1391#[repr(u32)]
1392pub enum QueueSelector {
1393    /// `TCIFLUSH`—Flush data received but not read.
1394    #[doc(alias = "TCIFLUSH")]
1395    IFlush = c::TCIFLUSH as u32,
1396
1397    /// `TCOFLUSH`—Flush data written but not transmitted.
1398    #[doc(alias = "TCOFLUSH")]
1399    OFlush = c::TCOFLUSH as u32,
1400
1401    /// `TCIOFLUSH`—`IFlush` and `OFlush` combined.
1402    #[doc(alias = "TCIOFLUSH")]
1403    IOFlush = c::TCIOFLUSH as u32,
1404}
1405
1406/// `TC*` values for use with [`tcflow`].
1407///
1408/// [`tcflow`]: crate::termios::tcflow
1409#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
1410#[repr(u32)]
1411pub enum Action {
1412    /// `TCOOFF`—Suspend output.
1413    #[doc(alias = "TCOOFF")]
1414    OOff = c::TCOOFF as u32,
1415
1416    /// `TCOON`—Restart suspended output.
1417    #[doc(alias = "TCOON")]
1418    OOn = c::TCOON as u32,
1419
1420    /// `TCIOFF`—Transmits a STOP byte.
1421    #[doc(alias = "TCIOFF")]
1422    IOff = c::TCIOFF as u32,
1423
1424    /// `TCION`—Transmits a START byte.
1425    #[doc(alias = "TCION")]
1426    IOn = c::TCION as u32,
1427}
1428
1429/// `struct winsize` for use with [`tcgetwinsize`].
1430///
1431/// [`tcgetwinsize`]: crate::termios::tcgetwinsize
1432#[doc(alias = "winsize")]
1433#[repr(C)]
1434#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
1435#[allow(missing_docs)]
1436pub struct Winsize {
1437    /// The number of rows the terminal has.
1438    pub ws_row: u16,
1439    /// The number of columns the terminal has.
1440    pub ws_col: u16,
1441
1442    pub ws_xpixel: u16,
1443    pub ws_ypixel: u16,
1444}
1445
1446#[cfg(test)]
1447mod tests {
1448    use super::*;
1449
1450    #[test]
1451    fn termios_layouts() {
1452        check_renamed_type!(InputModes, tcflag_t);
1453        check_renamed_type!(OutputModes, tcflag_t);
1454        check_renamed_type!(ControlModes, tcflag_t);
1455        check_renamed_type!(LocalModes, tcflag_t);
1456        assert_eq_size!(u8, libc::cc_t);
1457        assert_eq_size!(types::tcflag_t, libc::tcflag_t);
1458
1459        check_renamed_struct!(Winsize, winsize, ws_row, ws_col, ws_xpixel, ws_ypixel);
1460
1461        // On platforms with a termios/termios2 split, check `termios`.
1462        #[cfg(linux_raw)]
1463        {
1464            check_renamed_type!(Termios, termios2);
1465            check_renamed_struct_renamed_field!(Termios, termios2, input_modes, c_iflag);
1466            check_renamed_struct_renamed_field!(Termios, termios2, output_modes, c_oflag);
1467            check_renamed_struct_renamed_field!(Termios, termios2, control_modes, c_cflag);
1468            check_renamed_struct_renamed_field!(Termios, termios2, local_modes, c_lflag);
1469            check_renamed_struct_renamed_field!(Termios, termios2, line_discipline, c_line);
1470            check_renamed_struct_renamed_field!(Termios, termios2, special_codes, c_cc);
1471            check_renamed_struct_renamed_field!(Termios, termios2, input_speed, c_ispeed);
1472            check_renamed_struct_renamed_field!(Termios, termios2, output_speed, c_ospeed);
1473
1474            // We assume that `termios` has the same layout as `termios2` minus the
1475            // `c_ispeed` and `c_ospeed` fields.
1476            check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag);
1477            check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag);
1478            check_renamed_struct_renamed_field!(Termios, termios, control_modes, c_cflag);
1479            check_renamed_struct_renamed_field!(Termios, termios, local_modes, c_lflag);
1480            check_renamed_struct_renamed_field!(Termios, termios, special_codes, c_cc);
1481
1482            // On everything except PowerPC, `termios` matches `termios2` except
1483            // for the addition of `c_ispeed` and `c_ospeed`.
1484            #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))]
1485            const_assert_eq!(
1486                memoffset::offset_of!(Termios, input_speed),
1487                core::mem::size_of::<c::termios>()
1488            );
1489
1490            // On PowerPC, `termios2` is `termios`.
1491            #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
1492            assert_eq_size!(c::termios2, c::termios);
1493        }
1494
1495        #[cfg(not(linux_raw))]
1496        {
1497            // On MIPS, SPARC, and Android, the libc lacks the ospeed and ispeed
1498            // fields.
1499            #[cfg(all(
1500                not(all(
1501                    target_env = "gnu",
1502                    any(
1503                        target_arch = "mips",
1504                        target_arch = "mips32r6",
1505                        target_arch = "mips64",
1506                        target_arch = "mips64r6",
1507                        target_arch = "sparc",
1508                        target_arch = "sparc64"
1509                    )
1510                )),
1511                not(all(libc, target_os = "android"))
1512            ))]
1513            check_renamed_type!(Termios, termios);
1514            #[cfg(not(all(
1515                not(all(
1516                    target_env = "gnu",
1517                    any(
1518                        target_arch = "mips",
1519                        target_arch = "mips32r6",
1520                        target_arch = "mips64",
1521                        target_arch = "mips64r6",
1522                        target_arch = "sparc",
1523                        target_arch = "sparc64"
1524                    )
1525                )),
1526                not(all(libc, target_os = "android"))
1527            )))]
1528            const_assert!(core::mem::size_of::<Termios>() >= core::mem::size_of::<c::termios>());
1529
1530            check_renamed_struct_renamed_field!(Termios, termios, input_modes, c_iflag);
1531            check_renamed_struct_renamed_field!(Termios, termios, output_modes, c_oflag);
1532            check_renamed_struct_renamed_field!(Termios, termios, control_modes, c_cflag);
1533            check_renamed_struct_renamed_field!(Termios, termios, local_modes, c_lflag);
1534            #[cfg(any(
1535                linux_like,
1536                target_env = "newlib",
1537                target_os = "fuchsia",
1538                target_os = "haiku",
1539                target_os = "redox"
1540            ))]
1541            check_renamed_struct_renamed_field!(Termios, termios, line_discipline, c_line);
1542            check_renamed_struct_renamed_field!(Termios, termios, special_codes, c_cc);
1543            #[cfg(not(any(
1544                linux_kernel,
1545                solarish,
1546                target_os = "emscripten",
1547                target_os = "fuchsia"
1548            )))]
1549            {
1550                check_renamed_struct_renamed_field!(Termios, termios, input_speed, c_ispeed);
1551                check_renamed_struct_renamed_field!(Termios, termios, output_speed, c_ospeed);
1552            }
1553            #[cfg(any(target_env = "musl", target_os = "fuchsia"))]
1554            {
1555                check_renamed_struct_renamed_field!(Termios, termios, input_speed, __c_ispeed);
1556                check_renamed_struct_renamed_field!(Termios, termios, output_speed, __c_ospeed);
1557            }
1558        }
1559
1560        check_renamed_type!(OptionalActions, c_int);
1561        check_renamed_type!(QueueSelector, c_int);
1562        check_renamed_type!(Action, c_int);
1563    }
1564
1565    #[test]
1566    #[cfg(not(any(
1567        solarish,
1568        target_os = "cygwin",
1569        target_os = "emscripten",
1570        target_os = "haiku",
1571        target_os = "redox",
1572    )))]
1573    fn termios_legacy() {
1574        // Check that our doc aliases above are correct.
1575        const_assert_eq!(c::EXTA, c::B19200);
1576        const_assert_eq!(c::EXTB, c::B38400);
1577    }
1578
1579    #[cfg(bsd)]
1580    #[test]
1581    fn termios_bsd() {
1582        // On BSD platforms we can assume that the `B*` constants have their
1583        // arbitrary integer speed value. Confirm this.
1584        const_assert_eq!(c::B0, 0);
1585        const_assert_eq!(c::B50, 50);
1586        const_assert_eq!(c::B19200, 19200);
1587        const_assert_eq!(c::B38400, 38400);
1588    }
1589
1590    #[test]
1591    #[cfg(not(bsd))]
1592    fn termios_speed_encoding() {
1593        assert_eq!(speed::encode(0), Some(c::B0));
1594        assert_eq!(speed::encode(50), Some(c::B50));
1595        assert_eq!(speed::encode(19200), Some(c::B19200));
1596        assert_eq!(speed::encode(38400), Some(c::B38400));
1597        assert_eq!(speed::encode(1), None);
1598        assert_eq!(speed::encode(!0), None);
1599
1600        #[cfg(not(linux_kernel))]
1601        {
1602            assert_eq!(speed::decode(c::B0), Some(0));
1603            assert_eq!(speed::decode(c::B50), Some(50));
1604            assert_eq!(speed::decode(c::B19200), Some(19200));
1605            assert_eq!(speed::decode(c::B38400), Some(38400));
1606        }
1607    }
1608
1609    #[cfg(linux_kernel)]
1610    #[test]
1611    fn termios_ioctl_contiguity() {
1612        // When using `termios2`, we assume that we can add the optional actions
1613        // value to the ioctl request code. Test this assumption.
1614
1615        const_assert_eq!(c::TCSETS2, c::TCSETS2 + 0);
1616        const_assert_eq!(c::TCSETSW2, c::TCSETS2 + 1);
1617        const_assert_eq!(c::TCSETSF2, c::TCSETS2 + 2);
1618
1619        const_assert_eq!(c::TCSANOW - c::TCSANOW, 0);
1620        const_assert_eq!(c::TCSADRAIN - c::TCSANOW, 1);
1621        const_assert_eq!(c::TCSAFLUSH - c::TCSANOW, 2);
1622
1623        // MIPS is different here.
1624        #[cfg(any(
1625            target_arch = "mips",
1626            target_arch = "mips32r6",
1627            target_arch = "mips64",
1628            target_arch = "mips64r6"
1629        ))]
1630        {
1631            assert_eq!(i128::from(c::TCSANOW) - i128::from(c::TCSETS), 0);
1632            assert_eq!(i128::from(c::TCSADRAIN) - i128::from(c::TCSETS), 1);
1633            assert_eq!(i128::from(c::TCSAFLUSH) - i128::from(c::TCSETS), 2);
1634        }
1635        #[cfg(not(any(
1636            target_arch = "mips",
1637            target_arch = "mips32r6",
1638            target_arch = "mips64",
1639            target_arch = "mips64r6"
1640        )))]
1641        {
1642            const_assert_eq!(c::TCSANOW, 0);
1643            const_assert_eq!(c::TCSADRAIN, 1);
1644            const_assert_eq!(c::TCSAFLUSH, 2);
1645        }
1646    }
1647
1648    #[cfg(linux_kernel)]
1649    #[test]
1650    fn termios_cibaud() {
1651        // Test an assumption.
1652        const_assert_eq!(c::CIBAUD, c::CBAUD << c::IBSHIFT);
1653    }
1654}