backend, virtual_keyboard: set the time_nsec field of relevant events

Backends modified: libinput (with microsecond precision), X11 (with
millisecond precision), RDP (with millisecond precision), and Wayland
(with nanosecond precision if the host server supports the
input-timestamps protocol, otherwise millisecond precision); and
virtual-keyboard protocol (with millisecond precision).
This commit is contained in:
random human 2019-06-14 22:24:03 +00:00
parent 79ed410be4
commit e617da0378
No known key found for this signature in database
GPG key ID: 73E5A60444CC77A3
9 changed files with 172 additions and 38 deletions

View file

@ -65,8 +65,9 @@ void handle_keyboard_key(struct libinput_event *event,
struct libinput_event_keyboard *kbevent =
libinput_event_get_keyboard_event(event);
struct wlr_event_keyboard_key wlr_event = { 0 };
wlr_event.time_msec =
usec_to_msec(libinput_event_keyboard_get_time_usec(kbevent));
uint64_t event_time = libinput_event_keyboard_get_time_usec(kbevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.keycode = libinput_event_keyboard_get_key(kbevent);
enum libinput_key_state state =
libinput_event_keyboard_get_key_state(kbevent);

View file

@ -32,8 +32,9 @@ void handle_pointer_motion(struct libinput_event *event,
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_motion wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_pointer_get_time_usec(pevent));
uint64_t event_time = libinput_event_pointer_get_time_usec(pevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.delta_x = libinput_event_pointer_get_dx(pevent);
wlr_event.delta_y = libinput_event_pointer_get_dy(pevent);
wlr_event.unaccel_dx = libinput_event_pointer_get_dx_unaccelerated(pevent);
@ -54,8 +55,9 @@ void handle_pointer_motion_abs(struct libinput_event *event,
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_motion_absolute wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_pointer_get_time_usec(pevent));
uint64_t event_time = libinput_event_pointer_get_time_usec(pevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.x = libinput_event_pointer_get_absolute_x_transformed(pevent, 1);
wlr_event.y = libinput_event_pointer_get_absolute_y_transformed(pevent, 1);
wlr_signal_emit_safe(&wlr_dev->pointer->events.motion_absolute, &wlr_event);
@ -74,8 +76,9 @@ void handle_pointer_button(struct libinput_event *event,
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_button wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_pointer_get_time_usec(pevent));
uint64_t event_time = libinput_event_pointer_get_time_usec(pevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.button = libinput_event_pointer_get_button(pevent);
switch (libinput_event_pointer_get_button_state(pevent)) {
case LIBINPUT_BUTTON_STATE_PRESSED:
@ -101,8 +104,9 @@ void handle_pointer_axis(struct libinput_event *event,
libinput_event_get_pointer_event(event);
struct wlr_event_pointer_axis wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_pointer_get_time_usec(pevent));
uint64_t event_time = libinput_event_pointer_get_time_usec(pevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
switch (libinput_event_pointer_get_axis_source(pevent)) {
case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL:
wlr_event.source = WLR_AXIS_SOURCE_WHEEL;

View file

@ -32,8 +32,9 @@ void handle_touch_down(struct libinput_event *event,
libinput_event_get_touch_event(event);
struct wlr_event_touch_down wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
uint64_t event_time = libinput_event_touch_get_time_usec(tevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
@ -52,8 +53,9 @@ void handle_touch_up(struct libinput_event *event,
libinput_event_get_touch_event(event);
struct wlr_event_touch_up wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
uint64_t event_time = libinput_event_touch_get_time_usec(tevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event);
}
@ -70,8 +72,9 @@ void handle_touch_motion(struct libinput_event *event,
libinput_event_get_touch_event(event);
struct wlr_event_touch_motion wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
uint64_t event_time = libinput_event_touch_get_time_usec(tevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
@ -90,8 +93,9 @@ void handle_touch_cancel(struct libinput_event *event,
libinput_event_get_touch_event(event);
struct wlr_event_touch_cancel wlr_event = { 0 };
wlr_event.device = wlr_dev;
wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
uint64_t event_time = libinput_event_touch_get_time_usec(tevent);
wlr_event.time_msec = usec_to_msec(event_time);
wlr_event.time_nsec = event_time * 1000;
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event);
}

View file

@ -87,8 +87,11 @@ static int xf_input_synchronize_event(rdpInput *input, UINT32 flags) {
return true;
}
static inline int64_t timespec_to_msec(const struct timespec *a) {
return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
static inline uint32_t timespec_to_msec(const struct timespec *a) {
return (uint32_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
static inline uint64_t timespec_to_nsec(const struct timespec *a) {
return (uint64_t)a->tv_sec * 1000000000 + a->tv_nsec;
}
static int xf_input_mouse_event(rdpInput *input,
@ -105,6 +108,7 @@ static int xf_input_mouse_event(rdpInput *input,
struct wlr_event_pointer_motion_absolute event = { 0 };
event.device = wlr_device;
event.time_msec = timespec_to_msec(&now);
event.time_nsec = timespec_to_nsec(&now);
event.x = x / (double)context->output->wlr_output.width;
event.y = y / (double)context->output->wlr_output.height;
wlr_signal_emit_safe(&pointer->events.motion_absolute, &event);
@ -124,6 +128,7 @@ static int xf_input_mouse_event(rdpInput *input,
struct wlr_event_pointer_button event = { 0 };
event.device = wlr_device;
event.time_msec = timespec_to_msec(&now);
event.time_nsec = timespec_to_nsec(&now);
event.button = button;
event.state = (flags & PTR_FLAGS_DOWN) ?
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED;
@ -139,6 +144,7 @@ static int xf_input_mouse_event(rdpInput *input,
struct wlr_event_pointer_axis event = { 0 };
event.device = &context->pointer->wlr_input_device;
event.time_msec = timespec_to_msec(&now);
event.time_nsec = timespec_to_nsec(&now);
event.source = WLR_AXIS_SOURCE_WHEEL;
event.orientation = WLR_AXIS_ORIENTATION_VERTICAL;
event.delta = value;
@ -165,6 +171,7 @@ static int xf_input_extended_mouse_event(
struct wlr_event_pointer_motion_absolute event = { 0 };
event.device = wlr_device;
event.time_msec = timespec_to_msec(&now);
event.time_nsec = timespec_to_nsec(&now);
event.x = x / (double)context->output->wlr_output.width;
event.y = y / (double)context->output->wlr_output.height;
wlr_signal_emit_safe(&pointer->events.motion_absolute, &event);
@ -207,6 +214,7 @@ static int xf_input_keyboard_event(rdpInput *input, UINT16 flags, UINT16 code) {
vk_code, KEYCODE_TYPE_EVDEV);
struct wlr_event_keyboard_key event = { 0 };
event.time_msec = timespec_to_msec(&now);
event.time_nsec = timespec_to_nsec(&now);
event.keycode = scan_code - 8;
event.state = state;
event.update_state = true;

View file

@ -16,6 +16,7 @@
#include "backend/wayland.h"
#include "util/signal.h"
#include "input-timestamps-unstable-v1-client-protocol.h"
#include "xdg-decoration-unstable-v1-client-protocol.h"
#include "pointer-gestures-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -81,6 +82,9 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
&zwp_pointer_gestures_v1_interface, 1);
} else if (strcmp(iface, zwp_input_timestamps_manager_v1_interface.name) == 0) {
wl->input_timestamps.manager = wl_registry_bind(registry, name,
&zwp_input_timestamps_manager_v1_interface, 1);
}
}
@ -227,6 +231,11 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
goto error_registry;
}
if (!wl->input_timestamps.manager) {
wlr_log(WLR_INFO,
"Remote Wayland compositor does not support input-timestamps");
}
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
int fd = wl_display_get_fd(wl->remote_display);
int events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;

View file

@ -1,6 +1,7 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -18,6 +19,7 @@
#include "pointer-gestures-unstable-v1-client-protocol.h"
#include "backend/wayland.h"
#include "util/signal.h"
#include "input-timestamps-unstable-v1-client-protocol.h"
static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
struct wlr_input_device *wlr_dev;
@ -34,6 +36,15 @@ static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
return NULL;
}
static inline uint32_t timespec_to_msec(const struct timespec *a) {
assert((unsigned long)a->tv_sec < UINT32_MAX / 1000 &&
"struct timespec value too big");
return (uint32_t)a->tv_sec * 1000 + a->tv_nsec / 1000000;
}
static inline uint64_t timespec_to_nsec(const struct timespec *a) {
return (uint64_t)a->tv_sec * 1000000000 + a->tv_nsec;
}
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
wl_fixed_t sy) {
@ -78,10 +89,19 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
return;
}
uint64_t time_nsec;
if (backend->input_timestamps.manager &&
backend->input_timestamps.pointer.tv_sec == time / 1000) {
time_nsec = timespec_to_nsec(&backend->input_timestamps.pointer);
} else {
time_nsec = (time % 1000) * 1000000;
}
struct wlr_output *wlr_output = &pointer->output->wlr_output;
struct wlr_event_pointer_motion_absolute event = {
.device = &pointer->input_device->wlr_input_device,
.time_msec = time,
.time_nsec = time_nsec,
.x = wl_fixed_to_double(sx) / wlr_output->width,
.y = wl_fixed_to_double(sy) / wlr_output->height,
};
@ -96,11 +116,20 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
return;
}
uint64_t time_nsec;
if (backend->input_timestamps.manager &&
backend->input_timestamps.pointer.tv_sec == time / 1000) {
time_nsec = timespec_to_nsec(&backend->input_timestamps.pointer);
} else {
time_nsec = (time % 1000) * 1000000;
}
struct wlr_event_pointer_button event = {
.device = &pointer->input_device->wlr_input_device,
.button = button,
.state = state,
.time_msec = time,
.time_nsec = time_nsec,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
}
@ -113,12 +142,21 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
return;
}
uint64_t time_nsec;
if (backend->input_timestamps.manager &&
backend->input_timestamps.pointer.tv_sec == time / 1000) {
time_nsec = timespec_to_nsec(&backend->input_timestamps.pointer);
} else {
time_nsec = (time % 1000) * 1000000;
}
struct wlr_event_pointer_axis event = {
.device = &pointer->input_device->wlr_input_device,
.delta = wl_fixed_to_double(value),
.delta_discrete = pointer->axis_discrete,
.orientation = axis,
.time_msec = time,
.time_nsec = time_nsec,
.source = pointer->axis_source,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
@ -156,12 +194,21 @@ static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
return;
}
uint64_t time_nsec;
if (backend->input_timestamps.manager &&
backend->input_timestamps.pointer.tv_sec == time / 1000) {
time_nsec = timespec_to_nsec(&backend->input_timestamps.pointer);
} else {
time_nsec = (time % 1000) * 1000000;
}
struct wlr_event_pointer_axis event = {
.device = &pointer->input_device->wlr_input_device,
.delta = 0,
.delta_discrete = 0,
.orientation = axis,
.time_msec = time,
.time_nsec = time_nsec,
.source = pointer->axis_source,
};
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
@ -195,24 +242,23 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
// TODO: set keymap
}
static uint32_t get_current_time_msec(void) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return now.tv_nsec / 1000;
}
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
struct wlr_input_device *dev = data;
struct wlr_wl_input_device *wl_dev = data;
struct wlr_input_device *dev = &wl_dev->wlr_input_device;
uint32_t time = get_current_time_msec();
assert(dev && dev->keyboard);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t *keycode_ptr;
wl_array_for_each(keycode_ptr, keys) {
struct wlr_event_keyboard_key event = {
.keycode = *keycode_ptr,
.state = WLR_KEY_PRESSED,
.time_msec = time,
.time_msec = timespec_to_msec(&now),
.time_nsec = timespec_to_nsec(&now),
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &event);
@ -221,9 +267,13 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
struct wlr_input_device *dev = data;
struct wlr_wl_input_device *wl_dev = data;
struct wlr_input_device *dev = &wl_dev->wlr_input_device;
uint32_t time = get_current_time_msec();
assert(dev && dev->keyboard);
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t pressed[dev->keyboard->num_keycodes + 1];
memcpy(pressed, dev->keyboard->keycodes,
@ -235,7 +285,8 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
struct wlr_event_keyboard_key event = {
.keycode = keycode,
.state = WLR_KEY_RELEASED,
.time_msec = time,
.time_msec = timespec_to_msec(&now),
.time_nsec = timespec_to_nsec(&now),
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &event);
@ -244,13 +295,25 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
struct wlr_input_device *dev = data;
struct wlr_wl_input_device *wl_dev = data;
struct wlr_input_device *dev = &wl_dev->wlr_input_device;
assert(dev && dev->keyboard);
uint64_t time_nsec;
if (wl_dev->backend->input_timestamps.manager &&
wl_dev->backend->input_timestamps.keyboard.tv_sec == time / 1000) {
time_nsec =
timespec_to_nsec(&wl_dev->backend->input_timestamps.keyboard);
} else {
time_nsec = (time % 1000) * 1000000;
}
struct wlr_event_keyboard_key wlr_event = {
.keycode = key,
.state = state,
.time_msec = time,
.time_nsec = time_nsec,
.update_state = false,
};
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
@ -259,15 +322,18 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
struct wlr_input_device *dev = data;
struct wlr_wl_input_device *wl_dev = data;
struct wlr_input_device *dev = &wl_dev->wlr_input_device;
assert(dev && dev->keyboard);
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
mods_locked, group);
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed,
mods_latched, mods_locked, group);
}
static void keyboard_handle_repeat_info(void *data,
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
// This space is intentionally left blank
/* This space is intentionally left blank */
}
static struct wl_keyboard_listener keyboard_listener = {
@ -521,11 +587,27 @@ void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *
}
wlr_keyboard_init(wlr_dev->keyboard, NULL);
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, dev);
dev->resource = wl_keyboard;
wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev);
}
static void input_timestamps_handle_timestamp(void *data,
struct zwp_input_timestamps_v1 *zwp_input_timestamps_v1,
uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
struct timespec *timestamp = data;
uint64_t tv_sec = ((uint64_t)tv_sec_hi << 32) + tv_sec_lo;
assert(tv_sec < (uint64_t)LONG_MAX && "input-timestamps value is too big");
timestamp->tv_sec = tv_sec;
timestamp->tv_nsec = tv_nsec;
}
static const struct zwp_input_timestamps_v1_listener input_timestamps_listener = {
.timestamp = input_timestamps_handle_timestamp,
};
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) {
struct wlr_wl_backend *backend = data;
@ -543,6 +625,14 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
}
wl_pointer_add_listener(wl_pointer, &pointer_listener, backend);
if (backend->input_timestamps.manager) {
struct zwp_input_timestamps_v1 *pointer_input_timestamps =
zwp_input_timestamps_manager_v1_get_pointer_timestamps(
backend->input_timestamps.manager, wl_pointer);
zwp_input_timestamps_v1_add_listener(pointer_input_timestamps,
&input_timestamps_listener, &backend->input_timestamps.pointer);
}
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void*) wl_seat);
@ -553,6 +643,14 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
if (backend->started) {
create_wl_keyboard(wl_keyboard, backend);
}
if (backend->input_timestamps.manager) {
struct zwp_input_timestamps_v1 *keyboard_input_timestamps =
zwp_input_timestamps_manager_v1_get_keyboard_timestamps(
backend->input_timestamps.manager, wl_keyboard);
zwp_input_timestamps_v1_add_listener(keyboard_input_timestamps,
&input_timestamps_listener, &backend->input_timestamps.keyboard);
}
}
}

View file

@ -20,6 +20,7 @@ static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
enum wlr_key_state st, xcb_timestamp_t time) {
struct wlr_event_keyboard_key ev = {
.time_msec = time,
.time_nsec = time * 1000000,
.keycode = key,
.state = st,
.update_state = true,
@ -32,6 +33,7 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key,
struct wlr_event_pointer_button ev = {
.device = &output->pointer_dev,
.time_msec = time,
.time_nsec = time * 1000000,
.button = key,
.state = st,
};
@ -44,6 +46,7 @@ static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
struct wlr_event_pointer_axis ev = {
.device = &output->pointer_dev,
.time_msec = time,
.time_nsec = time * 1000000,
.source = WLR_AXIS_SOURCE_WHEEL,
.orientation = WLR_AXIS_ORIENTATION_VERTICAL,
// 15 is a typical value libinput sends for one scroll
@ -59,6 +62,7 @@ static void send_pointer_position_event(struct wlr_x11_output *output,
struct wlr_event_pointer_motion_absolute ev = {
.device = &output->pointer_dev,
.time_msec = time,
.time_nsec = time * 1000000,
.x = (double)x / output->wlr_output.width,
.y = (double)y / output->wlr_output.height,
};

View file

@ -39,6 +39,11 @@ struct wlr_wl_backend {
struct wl_keyboard *keyboard;
struct wlr_wl_pointer *current_pointer;
char *seat_name;
struct {
struct zwp_input_timestamps_manager_v1 *manager;
struct timespec keyboard;
struct timespec pointer;
} input_timestamps;
};
struct wlr_wl_output {

View file

@ -85,6 +85,7 @@ static void virtual_keyboard_key(struct wl_client *client,
}
struct wlr_event_keyboard_key event = {
.time_msec = time,
.time_nsec = time * 1000000,
.keycode = key,
.update_state = false,
.state = state,