[go: up one dir, main page]

evdev 0.13.1

evdev interface for Linux
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
//! Linux event device handling.
//!
//! The Linux kernel's "evdev" subsystem exposes input devices to userspace in a generic,
//! consistent way. I'll try to explain the device model as completely as possible. The upstream
//! kernel documentation is split across two files:
//!
//! - <https://www.kernel.org/doc/Documentation/input/event-codes.txt>
//! - <https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt>
//!
//! The `evdev` kernel system exposes input devices as character devices in `/dev/input`,
//! typically `/dev/input/eventX` where `X` is an integer.
//! Userspace applications can use `ioctl` system calls to interact with these devices.
//! Libraries such as this one abstract away the low level calls to provide a high level
//! interface.
//!
//! Applications can interact with `uinput` by writing to `/dev/uinput` to create virtual
//! devices and send events to the virtual devices.
//! Virtual devices are created in `/sys/devices/virtual/input`.
//!
//! # Devices
//!
//! Devices can be opened directly via their path:
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use evdev::Device;
//! let device = Device::open("/dev/input/event0")?;
//! # Ok(())
//! # }
//! ```
//! This approach requires the calling process to have the appropriate privileges to
//! open the device node (typically this requires running as root user).
//! Alternatively a device can be created from an already open file descriptor. This approach
//! is useful where the file descriptor is provided by an external privileged process
//! (e.g. systemd's logind):
//!
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use evdev::Device;
//! use std::fs::File;
//! use std::os::fd::OwnedFd;
//! let f = File::open("/dev/input/event0")?;
//! let fd = OwnedFd::from(f);
//! let device = Device::from_fd(fd)?;
//! # Ok(())
//! # }
//! ```
//!
//! # Input Events
//!
//! Devices emit events, represented by the [`InputEvent`] struct.
//! A input event has three main fields: event [type](InputEvent::event_type), [code](InputEvent::code)
//! and [value](InputEvent::value)
//!
//! The kernel documentation specifies different event types, reperesented by the [`EventType`] struct.
//! Each device can support a subset of those types. See [`Device::supported_events()`].
//! For each of the known event types there is a new-type wrapper around [`InputEvent`]  
//! in [`event_variants`] see the module documenation for more info about those.
//!
//! For most event types the kernel documentation also specifies a set of codes, represented by a new-type
//! e.g. [`KeyCode`]. The individual codes of a [`EventType`] that a device supports can be retrieved
//! through the `Device::supported_*()` methods, e.g. [`Device::supported_keys()`]:
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use evdev::{Device, KeyCode};
//! let device = Device::open("/dev/input/event0")?;
//! // check if the device has an ENTER key
//! if device.supported_keys().map_or(false, |keys| keys.contains(KeyCode::KEY_ENTER)) {
//!     println!("are you prepared to ENTER the world of evdev?");
//! } else {
//!     println!(":(");
//! }
//! # Ok(())
//! # }
//! ```
//! A [`InputEvent`] with a type of [`EventType::KEY`] a code of [`KeyCode::KEY_ENTER`] and a
//! value of 1 is emitted when the Enter key is pressed.
//!
//! All events (even single events) are sent in batches followed by a synchronization event:
//! `EV_SYN / SYN_REPORT / 0`.
//! Events are grouped into batches based on if they are related and occur simultaneously,
//! for example movement of a mouse triggers a movement event for the `X` and `Y` axes
//! separately in a batch of 2 events.
//!
//! The evdev crate exposes functions to query the current state of a device from the kernel, as
//! well as a function that can be called continuously to provide an iterator over update events
//! as they arrive.
//!
//! ## Matching Events
//!
//! When reading from an input Device it is often useful to check which type/code or value
//! the event has. This library provides the [`EventSummary`] enum which can be used to
//! match specific events. Calling [`InputEvent::destructure`] will return that enum.
//!
//! ```no_run
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! use evdev::*;
//! let mut device = Device::open("/dev/input/event0")?;
//! loop {
//!     for event in device.fetch_events().unwrap(){
//!         match event.destructure(){
//!             EventSummary::Key(ev, KeyCode::KEY_A, 1) => {
//!                 println!("Key 'a' was pressed, got event: {:?}", ev);
//!             },
//!             EventSummary::Key(_, key_type, 0) => {
//!                 println!("Key {:?} was released", key_type);
//!             },
//!             EventSummary::AbsoluteAxis(_, axis, value) => {
//!                 println!("The Axis {:?} was moved to {}", axis, value);
//!             },
//!             _ => println!("got a different event!")
//!         }
//!     }
//! }
//! # unreachable!()
//! # }
//! ```
//!
//! # Synchronizing versus Raw modes
//!
//! This library can be used in either Raw or Synchronizing modes, which correspond roughly to
//! evdev's `LIBEVDEV_READ_FLAG_NORMAL` and `LIBEVDEV_READ_FLAG_SYNC` modes, respectively.
//! In both modes, calling `fetch_events` and driving the resulting iterator to completion
//! will provide a stream of real-time events from the underlying kernel device state.
//! As the state changes, the kernel will write events into a ring buffer. If the buffer becomes full, the
//! kernel will *drop* events from the ring buffer and leave an event telling userspace that it
//! did so. At this point, if the application were using the events it received to update its
//! internal idea of what state the hardware device is in, it will be wrong: it is missing some
//! events.
//!
//! In synchronous mode, this library tries to ease that pain by removing the corrupted events
//! and injecting fake events as if the device had updated normally. Note that this is best-effort;
//! events can never be recovered once lost. This synchronization comes at a performance cost: each
//! set of input events read from the kernel in turn updates an internal state buffer, and events
//! must be internally held back until the end of each frame. If this latency is unacceptable or
//! for any reason you want to see every event directly, a raw stream reader is also provided.
//!
//! As an example of how synchronization behaves, if a switch is toggled twice there will be two switch events
//! in the buffer. However, if the kernel needs to drop events, when the device goes to synchronize
//! state with the kernel only one (or zero, if the switch is in the same state as it was before
//! the sync) switch events will be visible in the stream.
//!
//! This cache can also be queried. For example, the [`DeviceState::led_vals`] method will tell you which
//! LEDs are currently lit on the device. As calling code consumes each iterator, this state will be
//! updated, and it will be fully re-synchronized with the kernel if the stream drops any events.
//!
//! It is recommended that you dedicate a thread to processing input events, or use epoll or an
//! async runtime with the fd returned by `<Device as AsRawFd>::as_raw_fd` to process events when
//! they are ready.
//!
//! For demonstrations of how to use this library in blocking, nonblocking, and async (tokio) modes,
//! please reference the "examples" directory.

// should really be cfg(target_os = "linux") and maybe also android?
#![cfg(unix)]
// Flag items' docs' with their required feature flags, but only on docsrs so
// that local docs can still be built on stable toolchains.
// As of the time of writing, the stabilization plan is such that:
// - Once stabilized, this attribute should be replaced with #![doc(auto_cfg)]
// - Then in edition 2024, doc(auto_cfg) will become the default and the
//   attribute can be removed entirely
// (see https://github.com/rust-lang/rust/pull/100883#issuecomment-1264470491)
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

// has to be first for its macro
#[macro_use]
mod attribute_set;

mod compat;
mod constants;
mod device_state;
pub mod event_variants;
mod ff;
mod inputid;
pub mod raw_stream;
mod scancodes;
mod sync_stream;
mod sys;
pub mod uinput;

use crate::compat::{input_absinfo, input_event, uinput_abs_setup};
use std::fmt::{self, Display};
use std::io;
use std::os::fd::{AsFd, AsRawFd, OwnedFd};
use std::path::PathBuf;
use std::time::{Duration, SystemTime};

pub use attribute_set::{AttributeSet, AttributeSetRef, EvdevEnum};
pub use constants::*;
pub use device_state::DeviceState;
pub use event_variants::*;
pub use ff::*;
pub use inputid::*;
pub use scancodes::*;
pub use sync_stream::*;

macro_rules! common_trait_impls {
    ($raw:ty, $wrapper:ty) => {
        impl From<$raw> for $wrapper {
            fn from(raw: $raw) -> Self {
                Self(raw)
            }
        }

        impl From<$wrapper> for $raw {
            fn from(wrapper: $wrapper) -> Self {
                wrapper.0
            }
        }

        impl AsRef<$raw> for $wrapper {
            fn as_ref(&self) -> &$raw {
                &self.0
            }
        }
    };
}

const EVENT_BATCH_SIZE: usize = 32;

/// A convenience mapping for matching a [`InputEvent`] while simultaniously checking its kind `(type, code)`
/// and capturing the value
///
/// Note This enum can not enforce that `InputEvent.code() == ` enum variant(code).
/// It is suggested to not construct this enum and instead use [`InputEvent::destructure`] to obtain instances.
#[derive(Debug)]
pub enum EventSummary {
    Synchronization(SynchronizationEvent, SynchronizationCode, i32),
    Key(KeyEvent, KeyCode, i32),
    RelativeAxis(RelativeAxisEvent, RelativeAxisCode, i32),
    AbsoluteAxis(AbsoluteAxisEvent, AbsoluteAxisCode, i32),
    Misc(MiscEvent, MiscCode, i32),
    Switch(SwitchEvent, SwitchCode, i32),
    Led(LedEvent, LedCode, i32),
    Sound(SoundEvent, SoundCode, i32),
    Repeat(RepeatEvent, RepeatCode, i32),
    ForceFeedback(FFEvent, FFEffectCode, i32),
    Power(PowerEvent, PowerCode, i32),
    ForceFeedbackStatus(FFStatusEvent, FFStatusCode, i32),
    UInput(UInputEvent, UInputCode, i32),
    Other(OtherEvent, OtherCode, i32),
}

impl From<InputEvent> for EventSummary {
    fn from(value: InputEvent) -> Self {
        match value.event_type() {
            EventType::SYNCHRONIZATION => SynchronizationEvent::from_event(value).into(),
            EventType::KEY => KeyEvent::from_event(value).into(),
            EventType::RELATIVE => RelativeAxisEvent::from_event(value).into(),
            EventType::ABSOLUTE => AbsoluteAxisEvent::from_event(value).into(),
            EventType::MISC => MiscEvent::from_event(value).into(),
            EventType::SWITCH => SwitchEvent::from_event(value).into(),
            EventType::LED => LedEvent::from_event(value).into(),
            EventType::SOUND => SoundEvent::from_event(value).into(),
            EventType::REPEAT => RepeatEvent::from_event(value).into(),
            EventType::FORCEFEEDBACK => FFEvent::from_event(value).into(),
            EventType::POWER => PowerEvent::from_event(value).into(),
            EventType::FORCEFEEDBACKSTATUS => FFStatusEvent::from_event(value).into(),
            EventType::UINPUT => UInputEvent::from_event(value).into(),
            _ => OtherEvent(value).into(),
        }
    }
}

/// A wrapped `input_absinfo` returned by EVIOCGABS and used with uinput to set up absolute
/// axes
///
/// `input_absinfo` is a struct containing six fields:
/// - `value: s32`
/// - `minimum: s32`
/// - `maximum: s32`
/// - `fuzz: s32`
/// - `flat: s32`
/// - `resolution: s32`
///
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct AbsInfo(input_absinfo);

impl AbsInfo {
    #[inline]
    pub fn value(&self) -> i32 {
        self.0.value
    }
    #[inline]
    pub fn minimum(&self) -> i32 {
        self.0.minimum
    }
    #[inline]
    pub fn maximum(&self) -> i32 {
        self.0.maximum
    }
    #[inline]
    pub fn fuzz(&self) -> i32 {
        self.0.fuzz
    }
    #[inline]
    pub fn flat(&self) -> i32 {
        self.0.flat
    }
    #[inline]
    pub fn resolution(&self) -> i32 {
        self.0.resolution
    }

    /// Creates a new AbsInfo, particurarily useful for uinput
    pub fn new(
        value: i32,
        minimum: i32,
        maximum: i32,
        fuzz: i32,
        flat: i32,
        resolution: i32,
    ) -> Self {
        AbsInfo(input_absinfo {
            value,
            minimum,
            maximum,
            fuzz,
            flat,
            resolution,
        })
    }
}

common_trait_impls!(input_absinfo, AbsInfo);

/// A wrapped `uinput_abs_setup`, used to set up analogue axes with uinput
///
/// `uinput_abs_setup` is a struct containing two fields:
/// - `code: u16`
/// - `absinfo: input_absinfo`
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct UinputAbsSetup(uinput_abs_setup);

impl UinputAbsSetup {
    #[inline]
    pub fn code(&self) -> u16 {
        self.0.code
    }
    #[inline]
    pub fn absinfo(&self) -> AbsInfo {
        AbsInfo(self.0.absinfo)
    }
    /// Creates new UinputAbsSetup
    pub fn new(code: AbsoluteAxisCode, absinfo: AbsInfo) -> Self {
        UinputAbsSetup(uinput_abs_setup {
            code: code.0,
            absinfo: absinfo.0,
        })
    }
}

common_trait_impls!(uinput_abs_setup, UinputAbsSetup);

/// A wrapped `input_event` returned by the input device via the kernel.
///
/// `input_event` is a struct containing four fields:
/// - `time: timeval`
/// - `type_: u16`
/// - `code: u16`
/// - `value: s32`
///
/// The meaning of the "code" and "value" fields will depend on the underlying type of event.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct InputEvent(input_event);
common_trait_impls!(input_event, InputEvent);

impl InputEvent {
    /// Returns the timestamp associated with the event.
    #[inline]
    pub fn timestamp(&self) -> SystemTime {
        timeval_to_systime(&self.0.time)
    }

    /// Returns the type of event this describes, e.g. Key, Switch, etc.
    #[inline]
    pub fn event_type(&self) -> EventType {
        EventType(self.0.type_)
    }

    /// Returns the raw "code" field directly from input_event.
    #[inline]
    pub fn code(&self) -> u16 {
        self.0.code
    }

    /// Returns the raw "value" field directly from input_event.
    ///
    /// For keys and switches the values 0 and 1 map to pressed and not pressed respectively.
    /// For axes, the values depend on the hardware and driver implementation.
    #[inline]
    pub fn value(&self) -> i32 {
        self.0.value
    }

    /// A convenience function to destructure the InputEvent into a [`EventSummary`].
    ///
    /// # Example
    /// ```
    /// use evdev::*;
    /// let event =  InputEvent::new(1, KeyCode::KEY_A.0, 1);
    /// match event.destructure() {
    ///     EventSummary::Key(KeyEvent, KeyCode::KEY_A, 1) => (),
    ///     _=> panic!(),
    /// }
    /// ```
    pub fn destructure(self) -> EventSummary {
        self.into()
    }

    /// Create a new InputEvent. Only really useful for emitting events on virtual devices.
    pub fn new(type_: u16, code: u16, value: i32) -> Self {
        let raw = input_event {
            time: libc::timeval {
                tv_sec: 0,
                tv_usec: 0,
            },
            type_,
            code,
            value,
        };
        Self(raw)
    }

    /// Create a new InputEvent with the time field set to "now" on the system clock.
    ///
    /// Note that this isn't usually necessary simply for emitting events on a virtual device, as
    /// even though [`InputEvent::new`] creates an `input_event` with the time field as zero,
    /// the kernel will update `input_event.time` when it emits the events to any programs reading
    /// the event "file".
    pub fn new_now(type_: u16, code: u16, value: i32) -> Self {
        let raw = input_event {
            time: systime_to_timeval(&SystemTime::now()),
            type_,
            code,
            value,
        };
        Self(raw)
    }
}

impl fmt::Debug for InputEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let summary = self.destructure();
        let code: &dyn fmt::Debug = match &summary {
            EventSummary::Synchronization(_, code, _) => code,
            EventSummary::Key(_, code, _) => code,
            EventSummary::RelativeAxis(_, code, _) => code,
            EventSummary::AbsoluteAxis(_, code, _) => code,
            EventSummary::Misc(_, code, _) => code,
            EventSummary::Switch(_, code, _) => code,
            EventSummary::Led(_, code, _) => code,
            EventSummary::Sound(_, code, _) => code,
            EventSummary::Repeat(_, code, _) => code,
            EventSummary::ForceFeedback(_, code, _) => code,
            EventSummary::Power(_, code, _) => code,
            EventSummary::ForceFeedbackStatus(_, code, _) => code,
            EventSummary::UInput(_, code, _) => code,
            EventSummary::Other(_, code, _) => &code.1,
        };
        f.debug_struct("InputEvent")
            .field("time", &self.timestamp())
            .field("type", &self.event_type())
            .field("code", code)
            .field("value", &self.value())
            .finish()
    }
}

/// Crawls `/dev/input` for evdev devices.
///
/// Will not bubble up any errors in opening devices or traversing the directory. Instead returns
/// an empty iterator or omits the devices that could not be opened.
pub fn enumerate() -> EnumerateDevices {
    EnumerateDevices {
        inner: raw_stream::enumerate(),
    }
}

/// An iterator over currently connected evdev devices.
pub struct EnumerateDevices {
    inner: raw_stream::EnumerateDevices,
}
impl Iterator for EnumerateDevices {
    type Item = (PathBuf, Device);
    fn next(&mut self) -> Option<(PathBuf, Device)> {
        self.inner
            .next()
            .map(|(pb, dev)| (pb, Device::from_raw_device(dev)))
    }
}

/// A safe Rust version of clock_gettime against CLOCK_REALTIME
fn systime_to_timeval(time: &SystemTime) -> libc::timeval {
    let (sign, dur) = match time.duration_since(SystemTime::UNIX_EPOCH) {
        Ok(dur) => (1, dur),
        Err(e) => (-1, e.duration()),
    };

    libc::timeval {
        tv_sec: dur.as_secs() as libc::time_t * sign,
        tv_usec: dur.subsec_micros() as libc::suseconds_t,
    }
}

fn timeval_to_systime(tv: &libc::timeval) -> SystemTime {
    let dur = Duration::new(tv.tv_sec as u64, tv.tv_usec as u32 * 1000);
    if tv.tv_sec >= 0 {
        SystemTime::UNIX_EPOCH + dur
    } else {
        SystemTime::UNIX_EPOCH - dur
    }
}

/// SAFETY: T must not have any padding or otherwise uninitialized bytes inside of it
pub(crate) unsafe fn cast_to_bytes<T: ?Sized>(mem: &T) -> &[u8] {
    std::slice::from_raw_parts(mem as *const T as *const u8, std::mem::size_of_val(mem))
}

/// An error type for the `FromStr` implementation for enum-like types in this crate.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EnumParseError(());

impl Display for EnumParseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "failed to parse Key from string")
    }
}

impl std::error::Error for EnumParseError {}

fn fd_write_all(fd: std::os::fd::BorrowedFd<'_>, mut data: &[u8]) -> nix::Result<()> {
    loop {
        match nix::unistd::write(fd, data) {
            Ok(0) => return Ok(()),
            Ok(n) => data = &data[n..],
            Err(nix::Error::EINTR) => {}
            Err(e) => return Err(e),
        }
    }
}

fn write_events(fd: std::os::fd::BorrowedFd<'_>, events: &[InputEvent]) -> nix::Result<()> {
    let bytes = unsafe { cast_to_bytes(events) };
    fd_write_all(fd, bytes)
}

/// Represents a force feedback effect that has been successfully uploaded to the device for
/// playback.
#[derive(Debug)]
pub struct FFEffect {
    fd: OwnedFd,
    id: u16,
}

impl FFEffect {
    /// Returns the effect ID.
    pub fn id(&self) -> u16 {
        self.id
    }

    /// Plays the force feedback effect with the `count` argument specifying how often the effect
    /// should be played.
    pub fn play(&mut self, count: i32) -> io::Result<()> {
        let events = [*FFEvent::new(FFEffectCode(self.id), count)];
        crate::write_events(self.fd.as_fd(), &events)?;

        Ok(())
    }

    /// Stops playback of the force feedback effect.
    pub fn stop(&mut self) -> io::Result<()> {
        let events = [*FFEvent::new(FFEffectCode(self.id), 0)];
        crate::write_events(self.fd.as_fd(), &events)?;

        Ok(())
    }

    /// Updates the force feedback effect.
    pub fn update(&mut self, data: FFEffectData) -> io::Result<()> {
        let mut effect: sys::ff_effect = data.into();
        effect.id = self.id as i16;

        unsafe { sys::eviocsff(self.fd.as_raw_fd(), &effect)? };

        Ok(())
    }
}

impl Drop for FFEffect {
    fn drop(&mut self) {
        let _ = unsafe { sys::eviocrmff(self.fd.as_raw_fd(), self.id as _) };
    }
}

/// Auto-repeat settings for a device.
#[derive(Debug, Clone)]
#[repr(C)]
pub struct AutoRepeat {
    /// The duration, in milliseconds, that a key needs to be held down before
    /// it begins to auto-repeat.
    pub delay: u32,
    /// The duration, in milliseconds, between auto-repetitions of a held-down key.
    pub period: u32,
}