use {
    crate::{
        backend::{AxisSource, ButtonState, InputEvent, KeyState, ScrollAxis},
        backends::metal::MetalBackend,
        fixed::Fixed,
        ifs::wl_seat::tablet::{
            PadButtonState, TabletRingEventSource, TabletStripEventSource, TabletTool2dChange,
            TabletToolCapability, TabletToolChanges, TabletToolId, TabletToolInit,
            TabletToolPositionChange, TabletToolType, TabletToolWheelChange, ToolButtonState,
        },
        libinput::{
            consts::{
                LIBINPUT_BUTTON_STATE_PRESSED, LIBINPUT_BUTTON_STATE_RELEASED,
                LIBINPUT_KEY_STATE_PRESSED, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
                LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, LIBINPUT_SWITCH_LID,
                LIBINPUT_SWITCH_STATE_OFF, LIBINPUT_SWITCH_STATE_ON, LIBINPUT_SWITCH_TABLET_MODE,
                LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER, LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER,
                LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN, LIBINPUT_TABLET_TOOL_TIP_DOWN,
                LIBINPUT_TABLET_TOOL_TIP_UP, LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH,
                LIBINPUT_TABLET_TOOL_TYPE_BRUSH, LIBINPUT_TABLET_TOOL_TYPE_ERASER,
                LIBINPUT_TABLET_TOOL_TYPE_LENS, LIBINPUT_TABLET_TOOL_TYPE_MOUSE,
                LIBINPUT_TABLET_TOOL_TYPE_PEN, LIBINPUT_TABLET_TOOL_TYPE_PENCIL,
            },
            event::{LibInputEvent, LibInputEventTabletTool},
        },
        utils::{bitflags::BitflagsExt, errorfmt::ErrorFmt},
    },
    jay_config::input::SwitchEvent,
    std::rc::Rc,
    uapi::c,
};

macro_rules! unpack {
    ($slf:expr, $ev:expr) => {{
        let slot = match $ev.device().slot() {
            Some(s) => s,
            _ => return,
        };
        let data = match $slf
            .device_holder
            .input_devices
            .borrow_mut()
            .get(slot)
            .cloned()
            .and_then(|v| v)
        {
            Some(d) => d,
            _ => return,
        };
        data
    }};
    ($slf:expr, $ev:expr, $conv:ident) => {{
        let event = match $ev.$conv() {
            Some(e) => e,
            _ => return,
        };
        let data = unpack!($slf, $ev);
        (event, data)
    }};
}

impl MetalBackend {
    pub async fn handle_libinput_events(self: Rc<Self>) {
        loop {
            match self.state.ring.readable(&self.libinput_fd).await {
                Err(e) => {
                    log::error!(
                        "Cannot wait for libinput fd to become readable: {}",
                        ErrorFmt(e)
                    );
                    break;
                }
                Ok(n) if n.intersects(c::POLLERR | c::POLLHUP) => {
                    log::error!("libinput fd is in an error state");
                    break;
                }
                _ => {}
            }
            if let Err(e) = self.libinput.dispatch() {
                log::error!("Could not dispatch libinput events: {}", ErrorFmt(e));
                break;
            }
            while let Some(event) = self.libinput.event() {
                self.handle_event(event);
            }
        }
        log::error!("Libinput task exited. Future input events will be ignored.");
    }

    fn handle_event(self: &Rc<Self>, event: LibInputEvent) {
        use crate::libinput::consts as c;

        match event.ty() {
            c::LIBINPUT_EVENT_DEVICE_ADDED => self.handle_device_added(event),
            c::LIBINPUT_EVENT_DEVICE_REMOVED => self.handle_li_device_removed(event),
            c::LIBINPUT_EVENT_KEYBOARD_KEY => self.handle_keyboard_key(event),
            c::LIBINPUT_EVENT_POINTER_MOTION => self.handle_pointer_motion(event),
            c::LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE => self.handle_pointer_motion_absolute(event),
            c::LIBINPUT_EVENT_POINTER_BUTTON => self.handle_pointer_button(event),
            c::LIBINPUT_EVENT_POINTER_SCROLL_WHEEL => {
                self.handle_pointer_axis(event, AxisSource::Wheel)
            }
            c::LIBINPUT_EVENT_POINTER_SCROLL_FINGER => {
                self.handle_pointer_axis(event, AxisSource::Finger)
            }
            c::LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS => {
                self.handle_pointer_axis(event, AxisSource::Continuous)
            }
            c::LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN => self.handle_gesture_swipe_begin(event),
            c::LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE => self.handle_gesture_swipe_update(event),
            c::LIBINPUT_EVENT_GESTURE_SWIPE_END => self.handle_gesture_swipe_end(event),
            c::LIBINPUT_EVENT_GESTURE_PINCH_BEGIN => self.handle_gesture_pinch_begin(event),
            c::LIBINPUT_EVENT_GESTURE_PINCH_UPDATE => self.handle_gesture_pinch_update(event),
            c::LIBINPUT_EVENT_GESTURE_PINCH_END => self.handle_gesture_pinch_end(event),
            c::LIBINPUT_EVENT_GESTURE_HOLD_BEGIN => self.handle_gesture_hold_begin(event),
            c::LIBINPUT_EVENT_GESTURE_HOLD_END => self.handle_gesture_hold_end(event),
            c::LIBINPUT_EVENT_SWITCH_TOGGLE => self.handle_switch_toggle(event),
            c::LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY => self.handle_tablet_tool_proximity(event),
            c::LIBINPUT_EVENT_TABLET_TOOL_AXIS => self.handle_tablet_tool_axis(event),
            c::LIBINPUT_EVENT_TABLET_TOOL_BUTTON => self.handle_tablet_tool_button(event),
            c::LIBINPUT_EVENT_TABLET_TOOL_TIP => self.handle_tablet_tool_tip(event),
            c::LIBINPUT_EVENT_TABLET_PAD_BUTTON => self.handle_tablet_pad_button(event),
            c::LIBINPUT_EVENT_TABLET_PAD_RING => self.handle_tablet_pad_ring(event),
            c::LIBINPUT_EVENT_TABLET_PAD_STRIP => self.handle_tablet_pad_strip(event),
            c::LIBINPUT_EVENT_TABLET_PAD_DIAL => self.handle_tablet_pad_dial(event),
            c::LIBINPUT_EVENT_TOUCH_DOWN => self.handle_touch_down(event),
            c::LIBINPUT_EVENT_TOUCH_UP => self.handle_touch_up(event),
            c::LIBINPUT_EVENT_TOUCH_MOTION => self.handle_touch_motion(event),
            c::LIBINPUT_EVENT_TOUCH_CANCEL => self.handle_touch_cancel(event),
            c::LIBINPUT_EVENT_TOUCH_FRAME => self.handle_touch_frame(event),
            _ => {}
        }
    }

    fn handle_device_added(self: &Rc<Self>, _event: LibInputEvent) {
        // let dev = unpack!(self, event);
    }

    fn handle_li_device_removed(self: &Rc<Self>, event: LibInputEvent) {
        let dev = unpack!(self, event);
        dev.inputdev.set(None);
        event.device().unset_slot();
    }

    fn handle_keyboard_key(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, keyboard_event);
        let state = if event.key_state() == LIBINPUT_KEY_STATE_PRESSED {
            if dev.pressed_keys.insert(event.key(), ()).is_some() {
                return;
            }
            KeyState::Pressed
        } else {
            if dev.pressed_keys.remove(&event.key()).is_none() {
                return;
            }
            KeyState::Released
        };
        dev.event(InputEvent::Key {
            time_usec: event.time_usec(),
            key: event.key(),
            state,
        });
    }

    fn handle_pointer_axis(self: &Rc<Self>, event: LibInputEvent, source: AxisSource) {
        let (event, dev) = unpack!(self, event, pointer_event);
        let axes = [
            (
                LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL,
                ScrollAxis::Horizontal,
            ),
            (LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL, ScrollAxis::Vertical),
        ];
        dev.event(InputEvent::AxisSource { source });
        for (pointer_axis, axis) in axes {
            if !event.has_axis(pointer_axis) {
                continue;
            }
            let scroll = match source {
                AxisSource::Wheel => event.scroll_value_v120(pointer_axis),
                _ => event.scroll_value(pointer_axis),
            };
            let ie = if scroll == 0.0 {
                InputEvent::AxisStop { axis }
            } else if source == AxisSource::Wheel {
                InputEvent::Axis120 {
                    dist: scroll as _,
                    axis,
                    inverted: dev
                        .effective
                        .natural_scrolling_enabled
                        .get()
                        .unwrap_or_default(),
                }
            } else {
                InputEvent::AxisPx {
                    dist: Fixed::from_f64(scroll),
                    axis,
                    inverted: dev
                        .effective
                        .natural_scrolling_enabled
                        .get()
                        .unwrap_or_default(),
                }
            };
            dev.event(ie);
        }
        dev.event(InputEvent::AxisFrame {
            time_usec: event.time_usec(),
        });
    }

    fn handle_pointer_button(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, pointer_event);
        let state = if event.button_state() == LIBINPUT_BUTTON_STATE_PRESSED {
            if dev.pressed_buttons.insert(event.button(), ()).is_some() {
                return;
            }
            ButtonState::Pressed
        } else {
            if dev.pressed_buttons.remove(&event.button()).is_none() {
                return;
            }
            ButtonState::Released
        };
        dev.event(InputEvent::Button {
            time_usec: event.time_usec(),
            button: event.button(),
            state,
        });
    }

    fn handle_pointer_motion(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, pointer_event);
        let mut dx = event.dx();
        let mut dy = event.dy();
        if let Some(matrix) = dev.transform_matrix.get() {
            (dx, dy) = (
                matrix[0][0] * dx + matrix[0][1] * dy,
                matrix[1][0] * dx + matrix[1][1] * dy,
            );
        }
        dev.event(InputEvent::Motion {
            time_usec: event.time_usec(),
            dx: Fixed::from_f64(dx),
            dy: Fixed::from_f64(dy),
            dx_unaccelerated: Fixed::from_f64(event.dx_unaccelerated()),
            dy_unaccelerated: Fixed::from_f64(event.dy_unaccelerated()),
        });
    }

    fn handle_pointer_motion_absolute(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, pointer_event);
        dev.event(InputEvent::MotionAbsolute {
            time_usec: event.time_usec(),
            x_normed: event.x_transformed(1) as f32,
            y_normed: event.y_transformed(1) as f32,
        });
    }

    fn handle_gesture_swipe_begin(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::SwipeBegin {
            time_usec: event.time_usec(),
            finger_count: event.finger_count(),
        });
    }

    fn handle_gesture_swipe_update(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::SwipeUpdate {
            time_usec: event.time_usec(),
            dx: Fixed::from_f64(event.dx()),
            dy: Fixed::from_f64(event.dy()),
            dx_unaccelerated: Fixed::from_f64(event.dx_unaccelerated()),
            dy_unaccelerated: Fixed::from_f64(event.dy_unaccelerated()),
        });
    }

    fn handle_gesture_swipe_end(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::SwipeEnd {
            time_usec: event.time_usec(),
            cancelled: event.cancelled(),
        });
    }

    fn handle_gesture_pinch_begin(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::PinchBegin {
            time_usec: event.time_usec(),
            finger_count: event.finger_count(),
        });
    }

    fn handle_gesture_pinch_update(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::PinchUpdate {
            time_usec: event.time_usec(),
            dx: Fixed::from_f64(event.dx()),
            dy: Fixed::from_f64(event.dy()),
            dx_unaccelerated: Fixed::from_f64(event.dx_unaccelerated()),
            dy_unaccelerated: Fixed::from_f64(event.dy_unaccelerated()),
            scale: Fixed::from_f64(event.scale()),
            rotation: Fixed::from_f64(event.angle_delta()),
        });
    }

    fn handle_gesture_pinch_end(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::PinchEnd {
            time_usec: event.time_usec(),
            cancelled: event.cancelled(),
        });
    }

    fn handle_gesture_hold_begin(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::HoldBegin {
            time_usec: event.time_usec(),
            finger_count: event.finger_count(),
        });
    }

    fn handle_gesture_hold_end(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, gesture_event);
        dev.event(InputEvent::HoldEnd {
            time_usec: event.time_usec(),
            cancelled: event.cancelled(),
        });
    }

    fn handle_switch_toggle(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, switch_event);
        let switch_event = match (event.switch(), event.switch_state()) {
            (LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_OFF) => SwitchEvent::LidOpened,
            (LIBINPUT_SWITCH_LID, LIBINPUT_SWITCH_STATE_ON) => SwitchEvent::LidClosed,
            (LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_OFF) => {
                SwitchEvent::ConvertedToLaptop
            }
            (LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON) => {
                SwitchEvent::ConvertedToTablet
            }
            _ => return,
        };
        dev.event(InputEvent::SwitchEvent {
            time_usec: event.time_usec(),
            event: switch_event,
        });
    }

    fn get_tool_id(&self, event: &LibInputEventTabletTool) -> TabletToolId {
        let tool = event.tool();
        let mut user_data = tool.user_data();
        if user_data == 0 {
            user_data = self.state.tablet_tool_ids.next().raw();
            tool.set_user_data(user_data);
        }
        TabletToolId::from_raw(user_data)
    }

    fn build_tablet_tool_changed(
        &self,
        event: &LibInputEventTabletTool,
        down: Option<bool>,
    ) -> InputEvent {
        let mut changes = Box::<TabletToolChanges>::default();
        changes.down = down;
        if event.x_has_changed() || event.y_has_changed() {
            changes.pos = Some(TabletTool2dChange {
                x: TabletToolPositionChange {
                    x: event.x_transformed(1),
                    dx: event.dx(),
                },
                y: TabletToolPositionChange {
                    x: event.y_transformed(1),
                    dx: event.dy(),
                },
            })
        }
        if event.pressure_has_changed() {
            changes.pressure = Some(event.pressure());
        }
        if event.distance_has_changed() {
            changes.distance = Some(event.distance());
        }
        if event.tilt_x_has_changed() || event.tilt_y_has_changed() {
            changes.tilt = Some(TabletTool2dChange {
                x: event.tilt_x(),
                y: event.tilt_y(),
            });
        }
        if event.rotation_has_changed() {
            changes.rotation = Some(event.rotation());
        }
        if event.slider_has_changed() {
            changes.slider = Some(event.slider_position());
        }
        if event.wheel_has_changed() {
            changes.wheel = Some(TabletToolWheelChange {
                degrees: event.wheel_delta(),
                clicks: event.wheel_delta_discrete(),
            });
        }
        InputEvent::TabletToolChanged {
            time_usec: event.time_usec(),
            id: self.get_tool_id(event),
            changes,
        }
    }

    fn handle_tablet_tool_proximity(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_tool_event);
        let id = self.get_tool_id(&event);
        if event.proximity_state() == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN {
            let Some(tablet_id) = dev.tablet_id.get() else {
                return;
            };
            let tool = event.tool();
            dev.event(InputEvent::TabletToolAdded {
                time_usec: event.time_usec(),
                init: Box::new(TabletToolInit {
                    tablet_id,
                    id,
                    type_: match tool.type_() {
                        LIBINPUT_TABLET_TOOL_TYPE_PEN => TabletToolType::Pen,
                        LIBINPUT_TABLET_TOOL_TYPE_ERASER => TabletToolType::Eraser,
                        LIBINPUT_TABLET_TOOL_TYPE_BRUSH => TabletToolType::Brush,
                        LIBINPUT_TABLET_TOOL_TYPE_PENCIL => TabletToolType::Pencil,
                        LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH => TabletToolType::Airbrush,
                        LIBINPUT_TABLET_TOOL_TYPE_MOUSE => TabletToolType::Mouse,
                        LIBINPUT_TABLET_TOOL_TYPE_LENS => TabletToolType::Lens,
                        _ => return,
                    },
                    hardware_serial: tool.serial(),
                    hardware_id_wacom: tool.tool_id(),
                    capabilities: {
                        let mut caps = vec![];
                        macro_rules! add_cap {
                            ($f:ident, $cap:ident) => {
                                if tool.$f() {
                                    caps.push(TabletToolCapability::$cap);
                                }
                            };
                        }
                        add_cap!(has_tilt, Tilt);
                        add_cap!(has_pressure, Pressure);
                        add_cap!(has_distance, Distance);
                        add_cap!(has_rotation, Rotation);
                        add_cap!(has_slider, Slider);
                        add_cap!(has_wheel, Wheel);
                        caps
                    },
                }),
            });
            dev.event(self.build_tablet_tool_changed(&event, None));
        } else {
            dev.event(InputEvent::TabletToolRemoved {
                time_usec: event.time_usec(),
                id,
            });
        }
    }

    fn handle_tablet_tool_tip(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_tool_event);
        let down = match event.tip_state() {
            LIBINPUT_TABLET_TOOL_TIP_UP => false,
            LIBINPUT_TABLET_TOOL_TIP_DOWN => true,
            _ => return,
        };
        dev.event(self.build_tablet_tool_changed(&event, Some(down)));
    }

    fn handle_tablet_tool_axis(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_tool_event);
        dev.event(self.build_tablet_tool_changed(&event, None));
    }

    fn handle_tablet_tool_button(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_tool_event);
        dev.event(InputEvent::TabletToolButton {
            time_usec: event.time_usec(),
            id: self.get_tool_id(&event),
            button: event.button(),
            state: match event.button_state() {
                LIBINPUT_BUTTON_STATE_RELEASED => ToolButtonState::Released,
                LIBINPUT_BUTTON_STATE_PRESSED => ToolButtonState::Pressed,
                _ => return,
            },
        });
    }

    fn handle_tablet_pad_button(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_pad_event);
        let id = match dev.tablet_pad_id.get() {
            None => return,
            Some(id) => id,
        };
        let state = match event.button_state() {
            LIBINPUT_BUTTON_STATE_RELEASED => PadButtonState::Released,
            LIBINPUT_BUTTON_STATE_PRESSED => PadButtonState::Pressed,
            _ => return,
        };
        dev.event(InputEvent::TabletPadModeSwitch {
            time_usec: event.time_usec(),
            pad: id,
            group: event.mode_group().index(),
            mode: event.mode(),
        });
        dev.event(InputEvent::TabletPadButton {
            time_usec: event.time_usec(),
            id,
            button: event.button_number(),
            state,
        });
    }

    fn handle_tablet_pad_ring(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_pad_event);
        dev.event(InputEvent::TabletPadRing {
            time_usec: event.time_usec(),
            pad: match dev.tablet_pad_id.get() {
                None => return,
                Some(id) => id,
            },
            ring: event.ring_number(),
            source: match event.ring_source() {
                LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER => Some(TabletRingEventSource::Finger),
                _ => None,
            },
            angle: match event.ring_position() {
                -1.0 => None,
                n => Some(n),
            },
        });
    }

    fn handle_tablet_pad_strip(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_pad_event);
        dev.event(InputEvent::TabletPadStrip {
            time_usec: event.time_usec(),
            pad: match dev.tablet_pad_id.get() {
                None => return,
                Some(id) => id,
            },
            strip: event.strip_number(),
            source: match event.strip_source() {
                LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER => Some(TabletStripEventSource::Finger),
                _ => None,
            },
            position: match event.strip_position() {
                -1.0 => None,
                n => Some(n),
            },
        });
    }

    fn handle_tablet_pad_dial(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, tablet_pad_event);
        let Some(dial) = event.dial_number() else {
            return;
        };
        let Some(value120) = event.dial_delta_v120() else {
            return;
        };
        dev.event(InputEvent::TabletPadDial {
            time_usec: event.time_usec(),
            pad: match dev.tablet_pad_id.get() {
                None => return,
                Some(id) => id,
            },
            dial,
            value120: value120 as _,
        });
    }

    fn handle_touch_down(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, touch_event);
        dev.event(InputEvent::TouchDown {
            time_usec: event.time_usec(),
            id: event.seat_slot(),
            x_normed: Fixed::from_f64(event.x_transformed(1)),
            y_normed: Fixed::from_f64(event.y_transformed(1)),
        })
    }

    fn handle_touch_up(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, touch_event);
        dev.event(InputEvent::TouchUp {
            time_usec: event.time_usec(),
            id: event.seat_slot(),
        })
    }

    fn handle_touch_motion(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, touch_event);
        dev.event(InputEvent::TouchMotion {
            time_usec: event.time_usec(),
            id: event.seat_slot(),
            x_normed: Fixed::from_f64(event.x_transformed(1)),
            y_normed: Fixed::from_f64(event.y_transformed(1)),
        })
    }

    fn handle_touch_cancel(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, touch_event);
        dev.event(InputEvent::TouchCancel {
            time_usec: event.time_usec(),
            id: event.seat_slot(),
        })
    }

    fn handle_touch_frame(self: &Rc<Self>, event: LibInputEvent) {
        let (event, dev) = unpack!(self, event, touch_event);
        dev.event(InputEvent::TouchFrame {
            time_usec: event.time_usec(),
        })
    }
}
