#include "input.h" #include #include #include #include #include #include #include #include #include #include #include #include #define LOG_MODULE "input" #define LOG_ENABLE_DBG 0 #include "log.h" #include "commands.h" #include "keymap.h" #include "render.h" #include "search.h" #include "selection.h" #include "terminal.h" #include "vt.h" static void keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { struct wayland *wayl = data; char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); /* TODO: free old context + keymap */ if (wayl->kbd.xkb_compose_state != NULL) { xkb_compose_state_unref(wayl->kbd.xkb_compose_state); wayl->kbd.xkb_compose_state = NULL; } if (wayl->kbd.xkb_compose_table != NULL) { xkb_compose_table_unref(wayl->kbd.xkb_compose_table); wayl->kbd.xkb_compose_table = NULL; } if (wayl->kbd.xkb_keymap != NULL) { xkb_keymap_unref(wayl->kbd.xkb_keymap); wayl->kbd.xkb_keymap = NULL; } if (wayl->kbd.xkb_state != NULL) { xkb_state_unref(wayl->kbd.xkb_state); wayl->kbd.xkb_state = NULL; } if (wayl->kbd.xkb != NULL) { xkb_context_unref(wayl->kbd.xkb); wayl->kbd.xkb = NULL; } wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); wayl->kbd.xkb_keymap = xkb_keymap_new_from_string( wayl->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); /* TODO: initialize in enter? */ wayl->kbd.xkb_state = xkb_state_new(wayl->kbd.xkb_keymap); wayl->kbd.mod_shift = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Shift"); wayl->kbd.mod_alt = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod1") ; wayl->kbd.mod_ctrl = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Control"); wayl->kbd.mod_meta = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod4"); /* Compose (dead keys) */ wayl->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( wayl->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); wayl->kbd.xkb_compose_state = xkb_compose_state_new( wayl->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); munmap(map_str, size); close(fd); } static void keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { struct wayland *wayl = data; wayl->input_serial = serial; wayl->focused = wayl_terminal_from_surface(wayl, surface); assert(wayl->focused != NULL); term_focus_in(wayl->focused); } static bool start_repeater(struct wayland *wayl, uint32_t key) { if (wayl->kbd.repeat.dont_re_repeat) return true; struct itimerspec t = { .it_value = {.tv_sec = 0, .tv_nsec = wayl->kbd.repeat.delay * 1000000}, .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / wayl->kbd.repeat.rate}, }; if (t.it_value.tv_nsec >= 1000000000) { t.it_value.tv_sec += t.it_value.tv_nsec / 1000000000; t.it_value.tv_nsec %= 1000000000; } if (t.it_interval.tv_nsec >= 1000000000) { t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000; t.it_interval.tv_nsec %= 1000000000; } if (timerfd_settime(wayl->kbd.repeat.fd, 0, &t, NULL) < 0) { LOG_ERRNO("failed to arm keyboard repeat timer"); return false; } wayl->kbd.repeat.key = key; return true; } static bool stop_repeater(struct wayland *wayl, uint32_t key) { if (key != -1 && key != wayl->kbd.repeat.key) return true; if (timerfd_settime(wayl->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) { LOG_ERRNO("failed to disarm keyboard repeat timer"); return false; } return true; } static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { struct wayland *wayl = data; stop_repeater(wayl, -1); term_focus_out(wayl->focused); wayl->focused = NULL; } static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { struct wayland *wayl = data; struct terminal *term = wayl->focused; assert(term != NULL); const xkb_mod_mask_t ctrl = 1 << wayl->kbd.mod_ctrl; const xkb_mod_mask_t alt = 1 << wayl->kbd.mod_alt; const xkb_mod_mask_t shift = 1 << wayl->kbd.mod_shift; const xkb_mod_mask_t meta = 1 << wayl->kbd.mod_meta; if (state == XKB_KEY_UP) { stop_repeater(wayl, key); return; } key += 8; xkb_keysym_t sym = xkb_state_key_get_one_sym(wayl->kbd.xkb_state, key); #if 0 char foo[100]; xkb_keysym_get_name(sym, foo, sizeof(foo)); LOG_INFO("%s", foo); #endif xkb_compose_state_feed(wayl->kbd.xkb_compose_state, sym); enum xkb_compose_status compose_status = xkb_compose_state_get_status( wayl->kbd.xkb_compose_state); if (compose_status == XKB_COMPOSE_COMPOSING) return; xkb_mod_mask_t mods = xkb_state_serialize_mods( wayl->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); //xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(wayl->kbd.xkb_state, key); xkb_mod_mask_t consumed = 0x0; xkb_mod_mask_t significant = ctrl | alt | shift | meta; xkb_mod_mask_t effective_mods = mods & ~consumed & significant; if (term->is_searching) { start_repeater(wayl, key - 8); search_input(term, key, sym, effective_mods); return; } #if 0 for (size_t i = 0; i < 32; i++) { if (mods & (1 << i)) { LOG_INFO("%s", xkb_keymap_mod_get_name(wayl->kbd.xkb_keymap, i)); } } #endif LOG_DBG("sym=%u, mod=0x%08x, consumed=0x%08x, significant=0x%08x, " "effective=0x%08x", sym, mods, consumed, significant, effective_mods); bool found_map = false; enum modifier keymap_mods = MOD_NONE; keymap_mods |= wayl->kbd.shift ? MOD_SHIFT : MOD_NONE; keymap_mods |= wayl->kbd.alt ? MOD_ALT : MOD_NONE; keymap_mods |= wayl->kbd.ctrl ? MOD_CTRL : MOD_NONE; keymap_mods |= wayl->kbd.meta ? MOD_META : MOD_NONE; if (effective_mods == shift) { if (sym == XKB_KEY_Page_Up) { cmd_scrollback_up(term, term->rows); found_map = true; } else if (sym == XKB_KEY_Page_Down) { cmd_scrollback_down(term, term->rows); found_map = true; } } else if (effective_mods == (shift | ctrl)) { if (sym == XKB_KEY_C) { selection_to_clipboard(term, serial); found_map = true; } else if (sym == XKB_KEY_V) { selection_from_clipboard(term, serial); term_reset_view(term); found_map = true; } else if (sym == XKB_KEY_R) { search_begin(term); found_map = true; } } for (size_t i = 0; i < sizeof(key_map) / sizeof(key_map[0]) && !found_map; i++) { const struct key_map *k = &key_map[i]; if (k->sym != sym) continue; for (size_t j = 0; j < k->count; j++) { const struct key_data *info = &k->data[j]; if (info->modifiers != MOD_ANY && info->modifiers != keymap_mods) continue; if (info->cursor_keys_mode != CURSOR_KEYS_DONTCARE && info->cursor_keys_mode != term->cursor_keys_mode) continue; if (info->keypad_keys_mode != KEYPAD_DONTCARE && info->keypad_keys_mode != term->keypad_keys_mode) continue; term_to_slave(term, info->seq, strlen(info->seq)); found_map = true; term_reset_view(term); selection_cancel(term); break; } } if (!found_map) { uint8_t buf[64] = {0}; int count = 0; if (compose_status == XKB_COMPOSE_COMPOSED) { count = xkb_compose_state_get_utf8( wayl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); xkb_compose_state_reset(wayl->kbd.xkb_compose_state); } else { count = xkb_state_key_get_utf8( wayl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); } if (count > 0) { #define is_control_key(x) ((x) >= 0x40 && (x) <= 0x7f) #define IS_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f)) if ((keymap_mods & MOD_CTRL) && !is_control_key(sym) && (count == 1 && !IS_CTRL(buf[0])) && sym < 256) { static const int mod_param_map[32] = { [MOD_SHIFT] = 2, [MOD_ALT] = 3, [MOD_SHIFT | MOD_ALT] = 4, [MOD_CTRL] = 5, [MOD_SHIFT | MOD_CTRL] = 6, [MOD_ALT | MOD_CTRL] = 7, [MOD_SHIFT | MOD_ALT | MOD_CTRL] = 8, [MOD_META] = 9, [MOD_META | MOD_SHIFT] = 10, [MOD_META | MOD_ALT] = 11, [MOD_META | MOD_SHIFT | MOD_ALT] = 12, [MOD_META | MOD_CTRL] = 13, [MOD_META | MOD_SHIFT | MOD_CTRL] = 14, [MOD_META | MOD_ALT | MOD_CTRL] = 15, [MOD_META | MOD_SHIFT | MOD_ALT | MOD_CTRL] = 16, }; assert(keymap_mods < sizeof(mod_param_map) / sizeof(mod_param_map[0])); int modify_param = mod_param_map[keymap_mods]; assert(modify_param != 0); char reply[1024]; snprintf(reply, sizeof(reply), "\x1b[27;%d;%d~", modify_param, sym); term_to_slave(term, reply, strlen(reply)); } else { if (effective_mods & alt) term_to_slave(term, "\x1b", 1); term_to_slave(term, buf, count); } term_reset_view(term); selection_cancel(term); } } start_repeater(wayl, key - 8); } static void keyboard_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 wayland *wayl = data; LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u", mods_depressed, mods_latched, mods_locked, group); xkb_state_update_mask( wayl->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); /* Update state of modifiers we're interrested in for e.g mouse events */ wayl->kbd.shift = xkb_state_mod_index_is_active( wayl->kbd.xkb_state, wayl->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); wayl->kbd.alt = xkb_state_mod_index_is_active( wayl->kbd.xkb_state, wayl->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); wayl->kbd.ctrl = xkb_state_mod_index_is_active( wayl->kbd.xkb_state, wayl->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); wayl->kbd.meta = xkb_state_mod_index_is_active( wayl->kbd.xkb_state, wayl->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); } static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { struct wayland *wayl = data; LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay); wayl->kbd.repeat.rate = rate; wayl->kbd.repeat.delay = delay; } const struct wl_keyboard_listener keyboard_listener = { .keymap = &keyboard_keymap, .enter = &keyboard_enter, .leave = &keyboard_leave, .key = &keyboard_key, .modifiers = &keyboard_modifiers, .repeat_info = &keyboard_repeat_info, }; void input_repeat(struct wayland *wayl, uint32_t key) { keyboard_key(wayl, NULL, 0, 0, key, XKB_KEY_DOWN); } static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct wayland *wayl = data; struct terminal *term = wayl_terminal_from_surface(wayl, surface); wayl->moused = term; int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; wayl->mouse.col = x / term->cell_width; wayl->mouse.row = y / term->cell_height; wayl_update_cursor_surface(wayl, term); } static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { struct wayland *wayl = data; wayl->moused = NULL; } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct wayland *wayl = data; struct terminal *term = wayl->moused; assert(term != NULL); int x = wl_fixed_to_int(surface_x) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale; int col = (x - term->x_margin) / term->cell_width; int row = (y - term->y_margin) / term->cell_height; if (col < 0 || row < 0 || col >= term->cols || row >= term->rows) return; bool update_selection = wayl->mouse.button == BTN_LEFT; bool update_selection_early = term->selection.end.row == -1; if (update_selection && update_selection_early) selection_update(term, col, row); if (col == wayl->mouse.col && row == wayl->mouse.row) return; wayl->mouse.col = col; wayl->mouse.row = row; if (update_selection && !update_selection_early) selection_update(term, col, row); term_mouse_motion( term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col, wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); } static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { LOG_DBG("BUTTON: button=%x, state=%u", button, state); struct wayland *wayl = data; struct terminal *term = wayl->moused; assert(term != NULL); search_cancel(term); switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: { /* Time since last click */ struct timeval now, since_last; gettimeofday(&now, NULL); timersub(&now, &wayl->mouse.last_time, &since_last); /* Double- or triple click? */ if (button == wayl->mouse.last_button && since_last.tv_sec == 0 && since_last.tv_usec <= 300 * 1000) { wayl->mouse.count++; } else wayl->mouse.count = 1; if (button == BTN_LEFT) { switch (wayl->mouse.count) { case 1: selection_start(term, wayl->mouse.col, wayl->mouse.row); break; case 2: selection_mark_word(term, wayl->mouse.col, wayl->mouse.row, wayl->kbd.ctrl, serial); break; case 3: selection_mark_row(term, wayl->mouse.row, serial); break; } } else { if (wayl->mouse.count == 1 && button == BTN_MIDDLE && selection_enabled(term)) selection_from_primary(term); selection_cancel(term); } wayl->mouse.button = button; /* For motion events */ wayl->mouse.last_button = button; wayl->mouse.last_time = now; term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col, wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); break; } case WL_POINTER_BUTTON_STATE_RELEASED: if (button != BTN_LEFT || term->selection.end.col == -1) selection_cancel(term); else selection_finalize(term, serial); wayl->mouse.button = 0; /* For motion events */ term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col, wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); break; } } static void mouse_scroll(struct wayland *wayl, int amount) { struct terminal *term = wayl->moused; assert(term != NULL); int button = amount < 0 ? BTN_BACK : BTN_FORWARD; void (*scrollback)(struct terminal *term, int rows) = amount < 0 ? &cmd_scrollback_up : &cmd_scrollback_down; amount = abs(amount); if ((button == BTN_BACK || button == BTN_FORWARD) && term->grid == &term->alt && term->alt_scrolling) { /* * alternateScroll/faux scrolling - translate mouse * "back"/"forward" to up/down keys */ xkb_keycode_t key = xkb_keymap_key_by_name( wayl->kbd.xkb_keymap, button == BTN_BACK ? "UP" : "DOWN"); for (int i = 0; i < amount; i++) keyboard_key(term, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_DOWN); keyboard_key(term, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_UP); } else { for (int i = 0; i < amount; i++) term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col, wayl->kbd.shift, wayl->kbd.alt, wayl->kbd.ctrl); scrollback(term, amount); } } static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; struct wayland *wayl = data; if (wayl->mouse.have_discrete) return; mouse_scroll(wayl, wl_fixed_to_int(value)); } static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; struct wayland *wayl = data; wayl->mouse.have_discrete = true; mouse_scroll(wayl, discrete); } static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { struct wayland *wayl = data; wayl->mouse.have_discrete = false; } static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { } static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { } const struct wl_pointer_listener pointer_listener = { .enter = wl_pointer_enter, .leave = wl_pointer_leave, .motion = wl_pointer_motion, .button = wl_pointer_button, .axis = wl_pointer_axis, .frame = wl_pointer_frame, .axis_source = wl_pointer_axis_source, .axis_stop = wl_pointer_axis_stop, .axis_discrete = wl_pointer_axis_discrete, };