2019-06-19 10:04:47 +02:00
|
|
|
#include "input.h"
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
#include <threads.h>
|
2019-07-09 10:00:54 +02:00
|
|
|
#include <locale.h>
|
2019-06-19 10:04:47 +02:00
|
|
|
#include <sys/mman.h>
|
2019-07-17 21:30:57 +02:00
|
|
|
#include <sys/time.h>
|
2019-08-05 19:33:01 +02:00
|
|
|
#include <sys/timerfd.h>
|
2020-02-29 12:52:55 +01:00
|
|
|
#include <sys/epoll.h>
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2019-07-10 14:17:44 +02:00
|
|
|
#include <linux/input-event-codes.h>
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
#include <xkbcommon/xkbcommon.h>
|
|
|
|
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
2019-07-09 10:00:54 +02:00
|
|
|
#include <xkbcommon/xkbcommon-compose.h>
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
#include <xdg-shell.h>
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
#define LOG_MODULE "input"
|
2019-07-03 20:21:03 +02:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-19 10:04:47 +02:00
|
|
|
#include "log.h"
|
2020-03-02 18:42:49 +01:00
|
|
|
#include "config.h"
|
2019-07-09 16:26:36 +02:00
|
|
|
#include "commands.h"
|
2019-08-27 17:23:28 +02:00
|
|
|
#include "keymap.h"
|
2020-03-02 20:29:28 +01:00
|
|
|
#include "quirks.h"
|
2019-08-27 17:23:28 +02:00
|
|
|
#include "render.h"
|
|
|
|
|
#include "search.h"
|
2019-07-11 09:51:51 +02:00
|
|
|
#include "selection.h"
|
2019-08-27 17:23:28 +02:00
|
|
|
#include "terminal.h"
|
2019-07-15 15:42:21 +02:00
|
|
|
#include "vt.h"
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2020-03-12 10:20:05 +01:00
|
|
|
#define ALEN(v) (sizeof(v) / sizeof(v[0]))
|
|
|
|
|
|
2020-03-08 15:28:47 +01:00
|
|
|
void
|
|
|
|
|
input_execute_binding(struct terminal *term, enum binding_action action,
|
|
|
|
|
uint32_t serial)
|
2020-03-08 12:08:46 +01:00
|
|
|
{
|
|
|
|
|
switch (action) {
|
2020-03-12 10:46:27 +01:00
|
|
|
case BIND_ACTION_NONE:
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-08 15:17:29 +01:00
|
|
|
case BIND_ACTION_SCROLLBACK_UP:
|
|
|
|
|
cmd_scrollback_up(term, term->rows);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SCROLLBACK_DOWN:
|
|
|
|
|
cmd_scrollback_down(term, term->rows);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-08 12:08:46 +01:00
|
|
|
case BIND_ACTION_CLIPBOARD_COPY:
|
|
|
|
|
selection_to_clipboard(term, serial);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_CLIPBOARD_PASTE:
|
|
|
|
|
selection_from_clipboard(term, serial);
|
|
|
|
|
term_reset_view(term);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_PRIMARY_PASTE:
|
2020-03-12 10:20:05 +01:00
|
|
|
selection_from_primary(term);
|
2020-03-08 12:08:46 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SEARCH_START:
|
|
|
|
|
search_begin(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-08 15:17:29 +01:00
|
|
|
case BIND_ACTION_FONT_SIZE_UP:
|
|
|
|
|
term_font_size_increase(term);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_FONT_SIZE_DOWN:
|
|
|
|
|
term_font_size_decrease(term);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_FONT_SIZE_RESET:
|
|
|
|
|
term_font_size_reset(term);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_SPAWN_TERMINAL:
|
|
|
|
|
term_spawn_new(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-12 09:34:09 +01:00
|
|
|
case BIND_ACTION_MINIMIZE:
|
|
|
|
|
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_MAXIMIZE:
|
2020-03-12 17:47:53 +01:00
|
|
|
if (term->window->is_fullscreen)
|
|
|
|
|
xdg_toplevel_unset_fullscreen(term->window->xdg_toplevel);
|
2020-03-12 09:34:09 +01:00
|
|
|
if (term->window->is_maximized)
|
|
|
|
|
xdg_toplevel_unset_maximized(term->window->xdg_toplevel);
|
|
|
|
|
else
|
|
|
|
|
xdg_toplevel_set_maximized(term->window->xdg_toplevel);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BIND_ACTION_FULLSCREEN:
|
|
|
|
|
if (term->window->is_fullscreen)
|
|
|
|
|
xdg_toplevel_unset_fullscreen(term->window->xdg_toplevel);
|
|
|
|
|
else
|
|
|
|
|
xdg_toplevel_set_fullscreen(term->window->xdg_toplevel, NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-08 12:08:46 +01:00
|
|
|
case BIND_ACTION_COUNT:
|
|
|
|
|
assert(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-09 19:47:22 +01:00
|
|
|
bool
|
|
|
|
|
input_parse_key_binding_for_action(
|
2020-03-08 15:17:29 +01:00
|
|
|
struct xkb_keymap *keymap, enum binding_action action,
|
|
|
|
|
const char *combos, key_binding_list_t *bindings)
|
|
|
|
|
{
|
|
|
|
|
if (combos == NULL)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
xkb_mod_mask_t mod_mask = 0;
|
|
|
|
|
xkb_keysym_t sym = 0;
|
|
|
|
|
|
|
|
|
|
char *copy = strdup(combos);
|
|
|
|
|
|
|
|
|
|
for (char *save1 = NULL, *combo = strtok_r(copy, " ", &save1);
|
|
|
|
|
combo != NULL;
|
|
|
|
|
combo = strtok_r(NULL, " ", &save1))
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("%s", combo);
|
|
|
|
|
for (char *save2 = NULL, *key = strtok_r(combo, "+", &save2),
|
|
|
|
|
*next_key = strtok_r(NULL, "+", &save2);
|
|
|
|
|
key != NULL;
|
|
|
|
|
key = next_key, next_key = strtok_r(NULL, "+", &save2))
|
|
|
|
|
{
|
|
|
|
|
if (next_key != NULL) {
|
|
|
|
|
/* Modifier */
|
|
|
|
|
xkb_mod_index_t mod = xkb_keymap_mod_get_index(keymap, key);
|
|
|
|
|
|
|
|
|
|
if (mod == -1) {
|
|
|
|
|
LOG_ERR("%s: not a valid modifier name", key);
|
|
|
|
|
free(copy);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mod_mask |= 1 << mod;
|
|
|
|
|
LOG_DBG("MOD: %d - %s", mod, key);
|
|
|
|
|
} else {
|
|
|
|
|
/* Symbol */
|
|
|
|
|
sym = xkb_keysym_from_name(key, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-09 19:47:01 +01:00
|
|
|
LOG_DBG("action=%u: mods=0x%08x, sym=%d", action, mod_mask, sym);
|
2020-03-08 15:17:29 +01:00
|
|
|
|
|
|
|
|
assert(sym != 0);
|
|
|
|
|
if (bindings != NULL) {
|
|
|
|
|
const struct key_binding binding = {
|
|
|
|
|
.mods = mod_mask,
|
|
|
|
|
.sym = sym,
|
|
|
|
|
.action = action,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
tll_push_back(*bindings, binding);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(copy);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
static void
|
|
|
|
|
keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|
|
|
|
uint32_t format, int32_t fd, uint32_t size)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
|
|
|
char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->kbd.xkb_compose_state != NULL) {
|
|
|
|
|
xkb_compose_state_unref(wayl->kbd.xkb_compose_state);
|
|
|
|
|
wayl->kbd.xkb_compose_state = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
}
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->kbd.xkb_compose_table != NULL) {
|
|
|
|
|
xkb_compose_table_unref(wayl->kbd.xkb_compose_table);
|
|
|
|
|
wayl->kbd.xkb_compose_table = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
}
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->kbd.xkb_keymap != NULL) {
|
|
|
|
|
xkb_keymap_unref(wayl->kbd.xkb_keymap);
|
|
|
|
|
wayl->kbd.xkb_keymap = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
}
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->kbd.xkb_state != NULL) {
|
|
|
|
|
xkb_state_unref(wayl->kbd.xkb_state);
|
|
|
|
|
wayl->kbd.xkb_state = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
}
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->kbd.xkb != NULL) {
|
|
|
|
|
xkb_context_unref(wayl->kbd.xkb);
|
|
|
|
|
wayl->kbd.xkb = NULL;
|
2019-08-12 21:32:53 +02:00
|
|
|
}
|
2020-03-08 15:17:29 +01:00
|
|
|
tll_free(wayl->kbd.bindings.key);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
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,
|
2019-06-19 10:04:47 +02:00
|
|
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
|
|
|
|
|
|
|
|
/* TODO: initialize in enter? */
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->kbd.xkb_state = xkb_state_new(wayl->kbd.xkb_keymap);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
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");
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2019-07-09 10:00:54 +02:00
|
|
|
/* Compose (dead keys) */
|
2019-10-27 18:43:07 +01:00
|
|
|
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);
|
2019-07-09 10:00:54 +02:00
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
munmap(map_str, size);
|
|
|
|
|
close(fd);
|
2020-03-08 12:08:46 +01:00
|
|
|
|
|
|
|
|
for (size_t i = 0; i < BIND_ACTION_COUNT; i++) {
|
2020-03-09 19:47:22 +01:00
|
|
|
input_parse_key_binding_for_action(
|
2020-03-08 15:28:47 +01:00
|
|
|
wayl->kbd.xkb_keymap, i,
|
|
|
|
|
wayl->conf->bindings.key[i], &wayl->kbd.bindings.key);
|
|
|
|
|
|
2020-03-09 19:47:22 +01:00
|
|
|
input_parse_key_binding_for_action(
|
2020-03-08 15:28:47 +01:00
|
|
|
wayl->kbd.xkb_keymap, i,
|
|
|
|
|
wayl->conf->bindings.search[i], &wayl->kbd.bindings.search);
|
2020-03-08 12:08:46 +01:00
|
|
|
}
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-14 19:28:37 +01:00
|
|
|
static void
|
2019-06-19 10:04:47 +02:00
|
|
|
keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
struct wl_surface *surface, struct wl_array *keys)
|
|
|
|
|
{
|
2019-11-28 19:46:27 +01:00
|
|
|
assert(surface != NULL);
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2020-02-29 12:13:58 +01:00
|
|
|
struct wl_window *win = wl_surface_get_user_data(surface);
|
|
|
|
|
struct terminal *term = win->term;
|
|
|
|
|
|
|
|
|
|
wayl->kbd_focus = term;
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->input_serial = serial;
|
2019-11-28 19:46:27 +01:00
|
|
|
|
2020-01-02 19:29:42 +01:00
|
|
|
term_kbd_focus_in(wayl->kbd_focus);
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
static bool
|
2019-10-27 18:43:07 +01:00
|
|
|
start_repeater(struct wayland *wayl, uint32_t key)
|
2019-06-19 10:04:47 +02:00
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
if (wayl->kbd.repeat.dont_re_repeat)
|
2019-08-05 19:33:01 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
struct itimerspec t = {
|
2019-10-27 18:43:07 +01:00
|
|
|
.it_value = {.tv_sec = 0, .tv_nsec = wayl->kbd.repeat.delay * 1000000},
|
|
|
|
|
.it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / wayl->kbd.repeat.rate},
|
2019-08-05 19:33:01 +02:00
|
|
|
};
|
2019-06-21 18:40:53 +02:00
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2019-10-27 18:43:07 +01:00
|
|
|
if (timerfd_settime(wayl->kbd.repeat.fd, 0, &t, NULL) < 0) {
|
2019-08-05 19:33:01 +02:00
|
|
|
LOG_ERRNO("failed to arm keyboard repeat timer");
|
|
|
|
|
return false;
|
2019-06-21 18:40:53 +02:00
|
|
|
}
|
2019-08-02 18:19:07 +02:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->kbd.repeat.key = key;
|
2019-08-05 19:33:01 +02:00
|
|
|
return true;
|
2019-08-02 18:19:07 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-05 19:33:01 +02:00
|
|
|
static bool
|
2019-10-27 18:43:07 +01:00
|
|
|
stop_repeater(struct wayland *wayl, uint32_t key)
|
2019-08-02 18:19:07 +02:00
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
if (key != -1 && key != wayl->kbd.repeat.key)
|
2019-08-05 19:33:01 +02:00
|
|
|
return true;
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
if (timerfd_settime(wayl->kbd.repeat.fd, 0, &(struct itimerspec){{0}}, NULL) < 0) {
|
2019-08-05 19:33:01 +02:00
|
|
|
LOG_ERRNO("failed to disarm keyboard repeat timer");
|
|
|
|
|
return false;
|
2019-08-02 18:19:07 +02:00
|
|
|
}
|
2019-08-05 19:33:01 +02:00
|
|
|
|
|
|
|
|
return true;
|
2019-08-02 18:19:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2019-08-05 19:33:01 +02:00
|
|
|
keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
struct wl_surface *surface)
|
2019-08-02 18:19:07 +02:00
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2019-08-05 19:33:01 +02:00
|
|
|
|
2019-11-28 19:24:16 +01:00
|
|
|
assert(
|
2020-01-02 15:58:52 +01:00
|
|
|
wayl->kbd_focus == NULL ||
|
2019-11-28 19:24:16 +01:00
|
|
|
surface == NULL || /* Seen on Sway 1.2 */
|
2020-02-29 12:13:58 +01:00
|
|
|
((const struct wl_window *)wl_surface_get_user_data(surface))->term == wayl->kbd_focus
|
|
|
|
|
);
|
2019-11-21 18:17:02 +01:00
|
|
|
|
2020-01-02 15:58:52 +01:00
|
|
|
struct terminal *old_focused = wayl->kbd_focus;
|
|
|
|
|
wayl->kbd_focus = NULL;
|
2019-11-29 22:11:41 +01:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
stop_repeater(wayl, -1);
|
2020-01-13 19:58:57 +01:00
|
|
|
wayl->kbd.shift = false;;
|
|
|
|
|
wayl->kbd.alt = false;;
|
|
|
|
|
wayl->kbd.ctrl = false;;
|
|
|
|
|
wayl->kbd.meta = false;;
|
2020-01-14 19:32:02 +01:00
|
|
|
xkb_compose_state_reset(wayl->kbd.xkb_compose_state);
|
2020-01-13 19:58:57 +01:00
|
|
|
|
2020-02-29 13:04:18 +01:00
|
|
|
if (old_focused != NULL)
|
2020-01-02 19:30:34 +01:00
|
|
|
term_kbd_focus_out(old_focused);
|
2020-02-29 13:04:18 +01:00
|
|
|
else {
|
2019-11-24 00:06:50 +01:00
|
|
|
/*
|
|
|
|
|
* Sway bug - under certain conditions we get a
|
|
|
|
|
* keyboard_leave() (and keyboard_key()) without first having
|
|
|
|
|
* received a keyboard_enter()
|
|
|
|
|
*/
|
2019-11-28 19:47:00 +01:00
|
|
|
LOG_WARN(
|
|
|
|
|
"compositor sent keyboard_leave event without a keyboard_enter "
|
|
|
|
|
"event: surface=%p", surface);
|
2019-11-24 00:06:50 +01:00
|
|
|
}
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
static const struct key_data *
|
2020-01-11 18:40:27 +01:00
|
|
|
keymap_data_for_sym(xkb_keysym_t sym, size_t *count)
|
2020-01-11 18:18:31 +01:00
|
|
|
{
|
|
|
|
|
switch (sym) {
|
|
|
|
|
case XKB_KEY_Escape: *count = ALEN(key_escape); return key_escape;
|
|
|
|
|
case XKB_KEY_Return: *count = ALEN(key_return); return key_return;
|
|
|
|
|
case XKB_KEY_Tab: *count = ALEN(key_tab); return key_tab;
|
|
|
|
|
case XKB_KEY_ISO_Left_Tab: *count = ALEN(key_backtab); return key_backtab;
|
|
|
|
|
case XKB_KEY_BackSpace: *count = ALEN(key_backspace); return key_backspace;
|
|
|
|
|
case XKB_KEY_Up: *count = ALEN(key_up); return key_up;
|
|
|
|
|
case XKB_KEY_Down: *count = ALEN(key_down); return key_down;
|
|
|
|
|
case XKB_KEY_Right: *count = ALEN(key_right); return key_right;
|
|
|
|
|
case XKB_KEY_Left: *count = ALEN(key_left); return key_left;
|
|
|
|
|
case XKB_KEY_Home: *count = ALEN(key_home); return key_home;
|
|
|
|
|
case XKB_KEY_End: *count = ALEN(key_end); return key_end;
|
|
|
|
|
case XKB_KEY_Insert: *count = ALEN(key_insert); return key_insert;
|
|
|
|
|
case XKB_KEY_Delete: *count = ALEN(key_delete); return key_delete;
|
|
|
|
|
case XKB_KEY_Page_Up: *count = ALEN(key_pageup); return key_pageup;
|
|
|
|
|
case XKB_KEY_Page_Down: *count = ALEN(key_pagedown); return key_pagedown;
|
|
|
|
|
case XKB_KEY_F1: *count = ALEN(key_f1); return key_f1;
|
|
|
|
|
case XKB_KEY_F2: *count = ALEN(key_f2); return key_f2;
|
|
|
|
|
case XKB_KEY_F3: *count = ALEN(key_f3); return key_f3;
|
|
|
|
|
case XKB_KEY_F4: *count = ALEN(key_f4); return key_f4;
|
|
|
|
|
case XKB_KEY_F5: *count = ALEN(key_f5); return key_f5;
|
|
|
|
|
case XKB_KEY_F6: *count = ALEN(key_f6); return key_f6;
|
|
|
|
|
case XKB_KEY_F7: *count = ALEN(key_f7); return key_f7;
|
|
|
|
|
case XKB_KEY_F8: *count = ALEN(key_f8); return key_f8;
|
|
|
|
|
case XKB_KEY_F9: *count = ALEN(key_f9); return key_f9;
|
|
|
|
|
case XKB_KEY_F10: *count = ALEN(key_f10); return key_f10;
|
|
|
|
|
case XKB_KEY_F11: *count = ALEN(key_f11); return key_f11;
|
|
|
|
|
case XKB_KEY_F12: *count = ALEN(key_f12); return key_f12;
|
|
|
|
|
case XKB_KEY_F13: *count = ALEN(key_f13); return key_f13;
|
|
|
|
|
case XKB_KEY_F14: *count = ALEN(key_f14); return key_f14;
|
|
|
|
|
case XKB_KEY_F15: *count = ALEN(key_f15); return key_f15;
|
|
|
|
|
case XKB_KEY_F16: *count = ALEN(key_f16); return key_f16;
|
|
|
|
|
case XKB_KEY_F17: *count = ALEN(key_f17); return key_f17;
|
|
|
|
|
case XKB_KEY_F18: *count = ALEN(key_f18); return key_f18;
|
|
|
|
|
case XKB_KEY_F19: *count = ALEN(key_f19); return key_f19;
|
|
|
|
|
case XKB_KEY_F20: *count = ALEN(key_f20); return key_f20;
|
|
|
|
|
case XKB_KEY_F21: *count = ALEN(key_f21); return key_f21;
|
|
|
|
|
case XKB_KEY_F22: *count = ALEN(key_f22); return key_f22;
|
|
|
|
|
case XKB_KEY_F23: *count = ALEN(key_f23); return key_f23;
|
|
|
|
|
case XKB_KEY_F24: *count = ALEN(key_f24); return key_f24;
|
|
|
|
|
case XKB_KEY_F25: *count = ALEN(key_f25); return key_f25;
|
|
|
|
|
case XKB_KEY_F26: *count = ALEN(key_f26); return key_f26;
|
|
|
|
|
case XKB_KEY_F27: *count = ALEN(key_f27); return key_f27;
|
|
|
|
|
case XKB_KEY_F28: *count = ALEN(key_f28); return key_f28;
|
|
|
|
|
case XKB_KEY_F29: *count = ALEN(key_f29); return key_f29;
|
|
|
|
|
case XKB_KEY_F30: *count = ALEN(key_f30); return key_f30;
|
|
|
|
|
case XKB_KEY_F31: *count = ALEN(key_f31); return key_f31;
|
|
|
|
|
case XKB_KEY_F32: *count = ALEN(key_f32); return key_f32;
|
|
|
|
|
case XKB_KEY_F33: *count = ALEN(key_f33); return key_f33;
|
|
|
|
|
case XKB_KEY_F34: *count = ALEN(key_f34); return key_f34;
|
|
|
|
|
case XKB_KEY_F35: *count = ALEN(key_f35); return key_f35;
|
|
|
|
|
case XKB_KEY_KP_Up: *count = ALEN(key_kp_up); return key_kp_up;
|
|
|
|
|
case XKB_KEY_KP_Down: *count = ALEN(key_kp_down); return key_kp_down;
|
|
|
|
|
case XKB_KEY_KP_Right: *count = ALEN(key_kp_right); return key_kp_right;
|
|
|
|
|
case XKB_KEY_KP_Left: *count = ALEN(key_kp_left); return key_kp_left;
|
|
|
|
|
case XKB_KEY_KP_Begin: *count = ALEN(key_kp_begin); return key_kp_begin;
|
|
|
|
|
case XKB_KEY_KP_Home: *count = ALEN(key_kp_home); return key_kp_home;
|
|
|
|
|
case XKB_KEY_KP_End: *count = ALEN(key_kp_end); return key_kp_end;
|
|
|
|
|
case XKB_KEY_KP_Insert: *count = ALEN(key_kp_insert); return key_kp_insert;
|
|
|
|
|
case XKB_KEY_KP_Delete: *count = ALEN(key_kp_delete); return key_kp_delete;
|
|
|
|
|
case XKB_KEY_KP_Page_Up: *count = ALEN(key_kp_pageup); return key_kp_pageup;
|
|
|
|
|
case XKB_KEY_KP_Page_Down: *count = ALEN(key_kp_pagedown); return key_kp_pagedown;
|
|
|
|
|
case XKB_KEY_KP_Enter: *count = ALEN(key_kp_enter); return key_kp_enter;
|
|
|
|
|
case XKB_KEY_KP_Divide: *count = ALEN(key_kp_divide); return key_kp_divide;
|
|
|
|
|
case XKB_KEY_KP_Multiply: *count = ALEN(key_kp_multiply); return key_kp_multiply;
|
|
|
|
|
case XKB_KEY_KP_Subtract: *count = ALEN(key_kp_subtract); return key_kp_subtract;
|
|
|
|
|
case XKB_KEY_KP_Add: *count = ALEN(key_kp_add); return key_kp_add;
|
|
|
|
|
case XKB_KEY_KP_Separator: *count = ALEN(key_kp_separator); return key_kp_separator;
|
|
|
|
|
case XKB_KEY_KP_0: *count = ALEN(key_kp_0); return key_kp_0;
|
|
|
|
|
case XKB_KEY_KP_1: *count = ALEN(key_kp_1); return key_kp_1;
|
|
|
|
|
case XKB_KEY_KP_2: *count = ALEN(key_kp_2); return key_kp_2;
|
|
|
|
|
case XKB_KEY_KP_3: *count = ALEN(key_kp_3); return key_kp_3;
|
|
|
|
|
case XKB_KEY_KP_4: *count = ALEN(key_kp_4); return key_kp_4;
|
|
|
|
|
case XKB_KEY_KP_5: *count = ALEN(key_kp_5); return key_kp_5;
|
|
|
|
|
case XKB_KEY_KP_6: *count = ALEN(key_kp_6); return key_kp_6;
|
|
|
|
|
case XKB_KEY_KP_7: *count = ALEN(key_kp_7); return key_kp_7;
|
|
|
|
|
case XKB_KEY_KP_8: *count = ALEN(key_kp_8); return key_kp_8;
|
|
|
|
|
case XKB_KEY_KP_9: *count = ALEN(key_kp_9); return key_kp_9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-01-11 17:55:45 +01:00
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
static const struct key_data *
|
2020-01-11 17:55:45 +01:00
|
|
|
keymap_lookup(struct terminal *term, xkb_keysym_t sym, enum modifier mods)
|
|
|
|
|
{
|
2020-01-11 18:18:31 +01:00
|
|
|
size_t count;
|
2020-01-11 18:40:27 +01:00
|
|
|
const struct key_data *info = keymap_data_for_sym(sym, &count);
|
2020-01-11 17:55:45 +01:00
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
if (info == NULL)
|
2020-01-11 18:40:27 +01:00
|
|
|
return NULL;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
for (size_t j = 0; j < count; j++) {
|
|
|
|
|
if (info[j].modifiers != MOD_ANY && info[j].modifiers != mods)
|
|
|
|
|
continue;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
if (info[j].cursor_keys_mode != CURSOR_KEYS_DONTCARE &&
|
|
|
|
|
info[j].cursor_keys_mode != term->cursor_keys_mode)
|
|
|
|
|
continue;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
2020-01-11 18:18:31 +01:00
|
|
|
if (info[j].keypad_keys_mode != KEYPAD_DONTCARE &&
|
|
|
|
|
info[j].keypad_keys_mode != term->keypad_keys_mode)
|
|
|
|
|
continue;
|
2020-01-11 17:55:45 +01:00
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
return &info[j];
|
2020-01-11 17:55:45 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
return NULL;
|
2020-01-11 17:55:45 +01:00
|
|
|
}
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
static void
|
|
|
|
|
keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
|
|
|
|
uint32_t time, uint32_t key, uint32_t state)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2020-01-02 15:58:52 +01:00
|
|
|
struct terminal *term = wayl->kbd_focus;
|
2019-11-22 21:49:50 +01:00
|
|
|
|
2019-11-22 21:56:13 +01:00
|
|
|
/* Workaround buggy Sway 1.2 */
|
|
|
|
|
if (term == NULL) {
|
|
|
|
|
static bool have_warned = false;
|
|
|
|
|
if (!have_warned) {
|
|
|
|
|
have_warned = true;
|
|
|
|
|
LOG_WARN("compositor sent keyboard_key event without first sending keyboard_enter");
|
|
|
|
|
}
|
2019-11-22 22:28:41 +01:00
|
|
|
|
|
|
|
|
if (tll_length(wayl->terms) == 1) {
|
|
|
|
|
/* With only one terminal we *know* which one has focus */
|
|
|
|
|
term = tll_front(wayl->terms);
|
|
|
|
|
} else {
|
|
|
|
|
/* But with multiple windows we can't guess - ignore the event */
|
|
|
|
|
stop_repeater(wayl, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-22 21:56:13 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
assert(term != NULL);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
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;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
|
|
|
if (state == XKB_KEY_UP) {
|
2019-10-27 18:43:07 +01:00
|
|
|
stop_repeater(wayl, key);
|
2019-06-19 10:04:47 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key += 8;
|
2019-11-22 22:19:00 +01:00
|
|
|
bool should_repeat = xkb_keymap_key_repeats(wayl->kbd.xkb_keymap, key);
|
2019-10-27 18:43:07 +01:00
|
|
|
xkb_keysym_t sym = xkb_state_key_get_one_sym(wayl->kbd.xkb_state, key);
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2019-08-19 21:18:43 +02:00
|
|
|
#if 0
|
2019-07-09 14:27:26 +02:00
|
|
|
char foo[100];
|
|
|
|
|
xkb_keysym_get_name(sym, foo, sizeof(foo));
|
2019-08-19 21:16:47 +02:00
|
|
|
LOG_INFO("%s", foo);
|
2019-07-09 14:27:26 +02:00
|
|
|
#endif
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
xkb_compose_state_feed(wayl->kbd.xkb_compose_state, sym);
|
2019-07-09 10:00:54 +02:00
|
|
|
enum xkb_compose_status compose_status = xkb_compose_state_get_status(
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->kbd.xkb_compose_state);
|
2019-07-09 10:00:54 +02:00
|
|
|
|
2019-11-22 22:19:00 +01:00
|
|
|
if (compose_status == XKB_COMPOSE_COMPOSING) {
|
|
|
|
|
/* TODO: goto maybe_repeat? */
|
2019-07-09 10:00:54 +02:00
|
|
|
return;
|
2019-11-22 22:19:00 +01:00
|
|
|
}
|
2019-07-09 10:00:54 +02:00
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
xkb_mod_mask_t mods = xkb_state_serialize_mods(
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED);
|
|
|
|
|
//xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(wayl->kbd.xkb_state, key);
|
2019-06-22 20:31:53 +02:00
|
|
|
xkb_mod_mask_t consumed = 0x0;
|
2019-08-15 19:38:28 +02:00
|
|
|
xkb_mod_mask_t significant = ctrl | alt | shift | meta;
|
2019-06-19 10:04:47 +02:00
|
|
|
xkb_mod_mask_t effective_mods = mods & ~consumed & significant;
|
|
|
|
|
|
2019-08-27 17:23:28 +02:00
|
|
|
if (term->is_searching) {
|
2019-11-22 22:19:00 +01:00
|
|
|
if (should_repeat)
|
|
|
|
|
start_repeater(wayl, key - 8);
|
2020-03-08 15:28:47 +01:00
|
|
|
search_input(term, key, sym, effective_mods, serial);
|
2019-08-27 17:23:28 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
#if 0
|
|
|
|
|
for (size_t i = 0; i < 32; i++) {
|
|
|
|
|
if (mods & (1 << i)) {
|
2019-10-27 18:43:07 +01:00
|
|
|
LOG_INFO("%s", xkb_keymap_mod_get_name(wayl->kbd.xkb_keymap, i));
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
LOG_DBG("sym=%u, mod=0x%08x, consumed=0x%08x, significant=0x%08x, "
|
2019-11-22 22:19:00 +01:00
|
|
|
"effective=0x%08x, repeats=%d",
|
|
|
|
|
sym, mods, consumed, significant, effective_mods, should_repeat);
|
2019-07-09 14:27:26 +02:00
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
/*
|
2020-03-08 15:17:29 +01:00
|
|
|
* User configurable bindings
|
2020-01-11 18:59:46 +01:00
|
|
|
*/
|
2020-03-08 15:17:29 +01:00
|
|
|
tll_foreach(wayl->kbd.bindings.key, it) {
|
|
|
|
|
if (it->item.mods == effective_mods && it->item.sym == sym) {
|
2020-03-08 15:28:47 +01:00
|
|
|
input_execute_binding(term, it->item.action, serial);
|
2019-12-21 15:27:17 +01:00
|
|
|
goto maybe_repeat;
|
2019-08-27 17:23:28 +02:00
|
|
|
}
|
2019-07-11 12:16:50 +02:00
|
|
|
}
|
|
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
/*
|
|
|
|
|
* Keys generating escape sequences
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
const struct key_data *keymap = keymap_lookup(term, sym, keymap_mods);
|
|
|
|
|
if (keymap != NULL) {
|
|
|
|
|
term_to_slave(term, keymap->seq, strlen(keymap->seq));
|
2019-11-22 22:02:50 +01:00
|
|
|
|
2020-01-11 18:40:27 +01:00
|
|
|
term_reset_view(term);
|
|
|
|
|
selection_cancel(term);
|
2020-01-11 17:55:45 +01:00
|
|
|
goto maybe_repeat;
|
2020-01-11 18:40:27 +01:00
|
|
|
}
|
2019-06-22 20:40:28 +02:00
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
/*
|
|
|
|
|
* Compose, and maybe emit "normal" character
|
|
|
|
|
*/
|
|
|
|
|
|
2019-11-22 22:02:50 +01:00
|
|
|
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);
|
2019-12-02 21:32:09 +01:00
|
|
|
} else if (compose_status == XKB_COMPOSE_CANCELLED) {
|
|
|
|
|
goto maybe_repeat;
|
2019-11-22 22:02:50 +01:00
|
|
|
} else {
|
|
|
|
|
count = xkb_state_key_get_utf8(
|
|
|
|
|
wayl->kbd.xkb_state, key, (char *)buf, sizeof(buf));
|
|
|
|
|
}
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2020-01-11 18:59:46 +01:00
|
|
|
if (count == 0)
|
|
|
|
|
goto maybe_repeat;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
2019-07-26 18:49:09 +02:00
|
|
|
#define is_control_key(x) ((x) >= 0x40 && (x) <= 0x7f)
|
|
|
|
|
#define IS_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f))
|
|
|
|
|
|
2020-01-20 18:38:50 +01:00
|
|
|
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) {
|
|
|
|
|
/*
|
|
|
|
|
* When the alt modifier is pressed, we do one out of three things:
|
|
|
|
|
*
|
|
|
|
|
* 1. we prefix the output bytes with ESC
|
|
|
|
|
* 2. we set the 8:th bit in the output byte
|
|
|
|
|
* 3. we ignore the alt modifier
|
|
|
|
|
*
|
|
|
|
|
* #1 is configured with \E[?1036, and is on by default
|
|
|
|
|
*
|
|
|
|
|
* If #1 has been disabled, we use #2, *if* it's a single
|
|
|
|
|
* byte we're emitting. Since this is an UTF-8 terminal,
|
|
|
|
|
* we then UTF8-encode the 8-bit character. #2 is
|
|
|
|
|
* configured with \E[?1034, and is on by default.
|
|
|
|
|
*
|
|
|
|
|
* Lastly, if both #1 and #2 have been disabled, the alt
|
|
|
|
|
* modifier is ignored.
|
|
|
|
|
*/
|
|
|
|
|
if (term->meta.esc_prefix) {
|
|
|
|
|
term_to_slave(term, "\x1b", 1);
|
|
|
|
|
term_to_slave(term, buf, count);
|
2020-01-11 18:59:46 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-20 18:38:50 +01:00
|
|
|
else if (term->meta.eight_bit && count == 1) {
|
|
|
|
|
const wchar_t wc = 0x80 | buf[0];
|
|
|
|
|
|
|
|
|
|
char utf8[8];
|
|
|
|
|
mbstate_t ps = {0};
|
|
|
|
|
size_t chars = wcrtomb(utf8, wc, &ps);
|
2020-01-11 18:59:46 +01:00
|
|
|
|
2020-01-20 18:38:50 +01:00
|
|
|
if (chars != (size_t)-1)
|
|
|
|
|
term_to_slave(term, utf8, chars);
|
|
|
|
|
else
|
|
|
|
|
term_to_slave(term, buf, count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
/* Alt ignored */
|
2020-01-11 18:59:46 +01:00
|
|
|
term_to_slave(term, buf, count);
|
|
|
|
|
}
|
2020-01-20 18:38:50 +01:00
|
|
|
} else
|
|
|
|
|
term_to_slave(term, buf, count);
|
|
|
|
|
}
|
2020-01-11 18:59:46 +01:00
|
|
|
|
|
|
|
|
term_reset_view(term);
|
|
|
|
|
selection_cancel(term);
|
2019-11-22 22:19:00 +01:00
|
|
|
|
|
|
|
|
maybe_repeat:
|
2020-01-21 18:51:04 +01:00
|
|
|
clock_gettime(
|
|
|
|
|
term->wl->presentation_clock_id, &term->render.input_time);
|
|
|
|
|
|
2019-11-22 22:19:00 +01:00
|
|
|
if (should_repeat)
|
|
|
|
|
start_repeater(wayl, key - 8);
|
|
|
|
|
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2019-06-19 10:04:47 +02:00
|
|
|
|
|
|
|
|
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(
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
2019-07-05 15:13:06 +02:00
|
|
|
|
|
|
|
|
/* Update state of modifiers we're interrested in for e.g mouse events */
|
2019-10-27 18:43:07 +01:00
|
|
|
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);
|
2019-11-28 19:35:47 +01:00
|
|
|
|
2020-03-02 18:47:17 +01:00
|
|
|
if (wayl->kbd_focus && wayl->kbd_focus->active_surface == TERM_SURF_GRID)
|
2020-01-02 15:58:52 +01:00
|
|
|
term_xcursor_update(wayl->kbd_focus);
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
|
|
|
|
|
int32_t rate, int32_t delay)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2019-06-19 10:04:47 +02:00
|
|
|
LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay);
|
2019-10-27 18:43:07 +01:00
|
|
|
wayl->kbd.repeat.rate = rate;
|
|
|
|
|
wayl->kbd.repeat.delay = delay;
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2019-10-27 18:44:58 +01:00
|
|
|
input_repeat(struct wayland *wayl, uint32_t key)
|
2019-06-19 10:04:47 +02:00
|
|
|
{
|
2019-11-22 21:49:50 +01:00
|
|
|
keyboard_key(wayl, NULL, wayl->input_serial, 0, key, XKB_KEY_DOWN);
|
2019-06-19 10:04:47 +02:00
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
|
2020-02-29 09:32:22 +01:00
|
|
|
static bool
|
|
|
|
|
is_top_left(const struct terminal *term, int x, int y)
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
return (
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_LEFT && y < 10 * term->scale) ||
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_TOP && x < (10 + csd_border_size) * term->scale));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
is_top_right(const struct terminal *term, int x, int y)
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
return (
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_RIGHT && y < 10 * term->scale) ||
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_TOP && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
is_bottom_left(const struct terminal *term, int x, int y)
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
int csd_title_size = term->conf->csd.title_height;
|
|
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
return (
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_LEFT && y > csd_title_size * term->scale + term->height) ||
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x < (10 + csd_border_size) * term->scale));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
is_bottom_right(const struct terminal *term, int x, int y)
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
int csd_title_size = term->conf->csd.title_height;
|
|
|
|
|
int csd_border_size = term->conf->csd.border_width;
|
2020-02-29 09:32:22 +01:00
|
|
|
return (
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_RIGHT && y > csd_title_size * term->scale + term->height) ||
|
|
|
|
|
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
|
|
|
|
{
|
2020-03-02 18:42:49 +01:00
|
|
|
if (is_top_left(term, x, y)) return "top_left_corner";
|
|
|
|
|
else if (is_top_right(term, x, y)) return "top_right_corner";
|
|
|
|
|
else if (is_bottom_left(term, x, y)) return "bottom_left_corner";
|
|
|
|
|
else if (is_bottom_right(term, x, y)) return "bottom_right_corner";
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return "left_side";
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return "right_side";
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_TOP) return "top_side";
|
|
|
|
|
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return"bottom_side";
|
2020-02-29 09:32:22 +01:00
|
|
|
else {
|
|
|
|
|
assert(false);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 10:44:57 +02:00
|
|
|
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)
|
|
|
|
|
{
|
2019-11-28 19:46:27 +01:00
|
|
|
assert(surface != NULL);
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2020-02-29 12:09:28 +01:00
|
|
|
struct wl_window *win = wl_surface_get_user_data(surface);
|
|
|
|
|
struct terminal *term = win->term;
|
2019-10-27 18:43:07 +01:00
|
|
|
|
2019-11-29 22:12:54 +01:00
|
|
|
LOG_DBG("pointer-enter: surface = %p, new-moused = %p", surface, term);
|
|
|
|
|
|
2020-01-02 15:58:52 +01:00
|
|
|
wayl->mouse_focus = term;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2019-09-26 18:41:26 +02:00
|
|
|
int x = wl_fixed_to_int(surface_x) * term->scale;
|
|
|
|
|
int y = wl_fixed_to_int(surface_y) * term->scale;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2020-02-24 22:40:24 +01:00
|
|
|
switch ((term->active_surface = term_surface_kind(term, surface))) {
|
|
|
|
|
case TERM_SURF_GRID:
|
|
|
|
|
wayl->mouse.col = x / term->cell_width;
|
|
|
|
|
wayl->mouse.row = y / term->cell_height;
|
|
|
|
|
term_xcursor_update(term);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_SEARCH:
|
2020-02-29 15:29:00 +01:00
|
|
|
case TERM_SURF_TITLE:
|
|
|
|
|
term->xcursor = "left_ptr";
|
|
|
|
|
render_xcursor_set(term);
|
2020-02-24 22:40:24 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
2020-02-29 09:32:22 +01:00
|
|
|
case TERM_SURF_BORDER_BOTTOM:
|
|
|
|
|
term->xcursor = xcursor_for_csd_border(term, x, y);
|
2020-02-25 20:31:13 +01:00
|
|
|
render_xcursor_set(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
2020-03-03 18:22:32 +01:00
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
2020-03-02 20:29:28 +01:00
|
|
|
term->xcursor = "left_ptr";
|
|
|
|
|
render_xcursor_set(term);
|
2020-03-06 19:16:54 +01:00
|
|
|
render_refresh_csd(term);
|
2020-03-02 20:29:28 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
case TERM_SURF_NONE:
|
|
|
|
|
assert(false);
|
2020-02-24 22:40:24 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
uint32_t serial, struct wl_surface *surface)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2020-01-02 15:58:52 +01:00
|
|
|
struct terminal *old_moused = wayl->mouse_focus;
|
2019-11-29 22:13:19 +01:00
|
|
|
|
|
|
|
|
LOG_DBG("pointer-leave: surface = %p, old-moused = %p", surface, old_moused);
|
|
|
|
|
|
2020-02-15 19:02:43 +01:00
|
|
|
if (wayl->pointer.xcursor_callback != NULL) {
|
|
|
|
|
/* A cursor frame callback may never be called if the pointer leaves our surface */
|
|
|
|
|
wl_callback_destroy(wayl->pointer.xcursor_callback);
|
|
|
|
|
wayl->pointer.xcursor_callback = NULL;
|
|
|
|
|
wayl->pointer.pending_terminal = NULL;
|
|
|
|
|
wayl->pointer.xcursor = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-29 12:08:59 +01:00
|
|
|
/* Reset mouse state */
|
|
|
|
|
memset(&wayl->mouse, 0, sizeof(wayl->mouse));
|
|
|
|
|
|
2020-01-02 15:58:52 +01:00
|
|
|
wayl->mouse_focus = NULL;
|
2019-11-29 22:13:19 +01:00
|
|
|
if (old_moused == NULL) {
|
2019-11-28 19:47:00 +01:00
|
|
|
LOG_WARN(
|
|
|
|
|
"compositor sent pointer_leave event without a pointer_enter "
|
|
|
|
|
"event: surface=%p", surface);
|
2020-02-24 22:41:46 +01:00
|
|
|
} else {
|
2020-02-29 12:09:28 +01:00
|
|
|
if (surface != NULL) {
|
|
|
|
|
/* Sway 1.4 sends this event with a NULL surface when we destroy the window */
|
2020-02-29 15:29:58 +01:00
|
|
|
const struct wl_window *win __attribute__((unused))
|
|
|
|
|
= wl_surface_get_user_data(surface);
|
2020-02-29 12:09:28 +01:00
|
|
|
assert(old_moused == win->term);
|
|
|
|
|
}
|
2020-03-02 20:29:28 +01:00
|
|
|
|
|
|
|
|
enum term_surface active_surface = old_moused->active_surface;
|
|
|
|
|
|
2020-02-24 22:41:46 +01:00
|
|
|
old_moused->active_surface = TERM_SURF_NONE;
|
2019-11-29 22:13:19 +01:00
|
|
|
term_xcursor_update(old_moused);
|
2020-03-02 20:29:28 +01:00
|
|
|
|
|
|
|
|
switch (active_surface) {
|
|
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
2020-03-03 18:22:32 +01:00
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
|
|
|
|
if (old_moused->is_shutting_down)
|
|
|
|
|
break;
|
|
|
|
|
|
2020-03-06 19:16:54 +01:00
|
|
|
render_refresh_csd(old_moused);
|
2020-03-02 20:29:28 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_NONE:
|
|
|
|
|
case TERM_SURF_GRID:
|
|
|
|
|
case TERM_SURF_SEARCH:
|
|
|
|
|
case TERM_SURF_TITLE:
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
|
|
|
|
case TERM_SURF_BORDER_BOTTOM:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-24 22:41:46 +01:00
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2020-01-02 15:58:52 +01:00
|
|
|
struct terminal *term = wayl->mouse_focus;
|
2020-02-29 15:38:04 +01:00
|
|
|
struct wl_window *win = term->window;
|
2019-11-22 21:49:50 +01:00
|
|
|
|
2019-11-22 21:56:13 +01:00
|
|
|
/* Workaround buggy Sway 1.2 */
|
|
|
|
|
if (term == NULL) {
|
|
|
|
|
static bool have_warned = false;
|
|
|
|
|
if (!have_warned) {
|
|
|
|
|
have_warned = true;
|
|
|
|
|
LOG_WARN("compositor sent pointer_motion event without first sending pointer_enter");
|
|
|
|
|
}
|
2019-11-22 22:28:41 +01:00
|
|
|
|
|
|
|
|
if (tll_length(wayl->terms) == 1) {
|
|
|
|
|
/* With only one terminal we *know* which one has focus */
|
|
|
|
|
term = tll_front(wayl->terms);
|
|
|
|
|
} else {
|
|
|
|
|
/* But with multiple windows we can't guess - ignore the event */
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-11-22 21:56:13 +01:00
|
|
|
}
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
assert(term != NULL);
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2019-09-26 18:39:49 +02:00
|
|
|
int x = wl_fixed_to_int(surface_x) * term->scale;
|
|
|
|
|
int y = wl_fixed_to_int(surface_y) * term->scale;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
wayl->mouse.x = x;
|
|
|
|
|
wayl->mouse.y = y;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
switch (term->active_surface) {
|
|
|
|
|
case TERM_SURF_NONE:
|
|
|
|
|
case TERM_SURF_SEARCH:
|
2020-03-02 20:29:28 +01:00
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
|
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
2020-02-29 15:38:04 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
case TERM_SURF_TITLE:
|
2020-02-29 15:38:04 +01:00
|
|
|
/* We've started a 'move' timer, but user started dragging
|
|
|
|
|
* right away - abort the timer and initiate the actual move
|
|
|
|
|
* right away */
|
|
|
|
|
if (wayl->mouse.button == BTN_LEFT && win->csd.move_timeout_fd != -1) {
|
|
|
|
|
fdm_del(wayl->fdm, win->csd.move_timeout_fd);
|
|
|
|
|
win->csd.move_timeout_fd = -1;
|
|
|
|
|
xdg_toplevel_move(win->xdg_toplevel, wayl->seat, win->csd.serial);
|
|
|
|
|
}
|
2020-02-25 20:31:13 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
|
|
|
|
case TERM_SURF_BORDER_BOTTOM:
|
2020-02-29 09:32:22 +01:00
|
|
|
term->xcursor = xcursor_for_csd_border(term, x, y);
|
|
|
|
|
render_xcursor_set(term);
|
2020-02-25 20:31:13 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
case TERM_SURF_GRID: {
|
|
|
|
|
int col = (x - term->margins.left) / term->cell_width;
|
|
|
|
|
int row = (y - term->margins.top) / term->cell_height;
|
2020-02-25 20:31:13 +01:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
if (col < 0 || row < 0 || col >= term->cols || row >= term->rows)
|
|
|
|
|
return;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
bool update_selection = wayl->mouse.button == BTN_LEFT;
|
|
|
|
|
bool update_selection_early = term->selection.end.row == -1;
|
2019-07-11 11:09:34 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
if (update_selection && update_selection_early)
|
|
|
|
|
selection_update(term, col, row);
|
2019-07-11 09:51:51 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
if (col == wayl->mouse.col && row == wayl->mouse.row)
|
|
|
|
|
break;
|
2019-07-11 09:51:51 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
wayl->mouse.col = col;
|
|
|
|
|
wayl->mouse.row = row;
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
if (update_selection && !update_selection_early)
|
|
|
|
|
selection_update(term, col, row);
|
2019-07-05 15:13:06 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
term_mouse_motion(
|
2019-11-30 17:11:00 +01:00
|
|
|
term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col);
|
2020-02-29 15:29:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-29 12:52:55 +01:00
|
|
|
static bool
|
|
|
|
|
fdm_csd_move(struct fdm *fdm, int fd, int events, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct wl_window *win = data;
|
|
|
|
|
struct wayland *wayl = win->term->wl;
|
|
|
|
|
|
|
|
|
|
fdm_del(fdm, fd);
|
|
|
|
|
win->csd.move_timeout_fd = -1;
|
|
|
|
|
|
|
|
|
|
xdg_toplevel_move(win->xdg_toplevel, wayl->seat, win->csd.serial);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 10:44:57 +02:00
|
|
|
static void
|
|
|
|
|
wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
|
|
|
|
|
{
|
2019-07-05 14:24:51 +02:00
|
|
|
LOG_DBG("BUTTON: button=%x, state=%u", button, state);
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
2020-01-02 15:58:52 +01:00
|
|
|
struct terminal *term = wayl->mouse_focus;
|
2019-07-05 14:24:51 +02:00
|
|
|
|
2019-11-28 19:26:52 +01:00
|
|
|
/* Workaround buggy Sway 1.2 */
|
|
|
|
|
if (term == NULL) {
|
|
|
|
|
static bool have_warned = false;
|
|
|
|
|
if (!have_warned) {
|
|
|
|
|
have_warned = true;
|
|
|
|
|
LOG_WARN("compositor sent pointer_button event without first sending pointer_enter");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tll_length(wayl->terms) == 1) {
|
|
|
|
|
/* With only one terminal we *know* which one has focus */
|
|
|
|
|
term = tll_front(wayl->terms);
|
|
|
|
|
} else {
|
|
|
|
|
/* But with multiple windows we can't guess - ignore the event */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(term != NULL);
|
2020-02-24 22:42:04 +01:00
|
|
|
|
2020-02-29 11:55:43 +01:00
|
|
|
/* Update double/triple click state */
|
|
|
|
|
if (state == 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;
|
|
|
|
|
|
|
|
|
|
wayl->mouse.button = button; /* For motion events */
|
|
|
|
|
wayl->mouse.last_button = button;
|
|
|
|
|
wayl->mouse.last_time = now;
|
|
|
|
|
} else
|
|
|
|
|
wayl->mouse.button = 0; /* For motion events */
|
|
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
switch (term->active_surface) {
|
|
|
|
|
case TERM_SURF_TITLE:
|
2020-02-29 11:56:16 +01:00
|
|
|
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
|
|
|
|
2020-02-29 12:52:55 +01:00
|
|
|
struct wl_window *win = term->window;
|
|
|
|
|
|
2020-02-29 11:56:16 +01:00
|
|
|
/* Toggle maximized state on double-click */
|
|
|
|
|
if (button == BTN_LEFT && wayl->mouse.count == 2) {
|
2020-02-29 12:52:55 +01:00
|
|
|
if (win->is_maximized)
|
|
|
|
|
xdg_toplevel_unset_maximized(win->xdg_toplevel);
|
2020-02-29 11:56:16 +01:00
|
|
|
else
|
2020-02-29 12:52:55 +01:00
|
|
|
xdg_toplevel_set_maximized(win->xdg_toplevel);
|
2020-02-26 13:23:11 +01:00
|
|
|
}
|
2020-02-29 11:56:16 +01:00
|
|
|
|
2020-02-29 12:52:55 +01:00
|
|
|
else if (button == BTN_LEFT && win->csd.move_timeout_fd == -1) {
|
|
|
|
|
const struct itimerspec timeout = {
|
2020-03-01 13:20:07 +01:00
|
|
|
.it_value = {.tv_nsec = 200000000},
|
2020-02-29 12:52:55 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
|
|
|
|
|
if (fd >= 0 &&
|
|
|
|
|
timerfd_settime(fd, 0, &timeout, NULL) == 0 &&
|
|
|
|
|
fdm_add(wayl->fdm, fd, EPOLLIN, &fdm_csd_move, win))
|
|
|
|
|
{
|
|
|
|
|
win->csd.move_timeout_fd = fd;
|
|
|
|
|
win->csd.serial = serial;
|
|
|
|
|
} else {
|
|
|
|
|
LOG_ERRNO("failed to configure XDG toplevel move timer FD");
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-26 13:23:11 +01:00
|
|
|
}
|
2020-02-29 15:38:04 +01:00
|
|
|
|
|
|
|
|
else if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
|
|
|
|
|
struct wl_window *win = term->window;
|
|
|
|
|
if (win->csd.move_timeout_fd != -1) {
|
|
|
|
|
fdm_del(wayl->fdm, win->csd.move_timeout_fd);
|
|
|
|
|
win->csd.move_timeout_fd = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-25 20:31:13 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BORDER_LEFT:
|
|
|
|
|
case TERM_SURF_BORDER_RIGHT:
|
|
|
|
|
case TERM_SURF_BORDER_TOP:
|
|
|
|
|
case TERM_SURF_BORDER_BOTTOM: {
|
2020-03-01 11:43:58 +01:00
|
|
|
static const enum xdg_toplevel_resize_edge map[] = {
|
2020-02-25 20:31:13 +01:00
|
|
|
[TERM_SURF_BORDER_LEFT] = XDG_TOPLEVEL_RESIZE_EDGE_LEFT,
|
|
|
|
|
[TERM_SURF_BORDER_RIGHT] = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT,
|
|
|
|
|
[TERM_SURF_BORDER_TOP] = XDG_TOPLEVEL_RESIZE_EDGE_TOP,
|
|
|
|
|
[TERM_SURF_BORDER_BOTTOM] = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM,
|
|
|
|
|
};
|
2020-02-29 09:32:22 +01:00
|
|
|
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
2020-03-01 11:43:58 +01:00
|
|
|
enum xdg_toplevel_resize_edge resize_type;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
int x = wayl->mouse.x;
|
|
|
|
|
int y = wayl->mouse.y;
|
2020-02-29 09:32:22 +01:00
|
|
|
|
|
|
|
|
if (is_top_left(term, x, y))
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
|
|
|
|
else if (is_top_right(term, x, y))
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
|
|
|
|
else if (is_bottom_left(term, x, y))
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
|
|
|
|
else if (is_bottom_right(term, x, y))
|
|
|
|
|
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
|
|
|
|
else
|
|
|
|
|
resize_type = map[term->active_surface];
|
|
|
|
|
|
|
|
|
|
xdg_toplevel_resize(
|
|
|
|
|
term->window->xdg_toplevel, term->wl->seat, serial, resize_type);
|
|
|
|
|
}
|
2020-02-25 20:31:13 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 20:29:28 +01:00
|
|
|
case TERM_SURF_BUTTON_MINIMIZE:
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
|
|
|
|
xdg_toplevel_set_minimized(term->window->xdg_toplevel);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
|
|
|
if (term->window->is_maximized)
|
|
|
|
|
xdg_toplevel_unset_maximized(term->window->xdg_toplevel);
|
|
|
|
|
else
|
|
|
|
|
xdg_toplevel_set_maximized(term->window->xdg_toplevel);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TERM_SURF_BUTTON_CLOSE:
|
|
|
|
|
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED)
|
|
|
|
|
term_shutdown(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-02-25 20:31:13 +01:00
|
|
|
case TERM_SURF_SEARCH:
|
|
|
|
|
break;
|
2020-02-24 22:42:04 +01:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
case TERM_SURF_GRID: {
|
|
|
|
|
search_cancel(term);
|
|
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
|
case WL_POINTER_BUTTON_STATE_PRESSED: {
|
|
|
|
|
if (button == BTN_LEFT) {
|
|
|
|
|
switch (wayl->mouse.count) {
|
|
|
|
|
case 1:
|
|
|
|
|
selection_start(
|
|
|
|
|
term, wayl->mouse.col, wayl->mouse.row,
|
|
|
|
|
wayl->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL);
|
|
|
|
|
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;
|
|
|
|
|
}
|
2020-03-12 10:20:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
for (size_t i = 0; i < ALEN(wayl->conf->bindings.mouse); i++) {
|
|
|
|
|
const struct mouse_binding *binding =
|
|
|
|
|
&wayl->conf->bindings.mouse[i];
|
|
|
|
|
|
|
|
|
|
if (binding->button != button) {
|
|
|
|
|
/* Wrong button */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (binding->count != wayl->mouse.count) {
|
|
|
|
|
/* Not correct click count */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_execute_binding(term, binding->action, serial);
|
2020-03-12 10:46:27 +01:00
|
|
|
break;
|
2020-02-29 15:29:00 +01:00
|
|
|
}
|
|
|
|
|
selection_cancel(term);
|
|
|
|
|
}
|
2019-08-27 19:44:19 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-08-06 19:32:06 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
case WL_POINTER_BUTTON_STATE_RELEASED:
|
|
|
|
|
if (button != BTN_LEFT || term->selection.end.col == -1)
|
|
|
|
|
selection_cancel(term);
|
|
|
|
|
else
|
|
|
|
|
selection_finalize(term, serial);
|
2019-08-06 19:32:06 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col);
|
|
|
|
|
break;
|
2019-07-11 16:42:59 +02:00
|
|
|
}
|
2019-07-05 14:24:51 +02:00
|
|
|
break;
|
2019-07-17 21:30:57 +02:00
|
|
|
}
|
2019-07-05 14:24:51 +02:00
|
|
|
|
2020-02-29 15:29:00 +01:00
|
|
|
case TERM_SURF_NONE:
|
|
|
|
|
assert(false);
|
2019-07-05 14:24:51 +02:00
|
|
|
break;
|
2020-02-29 15:29:00 +01:00
|
|
|
|
2019-07-05 14:24:51 +02:00
|
|
|
}
|
2019-07-05 10:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-26 18:47:56 +02:00
|
|
|
static void
|
2019-10-27 18:43:07 +01:00
|
|
|
mouse_scroll(struct wayland *wayl, int amount)
|
2019-07-26 18:47:56 +02:00
|
|
|
{
|
2020-01-02 15:58:52 +01:00
|
|
|
struct terminal *term = wayl->mouse_focus;
|
2019-10-27 18:43:07 +01:00
|
|
|
assert(term != NULL);
|
|
|
|
|
|
2019-07-26 18:47:56 +02:00
|
|
|
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);
|
|
|
|
|
|
2019-08-19 21:16:47 +02:00
|
|
|
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
|
|
|
|
|
*/
|
|
|
|
|
|
2019-11-30 16:48:58 +01:00
|
|
|
static xkb_keycode_t key_arrow_up = 0;
|
|
|
|
|
static xkb_keycode_t key_arrow_down = 0;
|
|
|
|
|
|
|
|
|
|
if (key_arrow_up == 0) {
|
|
|
|
|
key_arrow_up = xkb_keymap_key_by_name(wayl->kbd.xkb_keymap, "UP");
|
|
|
|
|
key_arrow_down = xkb_keymap_key_by_name(wayl->kbd.xkb_keymap, "DOWN");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xkb_keycode_t key = button == BTN_BACK ? key_arrow_up : key_arrow_down;
|
2019-08-19 21:16:47 +02:00
|
|
|
|
|
|
|
|
for (int i = 0; i < amount; i++)
|
2019-11-30 16:54:41 +01:00
|
|
|
keyboard_key(wayl, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_DOWN);
|
|
|
|
|
keyboard_key(wayl, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_UP);
|
2019-08-19 21:16:47 +02:00
|
|
|
} else {
|
|
|
|
|
for (int i = 0; i < amount; i++)
|
2019-11-30 17:11:00 +01:00
|
|
|
term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col);
|
2020-01-04 21:35:38 +01:00
|
|
|
term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col);
|
2019-08-19 21:16:47 +02:00
|
|
|
|
|
|
|
|
scrollback(term, amount);
|
|
|
|
|
}
|
2019-07-26 18:47:56 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-05 10:44:57 +02:00
|
|
|
static void
|
|
|
|
|
wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
uint32_t time, uint32_t axis, wl_fixed_t value)
|
|
|
|
|
{
|
2019-07-26 18:47:56 +02:00
|
|
|
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
|
|
|
|
|
if (wayl->mouse.have_discrete)
|
2019-07-26 18:47:56 +02:00
|
|
|
return;
|
2019-07-10 09:15:37 +02:00
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
mouse_scroll(wayl, wl_fixed_to_int(value));
|
2019-07-26 18:47:56 +02:00
|
|
|
}
|
2019-07-10 09:15:37 +02:00
|
|
|
|
2019-07-26 18:47:56 +02:00
|
|
|
static void
|
|
|
|
|
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
|
|
|
|
uint32_t axis, int32_t discrete)
|
|
|
|
|
{
|
2019-07-10 09:15:37 +02:00
|
|
|
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
wayl->mouse.have_discrete = true;
|
|
|
|
|
mouse_scroll(wayl, discrete);
|
2019-07-05 10:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
|
|
|
|
{
|
2019-10-27 18:43:07 +01:00
|
|
|
struct wayland *wayl = data;
|
|
|
|
|
wayl->mouse.have_discrete = false;
|
2019-07-05 10:44:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|