mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
This fixes an assertion when holding in ctrl+shift+c (copy to clipboard); subsequent "presses" would set the clipboard serial to 0, which we would then assert on in the next iteration when we tried to cancel the previous copy.
616 lines
19 KiB
C
616 lines
19 KiB
C
#include "input.h"
|
|
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <threads.h>
|
|
#include <locale.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/time.h>
|
|
#include <sys/timerfd.h>
|
|
|
|
#include <linux/input-event-codes.h>
|
|
|
|
#include <xkbcommon/xkbcommon.h>
|
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
|
#include <xkbcommon/xkbcommon-compose.h>
|
|
|
|
#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;
|
|
|
|
assert(wayl->focused == NULL ||
|
|
wayl_terminal_from_surface(wayl, surface) == wayl->focused);
|
|
|
|
stop_repeater(wayl, -1);
|
|
if (wayl->focused != NULL)
|
|
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, wayl->input_serial, 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,
|
|
};
|