Merge branch 'multi-seat'

Closes #32
This commit is contained in:
Daniel Eklöf 2020-07-11 08:30:47 +02:00
commit ed0cb06b48
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
15 changed files with 895 additions and 709 deletions

View file

@ -22,6 +22,7 @@
* **blink** option to `footrc`; a boolean that lets you control * **blink** option to `footrc`; a boolean that lets you control
whether the cursor should blink or not by default. Note that whether the cursor should blink or not by default. Note that
applications can override this. applications can override this.
* Multi-seat support
### Changed ### Changed

View file

@ -46,6 +46,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator.
* Scrollback search * Scrollback search
* Color emoji support * Color emoji support
* Server/daemon mode (one master process, many windows) * Server/daemon mode (one master process, many windows)
* Multi-seat
* [Synchronized Updates](https://gitlab.freedesktop.org/terminal-wg/specifications/-/merge_requests/2) support * [Synchronized Updates](https://gitlab.freedesktop.org/terminal-wg/specifications/-/merge_requests/2) support
* [Sixel image support](https://en.wikipedia.org/wiki/Sixel) * [Sixel image support](https://en.wikipedia.org/wiki/Sixel)

454
input.c
View file

@ -33,8 +33,8 @@
#include "vt.h" #include "vt.h"
static void static void
execute_binding(struct terminal *term, enum bind_action_normal action, execute_binding(struct seat *seat, struct terminal *term,
uint32_t serial) enum bind_action_normal action, uint32_t serial)
{ {
switch (action) { switch (action) {
case BIND_ACTION_NONE: case BIND_ACTION_NONE:
@ -49,16 +49,16 @@ execute_binding(struct terminal *term, enum bind_action_normal action,
break; break;
case BIND_ACTION_CLIPBOARD_COPY: case BIND_ACTION_CLIPBOARD_COPY:
selection_to_clipboard(term, serial); selection_to_clipboard(seat, term, serial);
break; break;
case BIND_ACTION_CLIPBOARD_PASTE: case BIND_ACTION_CLIPBOARD_PASTE:
selection_from_clipboard(term, serial); selection_from_clipboard(seat, term, serial);
term_reset_view(term); term_reset_view(term);
break; break;
case BIND_ACTION_PRIMARY_PASTE: case BIND_ACTION_PRIMARY_PASTE:
selection_from_primary(term); selection_from_primary(seat, term);
break; break;
case BIND_ACTION_SEARCH_START: case BIND_ACTION_SEARCH_START:
@ -206,57 +206,58 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
LOG_DBG("keyboard_keymap: keyboard=%p (format=%u, size=%u)", LOG_DBG("keyboard_keymap: keyboard=%p (format=%u, size=%u)",
wl_keyboard, format, size); wl_keyboard, format, size);
struct wayland *wayl = data; struct seat *seat = data;
struct wayland *wayl = seat->wayl;
char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); char *map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (wayl->kbd.xkb_compose_state != NULL) { if (seat->kbd.xkb_compose_state != NULL) {
xkb_compose_state_unref(wayl->kbd.xkb_compose_state); xkb_compose_state_unref(seat->kbd.xkb_compose_state);
wayl->kbd.xkb_compose_state = NULL; seat->kbd.xkb_compose_state = NULL;
} }
if (wayl->kbd.xkb_compose_table != NULL) { if (seat->kbd.xkb_compose_table != NULL) {
xkb_compose_table_unref(wayl->kbd.xkb_compose_table); xkb_compose_table_unref(seat->kbd.xkb_compose_table);
wayl->kbd.xkb_compose_table = NULL; seat->kbd.xkb_compose_table = NULL;
} }
if (wayl->kbd.xkb_keymap != NULL) { if (seat->kbd.xkb_keymap != NULL) {
xkb_keymap_unref(wayl->kbd.xkb_keymap); xkb_keymap_unref(seat->kbd.xkb_keymap);
wayl->kbd.xkb_keymap = NULL; seat->kbd.xkb_keymap = NULL;
} }
if (wayl->kbd.xkb_state != NULL) { if (seat->kbd.xkb_state != NULL) {
xkb_state_unref(wayl->kbd.xkb_state); xkb_state_unref(seat->kbd.xkb_state);
wayl->kbd.xkb_state = NULL; seat->kbd.xkb_state = NULL;
} }
if (wayl->kbd.xkb != NULL) { if (seat->kbd.xkb != NULL) {
xkb_context_unref(wayl->kbd.xkb); xkb_context_unref(seat->kbd.xkb);
wayl->kbd.xkb = NULL; seat->kbd.xkb = NULL;
} }
tll_foreach(wayl->kbd.bindings.key, it) tll_foreach(seat->kbd.bindings.key, it)
tll_free(it->item.bind.key_codes); tll_free(it->item.bind.key_codes);
tll_free(wayl->kbd.bindings.key); tll_free(seat->kbd.bindings.key);
tll_foreach(wayl->kbd.bindings.search, it) tll_foreach(seat->kbd.bindings.search, it)
tll_free(it->item.bind.key_codes); tll_free(it->item.bind.key_codes);
tll_free(wayl->kbd.bindings.search); tll_free(seat->kbd.bindings.search);
wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); seat->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
wayl->kbd.xkb_keymap = xkb_keymap_new_from_string( seat->kbd.xkb_keymap = xkb_keymap_new_from_string(
wayl->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, seat->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS); XKB_KEYMAP_COMPILE_NO_FLAGS);
/* TODO: initialize in enter? */ /* TODO: initialize in enter? */
wayl->kbd.xkb_state = xkb_state_new(wayl->kbd.xkb_keymap); seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap);
wayl->kbd.mod_shift = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Shift"); seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift");
wayl->kbd.mod_alt = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod1") ; seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ;
wayl->kbd.mod_ctrl = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Control"); seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control");
wayl->kbd.mod_meta = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod4"); seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4");
/* Compose (dead keys) */ /* Compose (dead keys) */
wayl->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
wayl->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
wayl->kbd.xkb_compose_state = xkb_compose_state_new( seat->kbd.xkb_compose_state = xkb_compose_state_new(
wayl->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
munmap(map_str, size); munmap(map_str, size);
close(fd); close(fd);
@ -264,11 +265,11 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
for (enum bind_action_normal i = 0; i < BIND_ACTION_COUNT; i++) { for (enum bind_action_normal i = 0; i < BIND_ACTION_COUNT; i++) {
key_binding_list_t bindings = tll_init(); key_binding_list_t bindings = tll_init();
input_parse_key_binding( input_parse_key_binding(
wayl->kbd.xkb_keymap, wayl->conf->bindings.key[i], &bindings); seat->kbd.xkb_keymap, wayl->conf->bindings.key[i], &bindings);
tll_foreach(bindings, it) { tll_foreach(bindings, it) {
tll_push_back( tll_push_back(
wayl->kbd.bindings.key, seat->kbd.bindings.key,
((struct key_binding_normal){.bind = it->item, .action = i})); ((struct key_binding_normal){.bind = it->item, .action = i}));
} }
@ -278,11 +279,11 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
for (enum bind_action_search i = 0; i < BIND_ACTION_SEARCH_COUNT; i++) { for (enum bind_action_search i = 0; i < BIND_ACTION_SEARCH_COUNT; i++) {
key_binding_list_t bindings = tll_init(); key_binding_list_t bindings = tll_init();
input_parse_key_binding( input_parse_key_binding(
wayl->kbd.xkb_keymap, wayl->conf->bindings.search[i], &bindings); seat->kbd.xkb_keymap, wayl->conf->bindings.search[i], &bindings);
tll_foreach(bindings, it) { tll_foreach(bindings, it) {
tll_push_back( tll_push_back(
wayl->kbd.bindings.search, seat->kbd.bindings.search,
((struct key_binding_search){.bind = it->item, .action = i})); ((struct key_binding_search){.bind = it->item, .action = i}));
} }
@ -296,28 +297,27 @@ keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
{ {
assert(surface != NULL); assert(surface != NULL);
struct wayland *wayl = data; struct seat *seat = data;
struct wl_window *win = wl_surface_get_user_data(surface); struct wl_window *win = wl_surface_get_user_data(surface);
struct terminal *term = win->term; struct terminal *term = win->term;
LOG_DBG("keyboard_enter: keyboard=%p, serial=%u, surface=%p", LOG_DBG("keyboard_enter: keyboard=%p, serial=%u, surface=%p",
wl_keyboard, serial, surface); wl_keyboard, serial, surface);
wayl->kbd_focus = term; term_kbd_focus_in(term);
wayl->input_serial = serial; seat->kbd_focus = term;
seat->kbd.serial = serial;
term_kbd_focus_in(wayl->kbd_focus);
} }
static bool static bool
start_repeater(struct wayland *wayl, uint32_t key) start_repeater(struct seat *seat, uint32_t key)
{ {
if (wayl->kbd.repeat.dont_re_repeat) if (seat->kbd.repeat.dont_re_repeat)
return true; return true;
struct itimerspec t = { struct itimerspec t = {
.it_value = {.tv_sec = 0, .tv_nsec = wayl->kbd.repeat.delay * 1000000}, .it_value = {.tv_sec = 0, .tv_nsec = seat->kbd.repeat.delay * 1000000},
.it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / wayl->kbd.repeat.rate}, .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / seat->kbd.repeat.rate},
}; };
if (t.it_value.tv_nsec >= 1000000000) { if (t.it_value.tv_nsec >= 1000000000) {
@ -328,23 +328,23 @@ start_repeater(struct wayland *wayl, uint32_t key)
t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000; t.it_interval.tv_sec += t.it_interval.tv_nsec / 1000000000;
t.it_interval.tv_nsec %= 1000000000; t.it_interval.tv_nsec %= 1000000000;
} }
if (timerfd_settime(wayl->kbd.repeat.fd, 0, &t, NULL) < 0) { if (timerfd_settime(seat->kbd.repeat.fd, 0, &t, NULL) < 0) {
LOG_ERRNO("failed to arm keyboard repeat timer"); LOG_ERRNO("%s: failed to arm keyboard repeat timer", seat->name);
return false; return false;
} }
wayl->kbd.repeat.key = key; seat->kbd.repeat.key = key;
return true; return true;
} }
static bool static bool
stop_repeater(struct wayland *wayl, uint32_t key) stop_repeater(struct seat *seat, uint32_t key)
{ {
if (key != -1 && key != wayl->kbd.repeat.key) if (key != -1 && key != seat->kbd.repeat.key)
return true; return true;
if (timerfd_settime(wayl->kbd.repeat.fd, 0, &(struct itimerspec){}, NULL) < 0) { if (timerfd_settime(seat->kbd.repeat.fd, 0, &(struct itimerspec){}, NULL) < 0) {
LOG_ERRNO("failed to disarm keyboard repeat timer"); LOG_ERRNO("%s: failed to disarm keyboard repeat timer", seat->name);
return false; return false;
} }
@ -355,26 +355,26 @@ static void
keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
struct wl_surface *surface) struct wl_surface *surface)
{ {
struct wayland *wayl = data; struct seat *seat = data;
LOG_DBG("keyboard_leave: keyboard=%p, serial=%u, surface=%p", LOG_DBG("keyboard_leave: keyboard=%p, serial=%u, surface=%p",
wl_keyboard, serial, surface); wl_keyboard, serial, surface);
assert( assert(
wayl->kbd_focus == NULL || seat->kbd_focus == NULL ||
surface == NULL || /* Seen on Sway 1.2 */ surface == NULL || /* Seen on Sway 1.2 */
((const struct wl_window *)wl_surface_get_user_data(surface))->term == wayl->kbd_focus ((const struct wl_window *)wl_surface_get_user_data(surface))->term == seat->kbd_focus
); );
struct terminal *old_focused = wayl->kbd_focus; struct terminal *old_focused = seat->kbd_focus;
wayl->kbd_focus = NULL; seat->kbd_focus = NULL;
stop_repeater(wayl, -1); stop_repeater(seat, -1);
wayl->kbd.shift = false;; seat->kbd.shift = false;
wayl->kbd.alt = false;; seat->kbd.alt = false;
wayl->kbd.ctrl = false;; seat->kbd.ctrl = false;
wayl->kbd.meta = false;; seat->kbd.meta = false;
xkb_compose_state_reset(wayl->kbd.xkb_compose_state); xkb_compose_state_reset(seat->kbd.xkb_compose_state);
if (old_focused != NULL) if (old_focused != NULL)
term_kbd_focus_out(old_focused); term_kbd_focus_out(old_focused);
@ -508,24 +508,24 @@ static void
keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
uint32_t time, uint32_t key, uint32_t state) uint32_t time, uint32_t key, uint32_t state)
{ {
struct wayland *wayl = data; struct seat *seat = data;
struct terminal *term = wayl->kbd_focus; struct terminal *term = seat->kbd_focus;
assert(term != NULL); assert(term != NULL);
const xkb_mod_mask_t ctrl = 1 << wayl->kbd.mod_ctrl; const xkb_mod_mask_t ctrl = 1 << seat->kbd.mod_ctrl;
const xkb_mod_mask_t alt = 1 << wayl->kbd.mod_alt; const xkb_mod_mask_t alt = 1 << seat->kbd.mod_alt;
const xkb_mod_mask_t shift = 1 << wayl->kbd.mod_shift; const xkb_mod_mask_t shift = 1 << seat->kbd.mod_shift;
const xkb_mod_mask_t meta = 1 << wayl->kbd.mod_meta; const xkb_mod_mask_t meta = 1 << seat->kbd.mod_meta;
if (state == XKB_KEY_UP) { if (state == XKB_KEY_UP) {
stop_repeater(wayl, key); stop_repeater(seat, key);
return; return;
} }
key += 8; key += 8;
bool should_repeat = xkb_keymap_key_repeats(wayl->kbd.xkb_keymap, key); bool should_repeat = xkb_keymap_key_repeats(seat->kbd.xkb_keymap, key);
xkb_keysym_t sym = xkb_state_key_get_one_sym(wayl->kbd.xkb_state, key); xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->kbd.xkb_state, key);
#if 0 #if 0
char foo[100]; char foo[100];
@ -533,9 +533,9 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
LOG_INFO("%s", foo); LOG_INFO("%s", foo);
#endif #endif
xkb_compose_state_feed(wayl->kbd.xkb_compose_state, sym); xkb_compose_state_feed(seat->kbd.xkb_compose_state, sym);
enum xkb_compose_status compose_status = xkb_compose_state_get_status( enum xkb_compose_status compose_status = xkb_compose_state_get_status(
wayl->kbd.xkb_compose_state); seat->kbd.xkb_compose_state);
if (compose_status == XKB_COMPOSE_COMPOSING) { if (compose_status == XKB_COMPOSE_COMPOSING) {
/* TODO: goto maybe_repeat? */ /* TODO: goto maybe_repeat? */
@ -543,23 +543,23 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
} }
xkb_mod_mask_t mods = xkb_state_serialize_mods( xkb_mod_mask_t mods = xkb_state_serialize_mods(
wayl->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); seat->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 = xkb_state_key_get_consumed_mods(seat->kbd.xkb_state, key);
xkb_mod_mask_t consumed = 0x0; xkb_mod_mask_t consumed = 0x0;
xkb_mod_mask_t significant = ctrl | alt | shift | meta; xkb_mod_mask_t significant = ctrl | alt | shift | meta;
xkb_mod_mask_t effective_mods = mods & ~consumed & significant; xkb_mod_mask_t effective_mods = mods & ~consumed & significant;
if (term->is_searching) { if (term->is_searching) {
if (should_repeat) if (should_repeat)
start_repeater(wayl, key - 8); start_repeater(seat, key - 8);
search_input(term, key, sym, effective_mods, serial); search_input(seat, term, key, sym, effective_mods, serial);
return; return;
} }
#if 0 #if 0
for (size_t i = 0; i < 32; i++) { for (size_t i = 0; i < 32; i++) {
if (mods & (1 << i)) { if (mods & (1 << i)) {
LOG_INFO("%s", xkb_keymap_mod_get_name(wayl->kbd.xkb_keymap, i)); LOG_INFO("%s", xkb_keymap_mod_get_name(seat->kbd.xkb_keymap, i));
} }
} }
#endif #endif
@ -573,20 +573,20 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
/* /*
* User configurable bindings * User configurable bindings
*/ */
tll_foreach(wayl->kbd.bindings.key, it) { tll_foreach(seat->kbd.bindings.key, it) {
if (it->item.bind.mods != effective_mods) if (it->item.bind.mods != effective_mods)
continue; continue;
/* Match symbol */ /* Match symbol */
if (it->item.bind.sym == sym) { if (it->item.bind.sym == sym) {
execute_binding(term, it->item.action, serial); execute_binding(seat, term, it->item.action, serial);
goto maybe_repeat; goto maybe_repeat;
} }
/* Match raw key code */ /* Match raw key code */
tll_foreach(it->item.bind.key_codes, code) { tll_foreach(it->item.bind.key_codes, code) {
if (code->item == key) { if (code->item == key) {
execute_binding(term, it->item.action, serial); execute_binding(seat, term, it->item.action, serial);
goto maybe_repeat; goto maybe_repeat;
} }
} }
@ -597,10 +597,10 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
*/ */
enum modifier keymap_mods = MOD_NONE; enum modifier keymap_mods = MOD_NONE;
keymap_mods |= wayl->kbd.shift ? MOD_SHIFT : MOD_NONE; keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE;
keymap_mods |= wayl->kbd.alt ? MOD_ALT : MOD_NONE; keymap_mods |= seat->kbd.alt ? MOD_ALT : MOD_NONE;
keymap_mods |= wayl->kbd.ctrl ? MOD_CTRL : MOD_NONE; keymap_mods |= seat->kbd.ctrl ? MOD_CTRL : MOD_NONE;
keymap_mods |= wayl->kbd.meta ? MOD_META : MOD_NONE; keymap_mods |= seat->kbd.meta ? MOD_META : MOD_NONE;
const struct key_data *keymap = keymap_lookup(term, sym, keymap_mods); const struct key_data *keymap = keymap_lookup(term, sym, keymap_mods);
if (keymap != NULL) { if (keymap != NULL) {
@ -620,13 +620,13 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
if (compose_status == XKB_COMPOSE_COMPOSED) { if (compose_status == XKB_COMPOSE_COMPOSED) {
count = xkb_compose_state_get_utf8( count = xkb_compose_state_get_utf8(
wayl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
xkb_compose_state_reset(wayl->kbd.xkb_compose_state); xkb_compose_state_reset(seat->kbd.xkb_compose_state);
} else if (compose_status == XKB_COMPOSE_CANCELLED) { } else if (compose_status == XKB_COMPOSE_CANCELLED) {
goto maybe_repeat; goto maybe_repeat;
} else { } else {
count = xkb_state_key_get_utf8( count = xkb_state_key_get_utf8(
wayl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); seat->kbd.xkb_state, key, (char *)buf, sizeof(buf));
} }
if (count == 0) if (count == 0)
@ -720,7 +720,7 @@ maybe_repeat:
term->wl->presentation_clock_id, &term->render.input_time); term->wl->presentation_clock_id, &term->render.input_time);
if (should_repeat) if (should_repeat)
start_repeater(wayl, key - 8); start_repeater(seat, key - 8);
} }
@ -729,36 +729,36 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) uint32_t mods_locked, uint32_t group)
{ {
struct wayland *wayl = data; struct seat *seat = data;
LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u", LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u",
mods_depressed, mods_latched, mods_locked, group); mods_depressed, mods_latched, mods_locked, group);
xkb_state_update_mask( xkb_state_update_mask(
wayl->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); seat->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 */ /* Update state of modifiers we're interrested in for e.g mouse events */
wayl->kbd.shift = xkb_state_mod_index_is_active( seat->kbd.shift = xkb_state_mod_index_is_active(
wayl->kbd.xkb_state, wayl->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED);
wayl->kbd.alt = xkb_state_mod_index_is_active( seat->kbd.alt = xkb_state_mod_index_is_active(
wayl->kbd.xkb_state, wayl->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED);
wayl->kbd.ctrl = xkb_state_mod_index_is_active( seat->kbd.ctrl = xkb_state_mod_index_is_active(
wayl->kbd.xkb_state, wayl->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED);
wayl->kbd.meta = xkb_state_mod_index_is_active( seat->kbd.meta = xkb_state_mod_index_is_active(
wayl->kbd.xkb_state, wayl->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED);
if (wayl->kbd_focus && wayl->kbd_focus->active_surface == TERM_SURF_GRID) if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID)
term_xcursor_update(wayl->kbd_focus); term_xcursor_update(seat->kbd_focus);
} }
static void static void
keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay) int32_t rate, int32_t delay)
{ {
struct wayland *wayl = data; struct seat *seat = data;
LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay); LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay);
wayl->kbd.repeat.rate = rate; seat->kbd.repeat.rate = rate;
wayl->kbd.repeat.delay = delay; seat->kbd.repeat.delay = delay;
} }
const struct wl_keyboard_listener keyboard_listener = { const struct wl_keyboard_listener keyboard_listener = {
@ -771,9 +771,9 @@ const struct wl_keyboard_listener keyboard_listener = {
}; };
void void
input_repeat(struct wayland *wayl, uint32_t key) input_repeat(struct seat *seat, uint32_t key)
{ {
keyboard_key(wayl, NULL, wayl->input_serial, 0, key, XKB_KEY_DOWN); keyboard_key(seat, NULL, seat->kbd.serial, 0, key, XKB_KEY_DOWN);
} }
static bool static bool
@ -817,14 +817,14 @@ is_bottom_right(const struct terminal *term, int x, int y)
static const char * static const char *
xcursor_for_csd_border(struct terminal *term, int x, int y) xcursor_for_csd_border(struct terminal *term, int x, int y)
{ {
if (is_top_left(term, x, y)) return "top_left_corner"; if (is_top_left(term, x, y)) return XCURSOR_TOP_LEFT_CORNER;
else if (is_top_right(term, x, y)) return "top_right_corner"; else if (is_top_right(term, x, y)) return XCURSOR_TOP_RIGHT_CORNER;
else if (is_bottom_left(term, x, y)) return "bottom_left_corner"; else if (is_bottom_left(term, x, y)) return XCURSOR_BOTTOM_LEFT_CORNER;
else if (is_bottom_right(term, x, y)) return "bottom_right_corner"; else if (is_bottom_right(term, x, y)) return XCURSOR_BOTTOM_RIGHT_CORNER;
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return "left_side"; else if (term->active_surface == TERM_SURF_BORDER_LEFT) return XCURSOR_LEFT_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return "right_side"; else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return XCURSOR_RIGHT_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_TOP) return "top_side"; else if (term->active_surface == TERM_SURF_BORDER_TOP) return XCURSOR_TOP_SIDE;
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return"bottom_side"; else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return XCURSOR_BOTTOM_SIDE;
else { else {
assert(false); assert(false);
return NULL; return NULL;
@ -838,44 +838,46 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
{ {
assert(surface != NULL); assert(surface != NULL);
struct wayland *wayl = data; struct seat *seat = data;
struct wl_window *win = wl_surface_get_user_data(surface); struct wl_window *win = wl_surface_get_user_data(surface);
struct terminal *term = win->term; struct terminal *term = win->term;
seat->pointer.serial = serial;
LOG_DBG("pointer-enter: pointer=%p, serial=%u, surface = %p, new-moused = %p", LOG_DBG("pointer-enter: pointer=%p, serial=%u, surface = %p, new-moused = %p",
wl_pointer, serial, surface, term); wl_pointer, serial, surface, term);
wayl->mouse_focus = term; /* Scale may have changed */
wayl_reload_xcursor_theme(seat, term->scale);
seat->mouse_focus = term;
int x = wl_fixed_to_int(surface_x) * term->scale; int x = wl_fixed_to_int(surface_x) * term->scale;
int y = wl_fixed_to_int(surface_y) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale;
switch ((term->active_surface = term_surface_kind(term, surface))) { switch ((term->active_surface = term_surface_kind(term, surface))) {
case TERM_SURF_GRID: case TERM_SURF_GRID:
wayl->mouse.col = x / term->cell_width; seat->mouse.col = x / term->cell_width;
wayl->mouse.row = y / term->cell_height; seat->mouse.row = y / term->cell_height;
term_xcursor_update(term); term_xcursor_update(term);
break; break;
case TERM_SURF_SEARCH: case TERM_SURF_SEARCH:
case TERM_SURF_TITLE: case TERM_SURF_TITLE:
term->xcursor = "left_ptr"; render_xcursor_set(seat, term, XCURSOR_LEFT_PTR);
render_xcursor_set(term);
break; break;
case TERM_SURF_BORDER_LEFT: case TERM_SURF_BORDER_LEFT:
case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_RIGHT:
case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_TOP:
case TERM_SURF_BORDER_BOTTOM: case TERM_SURF_BORDER_BOTTOM:
term->xcursor = xcursor_for_csd_border(term, x, y); render_xcursor_set(seat, term, xcursor_for_csd_border(term, x, y));
render_xcursor_set(term);
break; break;
case TERM_SURF_BUTTON_MINIMIZE: case TERM_SURF_BUTTON_MINIMIZE:
case TERM_SURF_BUTTON_MAXIMIZE: case TERM_SURF_BUTTON_MAXIMIZE:
case TERM_SURF_BUTTON_CLOSE: case TERM_SURF_BUTTON_CLOSE:
term->xcursor = "left_ptr"; render_xcursor_set(seat, term, XCURSOR_LEFT_PTR);
render_xcursor_set(term);
render_refresh_csd(term); render_refresh_csd(term);
break; break;
@ -889,25 +891,25 @@ static void
wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) uint32_t serial, struct wl_surface *surface)
{ {
struct wayland *wayl = data; struct seat *seat = data;
struct terminal *old_moused = wayl->mouse_focus; struct terminal *old_moused = seat->mouse_focus;
LOG_DBG( LOG_DBG(
"pointer-leave: pointer=%p, serial=%u, surface = %p, old-moused = %p", "pointer-leave: pointer=%p, serial=%u, surface = %p, old-moused = %p",
wl_pointer, serial, surface, old_moused); wl_pointer, serial, surface, old_moused);
if (wayl->pointer.xcursor_callback != NULL) { if (seat->pointer.xcursor_callback != NULL) {
/* A cursor frame callback may never be called if the pointer leaves our surface */ /* A cursor frame callback may never be called if the pointer leaves our surface */
wl_callback_destroy(wayl->pointer.xcursor_callback); wl_callback_destroy(seat->pointer.xcursor_callback);
wayl->pointer.xcursor_callback = NULL; seat->pointer.xcursor_callback = NULL;
wayl->pointer.pending_terminal = NULL; seat->pointer.xcursor_pending = false;
wayl->pointer.xcursor = NULL; seat->pointer.xcursor = NULL;
} }
/* Reset mouse state */ /* Reset mouse state */
memset(&wayl->mouse, 0, sizeof(wayl->mouse)); memset(&seat->mouse, 0, sizeof(seat->mouse));
wayl->mouse_focus = NULL; seat->mouse_focus = NULL;
if (old_moused == NULL) { if (old_moused == NULL) {
LOG_WARN( LOG_WARN(
"compositor sent pointer_leave event without a pointer_enter " "compositor sent pointer_leave event without a pointer_enter "
@ -953,8 +955,9 @@ static void
wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
{ {
struct wayland *wayl = data; struct seat *seat = data;
struct terminal *term = wayl->mouse_focus; struct wayland *wayl = seat->wayl;
struct terminal *term = seat->mouse_focus;
struct wl_window *win = term->window; struct wl_window *win = term->window;
LOG_DBG("pointer_motion: pointer=%p, x=%d, y=%d", wl_pointer, LOG_DBG("pointer_motion: pointer=%p, x=%d, y=%d", wl_pointer,
@ -965,8 +968,8 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
int x = wl_fixed_to_int(surface_x) * term->scale; int x = wl_fixed_to_int(surface_x) * term->scale;
int y = wl_fixed_to_int(surface_y) * term->scale; int y = wl_fixed_to_int(surface_y) * term->scale;
wayl->mouse.x = x; seat->mouse.x = x;
wayl->mouse.y = y; seat->mouse.y = y;
switch (term->active_surface) { switch (term->active_surface) {
case TERM_SURF_NONE: case TERM_SURF_NONE:
@ -980,10 +983,10 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
/* We've started a 'move' timer, but user started dragging /* We've started a 'move' timer, but user started dragging
* right away - abort the timer and initiate the actual move * right away - abort the timer and initiate the actual move
* right away */ * right away */
if (wayl->mouse.button == BTN_LEFT && win->csd.move_timeout_fd != -1) { if (seat->mouse.button == BTN_LEFT && win->csd.move_timeout_fd != -1) {
fdm_del(wayl->fdm, win->csd.move_timeout_fd); fdm_del(wayl->fdm, win->csd.move_timeout_fd);
win->csd.move_timeout_fd = -1; win->csd.move_timeout_fd = -1;
xdg_toplevel_move(win->xdg_toplevel, wayl->seat, win->csd.serial); xdg_toplevel_move(win->xdg_toplevel, seat->wl_seat, win->csd.serial);
} }
break; break;
@ -991,8 +994,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_RIGHT:
case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_TOP:
case TERM_SURF_BORDER_BOTTOM: case TERM_SURF_BORDER_BOTTOM:
term->xcursor = xcursor_for_csd_border(term, x, y); render_xcursor_set(seat, term, xcursor_for_csd_border(term, x, y));
render_xcursor_set(term);
break; break;
case TERM_SURF_GRID: { case TERM_SURF_GRID: {
@ -1002,23 +1004,26 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
if (col < 0 || row < 0 || col >= term->cols || row >= term->rows) if (col < 0 || row < 0 || col >= term->cols || row >= term->rows)
return; return;
bool update_selection = wayl->mouse.button == BTN_LEFT; bool update_selection = seat->mouse.button == BTN_LEFT;
bool update_selection_early = term->selection.end.row == -1; bool update_selection_early = term->selection.end.row == -1;
if (update_selection && update_selection_early) if (update_selection && update_selection_early)
selection_update(term, col, row); selection_update(term, col, row);
if (col == wayl->mouse.col && row == wayl->mouse.row) if (col == seat->mouse.col && row == seat->mouse.row)
break; break;
wayl->mouse.col = col; seat->mouse.col = col;
wayl->mouse.row = row; seat->mouse.row = row;
if (update_selection && !update_selection_early) if (update_selection && !update_selection_early)
selection_update(term, col, row); selection_update(term, col, row);
term_mouse_motion( if (!term_mouse_grabbed(term, seat)) {
term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col); term_mouse_motion(
term, seat->mouse.button, seat->mouse.row, seat->mouse.col,
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
}
break; break;
} }
} }
@ -1027,13 +1032,20 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
static bool static bool
fdm_csd_move(struct fdm *fdm, int fd, int events, void *data) fdm_csd_move(struct fdm *fdm, int fd, int events, void *data)
{ {
struct wl_window *win = data; struct seat *seat = data;
struct wayland *wayl = win->term->wl;
fdm_del(fdm, fd); fdm_del(fdm, fd);
win->csd.move_timeout_fd = -1;
xdg_toplevel_move(win->xdg_toplevel, wayl->seat, win->csd.serial); if (seat->mouse_focus == NULL) {
LOG_WARN(
"%s: CSD move timeout triggered, but seat's has no mouse focused terminal",
seat->name);
return true;
}
struct wl_window *win = seat->mouse_focus->window;
win->csd.move_timeout_fd = -1;
xdg_toplevel_move(win->xdg_toplevel, seat->wl_seat, win->csd.serial);
return true; return true;
} }
@ -1044,8 +1056,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
LOG_DBG("BUTTON: pointer=%p, serial=%u, button=%x, state=%u", LOG_DBG("BUTTON: pointer=%p, serial=%u, button=%x, state=%u",
wl_pointer, serial, button, state); wl_pointer, serial, button, state);
struct wayland *wayl = data; struct seat *seat = data;
struct terminal *term = wayl->mouse_focus; struct wayland *wayl = seat->wayl;
struct terminal *term = seat->mouse_focus;
assert(term != NULL); assert(term != NULL);
@ -1054,22 +1067,22 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
/* Time since last click */ /* Time since last click */
struct timeval now, since_last; struct timeval now, since_last;
gettimeofday(&now, NULL); gettimeofday(&now, NULL);
timersub(&now, &wayl->mouse.last_time, &since_last); timersub(&now, &seat->mouse.last_time, &since_last);
/* Double- or triple click? */ /* Double- or triple click? */
if (button == wayl->mouse.last_button && if (button == seat->mouse.last_button &&
since_last.tv_sec == 0 && since_last.tv_sec == 0 &&
since_last.tv_usec <= 300 * 1000) since_last.tv_usec <= 300 * 1000)
{ {
wayl->mouse.count++; seat->mouse.count++;
} else } else
wayl->mouse.count = 1; seat->mouse.count = 1;
wayl->mouse.button = button; /* For motion events */ seat->mouse.button = button; /* For motion events */
wayl->mouse.last_button = button; seat->mouse.last_button = button;
wayl->mouse.last_time = now; seat->mouse.last_time = now;
} else } else
wayl->mouse.button = 0; /* For motion events */ seat->mouse.button = 0; /* For motion events */
switch (term->active_surface) { switch (term->active_surface) {
case TERM_SURF_TITLE: case TERM_SURF_TITLE:
@ -1078,7 +1091,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
struct wl_window *win = term->window; struct wl_window *win = term->window;
/* Toggle maximized state on double-click */ /* Toggle maximized state on double-click */
if (button == BTN_LEFT && wayl->mouse.count == 2) { if (button == BTN_LEFT && seat->mouse.count == 2) {
if (win->is_maximized) if (win->is_maximized)
xdg_toplevel_unset_maximized(win->xdg_toplevel); xdg_toplevel_unset_maximized(win->xdg_toplevel);
else else
@ -1093,7 +1106,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); int fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
if (fd >= 0 && if (fd >= 0 &&
timerfd_settime(fd, 0, &timeout, NULL) == 0 && timerfd_settime(fd, 0, &timeout, NULL) == 0 &&
fdm_add(wayl->fdm, fd, EPOLLIN, &fdm_csd_move, win)) fdm_add(wayl->fdm, fd, EPOLLIN, &fdm_csd_move, seat))
{ {
win->csd.move_timeout_fd = fd; win->csd.move_timeout_fd = fd;
win->csd.serial = serial; win->csd.serial = serial;
@ -1127,8 +1140,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) {
enum xdg_toplevel_resize_edge resize_type; enum xdg_toplevel_resize_edge resize_type;
int x = wayl->mouse.x; int x = seat->mouse.x;
int y = wayl->mouse.y; int y = seat->mouse.y;
if (is_top_left(term, x, y)) if (is_top_left(term, x, y))
resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
@ -1142,7 +1155,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
resize_type = map[term->active_surface]; resize_type = map[term->active_surface];
xdg_toplevel_resize( xdg_toplevel_resize(
term->window->xdg_toplevel, term->wl->seat, serial, resize_type); term->window->xdg_toplevel, seat->wl_seat, serial, resize_type);
} }
return; return;
} }
@ -1174,32 +1187,35 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
switch (state) { switch (state) {
case WL_POINTER_BUTTON_STATE_PRESSED: { case WL_POINTER_BUTTON_STATE_PRESSED: {
if (button == BTN_LEFT && wayl->mouse.count <= 3) { if (button == BTN_LEFT && seat->mouse.count <= 3) {
selection_cancel(term); selection_cancel(term);
if (selection_enabled(term)) { if (selection_enabled(term, seat)) {
switch (wayl->mouse.count) { switch (seat->mouse.count) {
case 1: case 1:
selection_start( selection_start(
term, wayl->mouse.col, wayl->mouse.row, term, seat->mouse.col, seat->mouse.row,
wayl->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); seat->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL);
break; break;
case 2: case 2:
selection_mark_word(term, wayl->mouse.col, wayl->mouse.row, selection_mark_word(
wayl->kbd.ctrl, serial); seat, term, seat->mouse.col, seat->mouse.row,
seat->kbd.ctrl, serial);
break; break;
case 3: case 3:
selection_mark_row(term, wayl->mouse.row, serial); selection_mark_row(seat, term, seat->mouse.row, serial);
break; break;
} }
} }
} }
else if (button == BTN_RIGHT && wayl->mouse.count == 1) { else if (button == BTN_RIGHT && seat->mouse.count == 1) {
if (selection_enabled(term)) if (selection_enabled(term, seat)) {
selection_extend(term, wayl->mouse.col, wayl->mouse.row, serial); selection_extend(
seat, term, seat->mouse.col, seat->mouse.row, serial);
}
} }
else { else {
@ -1212,25 +1228,33 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
continue; continue;
} }
if (binding->count != wayl->mouse.count) { if (binding->count != seat->mouse.count) {
/* Not correct click count */ /* Not correct click count */
continue; continue;
} }
execute_binding(term, binding->action, serial); execute_binding(seat, term, binding->action, serial);
break; break;
} }
} }
term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); if (!term_mouse_grabbed(term, seat)) {
term_mouse_down(
term, button, seat->mouse.row, seat->mouse.col,
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
}
break; break;
} }
case WL_POINTER_BUTTON_STATE_RELEASED: case WL_POINTER_BUTTON_STATE_RELEASED:
if (button == BTN_LEFT && term->selection.end.col != -1) if (button == BTN_LEFT && term->selection.end.col != -1)
selection_finalize(term, serial); selection_finalize(seat, term, serial);
term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); if (!term_mouse_grabbed(term, seat)) {
term_mouse_up(
term, button, seat->mouse.row, seat->mouse.col,
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
}
break; break;
} }
break; break;
@ -1244,9 +1268,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
} }
static void static void
mouse_scroll(struct wayland *wayl, int amount) mouse_scroll(struct seat *seat, int amount)
{ {
struct terminal *term = wayl->mouse_focus; struct terminal *term = seat->mouse_focus;
assert(term != NULL); assert(term != NULL);
int button = amount < 0 ? BTN_BACK : BTN_FORWARD; int button = amount < 0 ? BTN_BACK : BTN_FORWARD;
@ -1268,20 +1292,26 @@ mouse_scroll(struct wayland *wayl, int amount)
static xkb_keycode_t key_arrow_down = 0; static xkb_keycode_t key_arrow_down = 0;
if (key_arrow_up == 0) { if (key_arrow_up == 0) {
key_arrow_up = xkb_keymap_key_by_name(wayl->kbd.xkb_keymap, "UP"); key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP");
key_arrow_down = xkb_keymap_key_by_name(wayl->kbd.xkb_keymap, "DOWN"); key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN");
} }
xkb_keycode_t key = button == BTN_BACK ? key_arrow_up : key_arrow_down; xkb_keycode_t key = button == BTN_BACK ? key_arrow_up : key_arrow_down;
for (int i = 0; i < amount; i++) for (int i = 0; i < amount; i++)
keyboard_key(wayl, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_DOWN); keyboard_key(seat, NULL, seat->kbd.serial, 0, key - 8, XKB_KEY_DOWN);
keyboard_key(wayl, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_UP); keyboard_key(seat, NULL, seat->kbd.serial, 0, key - 8, XKB_KEY_UP);
} else { } else {
for (int i = 0; i < amount; i++) if (!term_mouse_grabbed(term, seat)) {
term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); for (int i = 0; i < amount; i++) {
term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); term_mouse_down(
term, button, seat->mouse.row, seat->mouse.col,
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
}
term_mouse_up(
term, button, seat->mouse.row, seat->mouse.col,
seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl);
}
scrollback(term, amount); scrollback(term, amount);
} }
} }
@ -1293,9 +1323,9 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
return; return;
struct wayland *wayl = data; struct seat *seat = data;
if (wayl->mouse.have_discrete) if (seat->mouse.have_discrete)
return; return;
/* /*
@ -1304,11 +1334,11 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
* Without this, very slow scrolling will never actually scroll * Without this, very slow scrolling will never actually scroll
* anything. * anything.
*/ */
wayl->mouse.axis_aggregated += wl_fixed_to_double(value); seat->mouse.axis_aggregated += wl_fixed_to_double(value);
if (fabs(wayl->mouse.axis_aggregated) >= 1.) { if (fabs(seat->mouse.axis_aggregated) >= 1.) {
mouse_scroll(wayl, round(wayl->mouse.axis_aggregated)); mouse_scroll(seat, round(seat->mouse.axis_aggregated));
wayl->mouse.axis_aggregated = 0.; seat->mouse.axis_aggregated = 0.;
} }
} }
@ -1319,16 +1349,16 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
return; return;
struct wayland *wayl = data; struct seat *seat = data;
wayl->mouse.have_discrete = true; seat->mouse.have_discrete = true;
mouse_scroll(wayl, discrete); mouse_scroll(seat, discrete);
} }
static void static void
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
{ {
struct wayland *wayl = data; struct seat *seat = data;
wayl->mouse.have_discrete = false; seat->mouse.have_discrete = false;
} }
static void static void
@ -1344,8 +1374,8 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
return; return;
struct wayland *wayl = data; struct seat *seat = data;
wayl->mouse.axis_aggregated = 0.; seat->mouse.axis_aggregated = 0.;
} }
const struct wl_pointer_listener pointer_listener = { const struct wl_pointer_listener pointer_listener = {

View file

@ -8,7 +8,7 @@
extern const struct wl_keyboard_listener keyboard_listener; extern const struct wl_keyboard_listener keyboard_listener;
extern const struct wl_pointer_listener pointer_listener; extern const struct wl_pointer_listener pointer_listener;
void input_repeat(struct wayland *wayl, uint32_t key); void input_repeat(struct seat *seat, uint32_t key);
bool input_parse_key_binding(struct xkb_keymap *keymap, const char *combos, bool input_parse_key_binding(struct xkb_keymap *keymap, const char *combos,
key_binding_list_t *bindings); key_binding_list_t *bindings);

41
osc.c
View file

@ -52,15 +52,29 @@ osc_to_clipboard(struct terminal *term, const char *target,
} }
} }
/* Find a seat in which the terminal has focus */
struct seat *seat = NULL;
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term) {
seat = &it->item;
break;
}
}
if (seat == NULL) {
LOG_WARN("OSC52: client tried to write to clipboard data while window was unfocused");
return;
}
if (to_clipboard) { if (to_clipboard) {
char *copy = strdup(decoded); char *copy = strdup(decoded);
if (!text_to_clipboard(term, copy, term->wl->input_serial)) if (!text_to_clipboard(seat, term, copy, seat->kbd.serial))
free(copy); free(copy);
} }
if (to_primary) { if (to_primary) {
char *copy = strdup(decoded); char *copy = strdup(decoded);
if (!text_to_primary(term, copy, term->wl->input_serial)) if (!text_to_primary(seat, term, copy, seat->kbd.serial))
free(copy); free(copy);
} }
@ -68,6 +82,7 @@ osc_to_clipboard(struct terminal *term, const char *target,
} }
struct clip_context { struct clip_context {
struct seat *seat;
struct terminal *term; struct terminal *term;
uint8_t buf[3]; uint8_t buf[3];
int idx; int idx;
@ -151,23 +166,37 @@ osc_from_clipboard(struct terminal *term, const char *source)
if (src == 0) if (src == 0)
return; return;
/* Find a seat in which the terminal has focus */
struct seat *seat = NULL;
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term) {
seat = &it->item;
break;
}
}
if (seat == NULL) {
LOG_WARN("OSC52: client tried to read clipboard data while window was unfocused");
return;
}
term_to_slave(term, "\033]52;", 5); term_to_slave(term, "\033]52;", 5);
term_to_slave(term, &src, 1); term_to_slave(term, &src, 1);
term_to_slave(term, ";", 1); term_to_slave(term, ";", 1);
struct clip_context *ctx = malloc(sizeof(*ctx)); struct clip_context *ctx = malloc(sizeof(*ctx));
*ctx = (struct clip_context) {.term = term}; *ctx = (struct clip_context) {.seat = seat, .term = term};
switch (src) { switch (src) {
case 'c': case 'c':
text_from_clipboard( text_from_clipboard(
term, term->wl->input_serial, seat, term, &from_clipboard_cb, &from_clipboard_done, ctx);
&from_clipboard_cb, &from_clipboard_done, ctx);
break; break;
case 's': case 's':
case 'p': case 'p':
text_from_primary(term, &from_clipboard_cb, &from_clipboard_done, ctx); text_from_primary(
seat, term, &from_clipboard_cb, &from_clipboard_done, ctx);
break; break;
} }
} }

View file

@ -1941,62 +1941,60 @@ render_resize_force(struct terminal *term, int width, int height)
static void xcursor_callback( static void xcursor_callback(
void *data, struct wl_callback *wl_callback, uint32_t callback_data); void *data, struct wl_callback *wl_callback, uint32_t callback_data);
static const struct wl_callback_listener xcursor_listener = { static const struct wl_callback_listener xcursor_listener = {
.done = &xcursor_callback, .done = &xcursor_callback,
}; };
static void static void
render_xcursor_update(struct wayland *wayl, const struct terminal *term) render_xcursor_update(struct seat *seat)
{ {
/* If called from a frame callback, we may no longer have mouse focus */ /* If called from a frame callback, we may no longer have mouse focus */
if (wayl->mouse_focus != term) if (!seat->mouse_focus)
return; return;
wayl->pointer.cursor = wl_cursor_theme_get_cursor(wayl->pointer.theme, term->xcursor); seat->pointer.cursor = wl_cursor_theme_get_cursor(
if (wayl->pointer.cursor == NULL) { seat->pointer.theme, seat->pointer.xcursor);
LOG_ERR("%s: failed to load xcursor pointer '%s'",
wayl->pointer.theme_name, term->xcursor); if (seat->pointer.cursor == NULL) {
LOG_ERR("failed to load xcursor pointer '%s'", seat->pointer.xcursor);
return; return;
} }
wayl->pointer.xcursor = term->xcursor; const int scale = seat->pointer.scale;
struct wl_cursor_image *image = seat->pointer.cursor->images[0];
const int scale = term->scale;
struct wl_cursor_image *image = wayl->pointer.cursor->images[0];
wl_surface_attach( wl_surface_attach(
wayl->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0); seat->pointer.surface, wl_cursor_image_get_buffer(image), 0, 0);
wl_pointer_set_cursor( wl_pointer_set_cursor(
wayl->pointer.pointer, wayl->pointer.serial, seat->wl_pointer, seat->pointer.serial,
wayl->pointer.surface, seat->pointer.surface,
image->hotspot_x / scale, image->hotspot_y / scale); image->hotspot_x / scale, image->hotspot_y / scale);
wl_surface_damage_buffer( wl_surface_damage_buffer(
wayl->pointer.surface, 0, 0, INT32_MAX, INT32_MAX); seat->pointer.surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_set_buffer_scale(wayl->pointer.surface, scale); wl_surface_set_buffer_scale(seat->pointer.surface, scale);
assert(wayl->pointer.xcursor_callback == NULL); assert(seat->pointer.xcursor_callback == NULL);
wayl->pointer.xcursor_callback = wl_surface_frame(wayl->pointer.surface); seat->pointer.xcursor_callback = wl_surface_frame(seat->pointer.surface);
wl_callback_add_listener(wayl->pointer.xcursor_callback, &xcursor_listener, wayl); wl_callback_add_listener(seat->pointer.xcursor_callback, &xcursor_listener, seat);
wl_surface_commit(wayl->pointer.surface); wl_surface_commit(seat->pointer.surface);
} }
static void static void
xcursor_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data) xcursor_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_data)
{ {
struct wayland *wayl = data; struct seat *seat = data;
assert(wayl->pointer.xcursor_callback == wl_callback); assert(seat->pointer.xcursor_callback == wl_callback);
wl_callback_destroy(wl_callback); wl_callback_destroy(wl_callback);
wayl->pointer.xcursor_callback = NULL; seat->pointer.xcursor_callback = NULL;
if (wayl->pointer.pending_terminal != NULL) { if (seat->pointer.xcursor_pending) {
render_xcursor_update(wayl, wayl->pointer.pending_terminal); render_xcursor_update(seat);
wayl->pointer.pending_terminal = NULL; seat->pointer.xcursor_pending = false;
} }
} }
@ -2061,12 +2059,14 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
} }
} }
if (wayl->pointer.pending_terminal != NULL) { tll_foreach(wayl->seats, it) {
if (wayl->pointer.xcursor_callback == NULL) { if (it->item.pointer.xcursor_pending) {
render_xcursor_update(wayl, wayl->pointer.pending_terminal); if (it->item.pointer.xcursor_callback == NULL) {
wayl->pointer.pending_terminal = NULL; render_xcursor_update(&it->item);
} else { it->item.pointer.xcursor_pending = false;
/* Frame callback will call render_xcursor_update() */ } else {
/* Frame callback will call render_xcursor_update() */
}
} }
} }
} }
@ -2098,28 +2098,26 @@ render_refresh_search(struct terminal *term)
} }
bool bool
render_xcursor_set(struct terminal *term) render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor)
{ {
struct wayland *wayl = term->wl; if (seat->pointer.theme == NULL)
if (wayl->pointer.theme == NULL)
return false; return false;
if (wayl->mouse_focus == NULL) { if (seat->mouse_focus == NULL) {
wayl->pointer.xcursor = NULL; seat->pointer.xcursor = NULL;
wayl->pointer.pending_terminal = NULL;
return true; return true;
} }
if (wayl->mouse_focus != term) { if (seat->mouse_focus != term) {
/* This terminal doesn't have mouse focus */ /* This terminal doesn't have mouse focus */
return true; return true;
} }
if (wayl->pointer.xcursor == term->xcursor) if (seat->pointer.xcursor == xcursor)
return true; return true;
/* FDM hook takes care of actual rendering */ /* FDM hook takes care of actual rendering */
wayl->pointer.pending_terminal = term; seat->pointer.xcursor_pending = true;
seat->pointer.xcursor = xcursor;
return true; return true;
} }

View file

@ -16,7 +16,7 @@ void render_refresh(struct terminal *term);
void render_refresh_csd(struct terminal *term); void render_refresh_csd(struct terminal *term);
void render_refresh_search(struct terminal *term); void render_refresh_search(struct terminal *term);
void render_refresh_title(struct terminal *term); void render_refresh_title(struct terminal *term);
bool render_xcursor_set(struct terminal *term); bool render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor);
struct render_worker_context { struct render_worker_context {
int my_id; int my_id;

View file

@ -429,8 +429,10 @@ execute_binding(struct terminal *term, enum bind_action_search action,
return true; return true;
case BIND_ACTION_SEARCH_COMMIT: case BIND_ACTION_SEARCH_COMMIT:
#if 0
selection_finalize(term, term->wl->input_serial); selection_finalize(term, term->wl->input_serial);
search_cancel_keep_selection(term); search_cancel_keep_selection(term);
#endif
return true; return true;
case BIND_ACTION_SEARCH_FIND_PREV: case BIND_ACTION_SEARCH_FIND_PREV:
@ -567,16 +569,16 @@ execute_binding(struct terminal *term, enum bind_action_search action,
} }
void void
search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, search_input(struct seat *seat, struct terminal *term, uint32_t key,
xkb_mod_mask_t mods, uint32_t serial) xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial)
{ {
LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods); LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods);
enum xkb_compose_status compose_status = xkb_compose_state_get_status( enum xkb_compose_status compose_status = xkb_compose_state_get_status(
term->wl->kbd.xkb_compose_state); seat->kbd.xkb_compose_state);
/* Key bindings */ /* Key bindings */
tll_foreach(term->wl->kbd.bindings.search, it) { tll_foreach(seat->kbd.bindings.search, it) {
if (it->item.bind.mods != mods) if (it->item.bind.mods != mods)
continue; continue;
@ -602,13 +604,13 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym,
if (compose_status == XKB_COMPOSE_COMPOSED) { if (compose_status == XKB_COMPOSE_COMPOSED) {
count = xkb_compose_state_get_utf8( count = xkb_compose_state_get_utf8(
term->wl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
xkb_compose_state_reset(term->wl->kbd.xkb_compose_state); xkb_compose_state_reset(seat->kbd.xkb_compose_state);
} else if (compose_status == XKB_COMPOSE_CANCELLED) { } else if (compose_status == XKB_COMPOSE_CANCELLED) {
count = 0; count = 0;
} else { } else {
count = xkb_state_key_get_utf8( count = xkb_state_key_get_utf8(
term->wl->kbd.xkb_state, key, (char *)buf, sizeof(buf)); seat->kbd.xkb_state, key, (char *)buf, sizeof(buf));
} }
const char *src = (const char *)buf; const char *src = (const char *)buf;

View file

@ -5,5 +5,5 @@
void search_begin(struct terminal *term); void search_begin(struct terminal *term);
void search_cancel(struct terminal *term); void search_cancel(struct terminal *term);
void search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, xkb_mod_mask_t mods, void search_input(struct seat *seat, struct terminal *term, uint32_t key,
uint32_t serial); xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial);

View file

@ -21,11 +21,11 @@
#include "vt.h" #include "vt.h"
bool bool
selection_enabled(const struct terminal *term) selection_enabled(const struct terminal *term, struct seat *seat)
{ {
return return
term->mouse_tracking == MOUSE_NONE || term->mouse_tracking == MOUSE_NONE ||
term_mouse_grabbed(term) || term_mouse_grabbed(term, seat) ||
term->is_searching; term->is_searching;
} }
@ -591,7 +591,8 @@ selection_extend_block(struct terminal *term, int col, int row, uint32_t serial)
} }
void void
selection_extend(struct terminal *term, int col, int row, uint32_t serial) selection_extend(struct seat *seat, struct terminal *term,
int col, int row, uint32_t serial)
{ {
if (term->selection.start.row < 0 || term->selection.end.row < 0) { if (term->selection.start.row < 0 || term->selection.end.row < 0) {
/* No existing selection */ /* No existing selection */
@ -621,13 +622,13 @@ selection_extend(struct terminal *term, int col, int row, uint32_t serial)
break; break;
} }
selection_to_primary(term, serial); selection_to_primary(seat, term, serial);
} }
static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener; //static const struct zwp_primary_selection_source_v1_listener primary_selection_source_listener;
void void
selection_finalize(struct terminal *term, uint32_t serial) selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial)
{ {
if (term->selection.start.row < 0 || term->selection.end.row < 0) if (term->selection.start.row < 0 || term->selection.end.row < 0)
return; return;
@ -645,7 +646,7 @@ selection_finalize(struct terminal *term, uint32_t serial)
} }
assert(term->selection.start.row <= term->selection.end.row); assert(term->selection.start.row <= term->selection.end.row);
selection_to_primary(term, serial); selection_to_primary(seat, term, serial);
} }
void void
@ -668,8 +669,8 @@ selection_cancel(struct terminal *term)
} }
void void
selection_mark_word(struct terminal *term, int col, int row, bool spaces_only, selection_mark_word(struct seat *seat, struct terminal *term, int col, int row,
uint32_t serial) bool spaces_only, uint32_t serial)
{ {
selection_cancel(term); selection_cancel(term);
@ -730,17 +731,19 @@ selection_mark_word(struct terminal *term, int col, int row, bool spaces_only,
selection_start(term, start.col, start.row, SELECTION_NORMAL); selection_start(term, start.col, start.row, SELECTION_NORMAL);
selection_update(term, end.col, end.row); selection_update(term, end.col, end.row);
selection_finalize(term, serial); selection_finalize(seat, term, serial);
} }
void void
selection_mark_row(struct terminal *term, int row, uint32_t serial) selection_mark_row(
struct seat *seat, struct terminal *term, int row, uint32_t serial)
{ {
selection_start(term, 0, row, SELECTION_NORMAL); selection_start(term, 0, row, SELECTION_NORMAL);
selection_update(term, term->cols - 1, row); selection_update(term, term->cols - 1, row);
selection_finalize(term, serial); selection_finalize(seat, term, serial);
} }
static void static void
target(void *data, struct wl_data_source *wl_data_source, const char *mime_type) target(void *data, struct wl_data_source *wl_data_source, const char *mime_type)
{ {
@ -786,8 +789,8 @@ static void
send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
int32_t fd) int32_t fd)
{ {
struct wayland *wayl = data; struct seat *seat = data;
const struct wl_clipboard *clipboard = &wayl->clipboard; const struct wl_clipboard *clipboard = &seat->clipboard;
assert(clipboard != NULL); assert(clipboard != NULL);
assert(clipboard->text != NULL); assert(clipboard->text != NULL);
@ -815,7 +818,7 @@ send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
.idx = async_idx, .idx = async_idx,
}; };
if (fdm_add(wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx)) if (fdm_add(seat->wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx))
return; return;
free(ctx->data); free(ctx->data);
@ -839,8 +842,8 @@ send(void *data, struct wl_data_source *wl_data_source, const char *mime_type,
static void static void
cancelled(void *data, struct wl_data_source *wl_data_source) cancelled(void *data, struct wl_data_source *wl_data_source)
{ {
struct wayland *wayl = data; struct seat *seat = data;
struct wl_clipboard *clipboard = &wayl->clipboard; struct wl_clipboard *clipboard = &seat->clipboard;
assert(clipboard->data_source == wl_data_source); assert(clipboard->data_source == wl_data_source);
wl_data_source_destroy(clipboard->data_source); wl_data_source_destroy(clipboard->data_source);
@ -880,8 +883,8 @@ primary_send(void *data,
struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1,
const char *mime_type, int32_t fd) const char *mime_type, int32_t fd)
{ {
struct wayland *wayl = data; struct seat *seat = data;
const struct wl_primary *primary = &wayl->primary; const struct wl_primary *primary = &seat->primary;
assert(primary != NULL); assert(primary != NULL);
assert(primary->text != NULL); assert(primary->text != NULL);
@ -907,7 +910,7 @@ primary_send(void *data,
.idx = async_idx, .idx = async_idx,
}; };
if (fdm_add(wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx)) if (fdm_add(seat->wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx))
return; return;
free(ctx->data); free(ctx->data);
@ -932,8 +935,8 @@ static void
primary_cancelled(void *data, primary_cancelled(void *data,
struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1) struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1)
{ {
struct wayland *wayl = data; struct seat *seat = data;
struct wl_primary *primary = &wayl->primary; struct wl_primary *primary = &seat->primary;
zwp_primary_selection_source_v1_destroy(primary->data_source); zwp_primary_selection_source_v1_destroy(primary->data_source);
primary->data_source = NULL; primary->data_source = NULL;
@ -949,14 +952,14 @@ static const struct zwp_primary_selection_source_v1_listener primary_selection_s
}; };
bool bool
text_to_clipboard(struct terminal *term, char *text, uint32_t serial) text_to_clipboard(struct seat *seat, struct terminal *term, char *text, uint32_t serial)
{ {
struct wl_clipboard *clipboard = &term->wl->clipboard; struct wl_clipboard *clipboard = &seat->clipboard;
if (clipboard->data_source != NULL) { if (clipboard->data_source != NULL) {
/* Kill previous data source */ /* Kill previous data source */
assert(clipboard->serial != 0); assert(clipboard->serial != 0);
wl_data_device_set_selection(term->wl->data_device, NULL, clipboard->serial); wl_data_device_set_selection(seat->data_device, NULL, clipboard->serial);
wl_data_source_destroy(clipboard->data_source); wl_data_source_destroy(clipboard->data_source);
free(clipboard->text); free(clipboard->text);
@ -976,8 +979,8 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial)
/* Configure source */ /* Configure source */
wl_data_source_offer(clipboard->data_source, "text/plain;charset=utf-8"); wl_data_source_offer(clipboard->data_source, "text/plain;charset=utf-8");
wl_data_source_add_listener(clipboard->data_source, &data_source_listener, term->wl); wl_data_source_add_listener(clipboard->data_source, &data_source_listener, seat);
wl_data_device_set_selection(term->wl->data_device, clipboard->data_source, serial); wl_data_device_set_selection(seat->data_device, clipboard->data_source, serial);
/* Needed when sending the selection to other client */ /* Needed when sending the selection to other client */
assert(serial != 0); assert(serial != 0);
@ -986,14 +989,14 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial)
} }
void void
selection_to_clipboard(struct terminal *term, uint32_t serial) selection_to_clipboard(struct seat *seat, struct terminal *term, uint32_t serial)
{ {
if (term->selection.start.row < 0 || term->selection.end.row < 0) if (term->selection.start.row < 0 || term->selection.end.row < 0)
return; return;
/* Get selection as a string */ /* Get selection as a string */
char *text = extract_selection(term); char *text = extract_selection(term);
if (!text_to_clipboard(term, text, serial)) if (!text_to_clipboard(seat, term, text, serial))
free(text); free(text);
} }
@ -1004,6 +1007,7 @@ struct clipboard_receive {
void *user; void *user;
}; };
static bool static bool
fdm_receive(struct fdm *fdm, int fd, int events, void *data) fdm_receive(struct fdm *fdm, int fd, int events, void *data)
{ {
@ -1083,11 +1087,11 @@ begin_receive_clipboard(struct terminal *term, int read_fd,
} }
void void
text_from_clipboard(struct terminal *term, uint32_t serial, text_from_clipboard(struct seat *seat, struct terminal *term,
void (*cb)(const char *data, size_t size, void *user), void (*cb)(const char *data, size_t size, void *user),
void (*done)(void *user), void *user) void (*done)(void *user), void *user)
{ {
struct wl_clipboard *clipboard = &term->wl->clipboard; struct wl_clipboard *clipboard = &seat->clipboard;
if (clipboard->data_offer == NULL) if (clipboard->data_offer == NULL)
return done(user); return done(user);
@ -1128,9 +1132,9 @@ from_clipboard_done(void *user)
} }
void void
selection_from_clipboard(struct terminal *term, uint32_t serial) selection_from_clipboard(struct seat *seat, struct terminal *term, uint32_t serial)
{ {
struct wl_clipboard *clipboard = &term->wl->clipboard; struct wl_clipboard *clipboard = &seat->clipboard;
if (clipboard->data_offer == NULL) if (clipboard->data_offer == NULL)
return; return;
@ -1138,24 +1142,24 @@ selection_from_clipboard(struct terminal *term, uint32_t serial)
term_to_slave(term, "\033[200~", 6); term_to_slave(term, "\033[200~", 6);
text_from_clipboard( text_from_clipboard(
term, serial, &from_clipboard_cb, &from_clipboard_done, term); seat, term, &from_clipboard_cb, &from_clipboard_done, term);
} }
bool bool
text_to_primary(struct terminal *term, char *text, uint32_t serial) text_to_primary(struct seat *seat, struct terminal *term, char *text, uint32_t serial)
{ {
if (term->wl->primary_selection_device_manager == NULL) if (term->wl->primary_selection_device_manager == NULL)
return false; return false;
struct wl_primary *primary = &term->wl->primary; struct wl_primary *primary = &seat->primary;
/* TODO: somehow share code with the clipboard equivalent */ /* TODO: somehow share code with the clipboard equivalent */
if (term->wl->primary.data_source != NULL) { if (seat->primary.data_source != NULL) {
/* Kill previous data source */ /* Kill previous data source */
assert(primary->serial != 0); assert(primary->serial != 0);
zwp_primary_selection_device_v1_set_selection( zwp_primary_selection_device_v1_set_selection(
term->wl->primary_selection_device, NULL, primary->serial); seat->primary_selection_device, NULL, primary->serial);
zwp_primary_selection_source_v1_destroy(primary->data_source); zwp_primary_selection_source_v1_destroy(primary->data_source);
free(primary->text); free(primary->text);
@ -1177,8 +1181,8 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial)
/* Configure source */ /* Configure source */
zwp_primary_selection_source_v1_offer(primary->data_source, "text/plain;charset=utf-8"); zwp_primary_selection_source_v1_offer(primary->data_source, "text/plain;charset=utf-8");
zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, term->wl); zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, seat);
zwp_primary_selection_device_v1_set_selection(term->wl->primary_selection_device, primary->data_source, serial); zwp_primary_selection_device_v1_set_selection(seat->primary_selection_device, primary->data_source, serial);
/* Needed when sending the selection to other client */ /* Needed when sending the selection to other client */
primary->serial = serial; primary->serial = serial;
@ -1186,27 +1190,27 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial)
} }
void void
selection_to_primary(struct terminal *term, uint32_t serial) selection_to_primary(struct seat *seat, struct terminal *term, uint32_t serial)
{ {
if (term->wl->primary_selection_device_manager == NULL) if (term->wl->primary_selection_device_manager == NULL)
return; return;
/* Get selection as a string */ /* Get selection as a string */
char *text = extract_selection(term); char *text = extract_selection(term);
if (!text_to_primary(term, text, serial)) if (!text_to_primary(seat, term, text, serial))
free(text); free(text);
} }
void void
text_from_primary( text_from_primary(
struct terminal *term, struct seat *seat, struct terminal *term,
void (*cb)(const char *data, size_t size, void *user), void (*cb)(const char *data, size_t size, void *user),
void (*done)(void *user), void *user) void (*done)(void *user), void *user)
{ {
if (term->wl->primary_selection_device_manager == NULL) if (term->wl->primary_selection_device_manager == NULL)
return done(user); return done(user);
struct wl_primary *primary = &term->wl->primary; struct wl_primary *primary = &seat->primary;
if (primary->data_offer == NULL) if (primary->data_offer == NULL)
return done(user); return done(user);
@ -1231,19 +1235,19 @@ text_from_primary(
} }
void void
selection_from_primary(struct terminal *term) selection_from_primary(struct seat *seat, struct terminal *term)
{ {
if (term->wl->primary_selection_device_manager == NULL) if (term->wl->primary_selection_device_manager == NULL)
return; return;
struct wl_clipboard *clipboard = &term->wl->clipboard; struct wl_clipboard *clipboard = &seat->clipboard;
if (clipboard->data_offer == NULL) if (clipboard->data_offer == NULL)
return; return;
if (term->bracketed_paste) if (term->bracketed_paste)
term_to_slave(term, "\033[200~", 6); term_to_slave(term, "\033[200~", 6);
text_from_primary(term, &from_clipboard_cb, &from_clipboard_done, term); text_from_primary(seat, term, &from_clipboard_cb, &from_clipboard_done, term);
} }
#if 0 #if 0
@ -1305,8 +1309,8 @@ selection(void *data, struct wl_data_device *wl_data_device,
{ {
/* Selection offer from other client */ /* Selection offer from other client */
struct wayland *wayl = data; struct seat *seat = data;
struct wl_clipboard *clipboard = &wayl->clipboard; struct wl_clipboard *clipboard = &seat->clipboard;
if (clipboard->data_offer != NULL) if (clipboard->data_offer != NULL)
wl_data_offer_destroy(clipboard->data_offer); wl_data_offer_destroy(clipboard->data_offer);
@ -1354,8 +1358,8 @@ primary_selection(void *data,
{ {
/* Selection offer from other client, for primary */ /* Selection offer from other client, for primary */
struct wayland *wayl = data; struct seat *seat = data;
struct wl_primary *primary = &wayl->primary; struct wl_primary *primary = &seat->primary;
if (primary->data_offer != NULL) if (primary->data_offer != NULL)
zwp_primary_selection_offer_v1_destroy(primary->data_offer); zwp_primary_selection_offer_v1_destroy(primary->data_offer);

View file

@ -8,32 +8,41 @@
extern const struct wl_data_device_listener data_device_listener; extern const struct wl_data_device_listener data_device_listener;
extern const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener; extern const struct zwp_primary_selection_device_v1_listener primary_selection_device_listener;
bool selection_enabled(const struct terminal *term); bool selection_enabled(const struct terminal *term, struct seat *seat);
void selection_start( void selection_start(
struct terminal *term, int col, int row, enum selection_kind kind); struct terminal *term, int col, int row, enum selection_kind kind);
void selection_update(struct terminal *term, int col, int row); void selection_update(struct terminal *term, int col, int row);
void selection_finalize(struct terminal *term, uint32_t serial); void selection_finalize(
struct seat *seat, struct terminal *term, uint32_t serial);
void selection_dirty_cells(struct terminal *term); void selection_dirty_cells(struct terminal *term);
void selection_cancel(struct terminal *term); void selection_cancel(struct terminal *term);
void selection_extend(struct terminal *term, int col, int row, uint32_t serial); void selection_extend( struct seat *seat, struct terminal *term,
int col, int row, uint32_t serial);
bool selection_on_rows(const struct terminal *term, int start, int end); bool selection_on_rows(const struct terminal *term, int start, int end);
void selection_view_up(struct terminal *term, int new_view); void selection_view_up(struct terminal *term, int new_view);
void selection_view_down(struct terminal *term, int new_view); void selection_view_down(struct terminal *term, int new_view);
void selection_mark_word(struct terminal *term, int col, int row, void selection_mark_word(
bool spaces_only, uint32_t serial); struct seat *seat, struct terminal *term, int col, int row,
void selection_mark_row(struct terminal *term, int row, uint32_t serial); bool spaces_only, uint32_t serial);
void selection_mark_row(
struct seat *seat, struct terminal *term, int row, uint32_t serial);
void selection_to_clipboard(struct terminal *term, uint32_t serial); void selection_to_clipboard(
void selection_from_clipboard(struct terminal *term, uint32_t serial); struct seat *seat, struct terminal *term, uint32_t serial);
void selection_to_primary(struct terminal *term, uint32_t serial); void selection_from_clipboard(
void selection_from_primary(struct terminal *term); struct seat *seat, struct terminal *term, uint32_t serial);
void selection_to_primary(
struct seat *seat, struct terminal *term, uint32_t serial);
void selection_from_primary(struct seat *seat, struct terminal *term);
/* Copy text *to* primary/clipboard */ /* Copy text *to* primary/clipboard */
bool text_to_clipboard(struct terminal *term, char *text, uint32_t serial); bool text_to_clipboard(
bool text_to_primary(struct terminal *term, char *text, uint32_t serial); struct seat *seat, struct terminal *term, char *text, uint32_t serial);
bool text_to_primary(
struct seat *seat, struct terminal *term, char *text, uint32_t serial);
/* /*
* Copy text *from* primary/clipboard * Copy text *from* primary/clipboard
@ -50,11 +59,11 @@ bool text_to_primary(struct terminal *term, char *text, uint32_t serial);
* point). * point).
*/ */
void text_from_clipboard( void text_from_clipboard(
struct terminal *term, uint32_t serial, struct seat *seat, struct terminal *term,
void (*cb)(const char *data, size_t size, void *user), void (*cb)(const char *data, size_t size, void *user),
void (*done)(void *user), void *user); void (*done)(void *user), void *user);
void text_from_primary( void text_from_primary(
struct terminal *term, struct seat *seat, struct terminal *term,
void (*cb)(const char *data, size_t size, void *user), void (*cb)(const char *data, size_t size, void *user),
void (*dont)(void *user), void *user); void (*dont)(void *user), void *user);

View file

@ -34,9 +34,17 @@
#define PTMX_TIMING 0 #define PTMX_TIMING 0
static const char *const XCURSOR_LEFT_PTR = "left_ptr"; const char *const XCURSOR_LEFT_PTR = "left_ptr";
static const char *const XCURSOR_TEXT = "text"; const char *const XCURSOR_TEXT = "text";
//static const char *const XCURSOR_HAND2 = "hand2"; //const char *const XCURSOR_HAND2 = "hand2";
const char *const XCURSOR_TOP_LEFT_CORNER = "top_left_corner";
const char *const XCURSOR_TOP_RIGHT_CORNER = "top_right_corner";
const char *const XCURSOR_BOTTOM_LEFT_CORNER = "bottom_left_corner";
const char *const XCURSOR_BOTTOM_RIGHT_CORNER = "bottom_right_corner";
const char *const XCURSOR_LEFT_SIDE = "left_side";
const char *const XCURSOR_RIGHT_SIDE = "right_side";
const char *const XCURSOR_TOP_SIDE = "top_side";
const char *const XCURSOR_BOTTOM_SIDE = "bottom_side";
bool bool
term_to_slave(struct terminal *term, const void *_data, size_t len) term_to_slave(struct terminal *term, const void *_data, size_t len)
@ -889,7 +897,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.text = conf->cursor.color.text, .text = conf->cursor.color.text,
.cursor = conf->cursor.color.cursor, .cursor = conf->cursor.color.cursor,
}, },
.xcursor = "text",
.selection = { .selection = {
.start = {-1, -1}, .start = {-1, -1},
.end = {-1, -1}, .end = {-1, -1},
@ -1037,13 +1044,12 @@ fdm_shutdown(struct fdm *fdm, int fd, int events, void *data)
* are deferred (for example, when a screen locker is active), and * are deferred (for example, when a screen locker is active), and
* thus we can get here without having been unmapped. * thus we can get here without having been unmapped.
*/ */
if (wayl->kbd_focus == term) tll_foreach(wayl->seats, it) {
wayl->kbd_focus = NULL; if (it->item.kbd_focus == term)
if (wayl->mouse_focus == term) it->item.kbd_focus = NULL;
wayl->mouse_focus = NULL; if (it->item.mouse_focus == term)
it->item.mouse_focus = NULL;
assert(wayl->kbd_focus != term); }
assert(wayl->mouse_focus != term);
void (*cb)(void *, int) = term->shutdown_cb; void (*cb)(void *, int) = term->shutdown_cb;
void *cb_data = term->shutdown_data; void *cb_data = term->shutdown_data;
@ -1711,7 +1717,7 @@ void
term_cursor_blink_enable(struct terminal *term) term_cursor_blink_enable(struct terminal *term)
{ {
term->cursor_blink.state = CURSOR_BLINK_ON; term->cursor_blink.state = CURSOR_BLINK_ON;
term->cursor_blink.active = term->wl->kbd_focus == term term->cursor_blink.active = term_has_kbd_focus(term)
? cursor_blink_start_timer(term) : true; ? cursor_blink_start_timer(term) : true;
} }
@ -1728,7 +1734,7 @@ term_cursor_blink_restart(struct terminal *term)
{ {
if (term->cursor_blink.active) { if (term->cursor_blink.active) {
term->cursor_blink.state = CURSOR_BLINK_ON; term->cursor_blink.state = CURSOR_BLINK_ON;
term->cursor_blink.active = term->wl->kbd_focus == term term->cursor_blink.active = term_has_kbd_focus(term)
? cursor_blink_start_timer(term) : true; ? cursor_blink_start_timer(term) : true;
} }
} }
@ -1951,9 +1957,23 @@ term_visual_focus_out(struct terminal *term)
cursor_refresh(term); cursor_refresh(term);
} }
bool
term_has_kbd_focus(struct terminal *term)
{
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term)
return true;
}
return false;
}
void void
term_kbd_focus_in(struct terminal *term) term_kbd_focus_in(struct terminal *term)
{ {
if (term_has_kbd_focus(term))
return;
if (term->focus_events) if (term->focus_events)
term_to_slave(term, "\033[I", 3); term_to_slave(term, "\033[I", 3);
} }
@ -1961,6 +1981,9 @@ term_kbd_focus_in(struct terminal *term)
void void
term_kbd_focus_out(struct terminal *term) term_kbd_focus_out(struct terminal *term)
{ {
if (term_has_kbd_focus(term))
return;
if (term->focus_events) if (term->focus_events)
term_to_slave(term, "\033[O", 3); term_to_slave(term, "\033[O", 3);
} }
@ -2052,23 +2075,20 @@ report_mouse_motion(struct terminal *term, int encoded_button, int row, int col)
} }
bool bool
term_mouse_grabbed(const struct terminal *term) term_mouse_grabbed(const struct terminal *term, struct seat *seat)
{ {
/* /*
* Mouse is grabbed by us, regardless of whether mouse tracking has been enabled or not. * Mouse is grabbed by us, regardless of whether mouse tracking has been enabled or not.
*/ */
return return seat->kbd_focus == term &&
term->wl->kbd_focus == term && seat->kbd.shift &&
term->wl->kbd.shift && !seat->kbd.alt && /*!seat->kbd.ctrl &&*/ !seat->kbd.meta;
!term->wl->kbd.alt && /*!term->wl->kbd.ctrl &&*/ !term->wl->kbd.meta;
} }
void void
term_mouse_down(struct terminal *term, int button, int row, int col) term_mouse_down(struct terminal *term, int button, int row, int col,
bool _shift, bool _alt, bool _ctrl)
{ {
if (term_mouse_grabbed(term))
return;
/* Map libevent button event code to X button number */ /* Map libevent button event code to X button number */
int xbutton = linux_mouse_button_to_x(button); int xbutton = linux_mouse_button_to_x(button);
if (xbutton == -1) if (xbutton == -1)
@ -2079,10 +2099,10 @@ term_mouse_down(struct terminal *term, int button, int row, int col)
return; return;
bool has_focus = term->wl->kbd_focus == term; bool has_focus = term_has_kbd_focus(term);
bool shift = has_focus ? term->wl->kbd.shift : false; bool shift = has_focus ? _shift : false;
bool alt = has_focus ? term->wl->kbd.alt : false; bool alt = has_focus ? _alt : false;
bool ctrl = has_focus ? term->wl->kbd.ctrl : false; bool ctrl = has_focus ? _ctrl : false;
encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0); encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0);
@ -2104,11 +2124,9 @@ term_mouse_down(struct terminal *term, int button, int row, int col)
} }
void void
term_mouse_up(struct terminal *term, int button, int row, int col) term_mouse_up(struct terminal *term, int button, int row, int col,
bool _shift, bool _alt, bool _ctrl)
{ {
if (term_mouse_grabbed(term))
return;
/* Map libevent button event code to X button number */ /* Map libevent button event code to X button number */
int xbutton = linux_mouse_button_to_x(button); int xbutton = linux_mouse_button_to_x(button);
if (xbutton == -1) if (xbutton == -1)
@ -2123,10 +2141,10 @@ term_mouse_up(struct terminal *term, int button, int row, int col)
if (encoded == -1) if (encoded == -1)
return; return;
bool has_focus = term->wl->kbd_focus == term; bool has_focus = term_has_kbd_focus(term);
bool shift = has_focus ? term->wl->kbd.shift : false; bool shift = has_focus ? _shift : false;
bool alt = has_focus ? term->wl->kbd.alt : false; bool alt = has_focus ? _alt : false;
bool ctrl = has_focus ? term->wl->kbd.ctrl : false; bool ctrl = has_focus ? _ctrl : false;
encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0); encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0);
@ -2148,11 +2166,9 @@ term_mouse_up(struct terminal *term, int button, int row, int col)
} }
void void
term_mouse_motion(struct terminal *term, int button, int row, int col) term_mouse_motion(struct terminal *term, int button, int row, int col,
bool _shift, bool _alt, bool _ctrl)
{ {
if (term_mouse_grabbed(term))
return;
int encoded = 0; int encoded = 0;
if (button != 0) { if (button != 0) {
@ -2167,10 +2183,10 @@ term_mouse_motion(struct terminal *term, int button, int row, int col)
} else } else
encoded = 3; /* "released" */ encoded = 3; /* "released" */
bool has_focus = term->wl->kbd_focus == term; bool has_focus = term_has_kbd_focus(term);
bool shift = has_focus ? term->wl->kbd.shift : false; bool shift = has_focus ? _shift : false;
bool alt = has_focus ? term->wl->kbd.alt : false; bool alt = has_focus ? _alt : false;
bool ctrl = has_focus ? term->wl->kbd.ctrl : false; bool ctrl = has_focus ? _ctrl : false;
encoded += 32; /* Motion event */ encoded += 32; /* Motion event */
encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0); encoded += (shift ? 4 : 0) + (alt ? 8 : 0) + (ctrl ? 16 : 0);
@ -2199,12 +2215,16 @@ term_mouse_motion(struct terminal *term, int button, int row, int col)
void void
term_xcursor_update(struct terminal *term) term_xcursor_update(struct terminal *term)
{ {
term->xcursor = tll_foreach(term->wl->seats, it) {
term->is_searching ? XCURSOR_LEFT_PTR : /* TODO: something different? */ struct seat *seat = &it->item;
selection_enabled(term) ? XCURSOR_TEXT :
XCURSOR_LEFT_PTR;
render_xcursor_set(term); const char *xcursor
= term->is_searching ? XCURSOR_LEFT_PTR : /* TODO: something different? */
selection_enabled(term, seat) ? XCURSOR_TEXT :
XCURSOR_LEFT_PTR;
render_xcursor_set(seat, term, xcursor);
}
} }
void void

View file

@ -305,7 +305,6 @@ struct terminal {
uint32_t text; uint32_t text;
uint32_t cursor; uint32_t cursor;
} cursor_color; } cursor_color;
const char *xcursor;
struct { struct {
enum selection_kind kind; enum selection_kind kind;
@ -439,6 +438,18 @@ struct terminal {
char *cwd; char *cwd;
}; };
extern const char *const XCURSOR_LEFT_PTR;
extern const char *const XCURSOR_TEXT;
//extern const char *const XCURSOR_HAND2;
extern const char *const XCURSOR_TOP_LEFT_CORNER;
extern const char *const XCURSOR_TOP_RIGHT_CORNER;
extern const char *const XCURSOR_BOTTOM_LEFT_CORNER;
extern const char *const XCURSOR_BOTTOM_RIGHT_CORNER;
extern const char *const XCURSOR_LEFT_SIDE;
extern const char *const XCURSOR_RIGHT_SIDE;
extern const char *const XCURSOR_TOP_SIDE;
extern const char *const XCURSOR_BOTTOM_SIDE;
struct config; struct config;
struct terminal *term_init( struct terminal *term_init(
const struct config *conf, struct fdm *fdm, struct reaper *reaper, const struct config *conf, struct fdm *fdm, struct reaper *reaper,
@ -506,12 +517,19 @@ void term_restore_cursor(struct terminal *term, const struct cursor *cursor);
void term_visual_focus_in(struct terminal *term); void term_visual_focus_in(struct terminal *term);
void term_visual_focus_out(struct terminal *term); void term_visual_focus_out(struct terminal *term);
bool term_has_kbd_focus(struct terminal *term);
void term_kbd_focus_in(struct terminal *term); void term_kbd_focus_in(struct terminal *term);
void term_kbd_focus_out(struct terminal *term); void term_kbd_focus_out(struct terminal *term);
void term_mouse_down(struct terminal *term, int button, int row, int col); void term_mouse_down(
void term_mouse_up(struct terminal *term, int button, int row, int col); struct terminal *term, int button, int row, int col,
void term_mouse_motion(struct terminal *term, int button, int row, int col); bool shift, bool alt, bool ctrl);
bool term_mouse_grabbed(const struct terminal *term); void term_mouse_up(
struct terminal *term, int button, int row, int col,
bool shift, bool alt, bool ctrl);
void term_mouse_motion(
struct terminal *term, int button, int row, int col,
bool shift, bool alt, bool ctrl);
bool term_mouse_grabbed(const struct terminal *term, struct seat *seat);
void term_xcursor_update(struct terminal *term); void term_xcursor_update(struct terminal *term);
void term_set_window_title(struct terminal *term, const char *title); void term_set_window_title(struct terminal *term, const char *title);

427
wayland.c
View file

@ -30,9 +30,6 @@
#include "selection.h" #include "selection.h"
#include "util.h" #include "util.h"
static bool wayl_reload_cursor_theme(
struct wayland *wayl, struct terminal *term);
static void static void
csd_instantiate(struct wl_window *win) csd_instantiate(struct wl_window *win)
{ {
@ -71,6 +68,66 @@ csd_destroy(struct wl_window *win)
} }
} }
static void
seat_destroy(struct seat *seat)
{
if (seat == NULL)
return;
tll_foreach(seat->kbd.bindings.key, it)
tll_free(it->item.bind.key_codes);
tll_free(seat->kbd.bindings.key);
tll_foreach(seat->kbd.bindings.search, it)
tll_free(it->item.bind.key_codes);
tll_free(seat->kbd.bindings.search);
if (seat->kbd.xkb_compose_state != NULL)
xkb_compose_state_unref(seat->kbd.xkb_compose_state);
if (seat->kbd.xkb_compose_table != NULL)
xkb_compose_table_unref(seat->kbd.xkb_compose_table);
if (seat->kbd.xkb_keymap != NULL)
xkb_keymap_unref(seat->kbd.xkb_keymap);
if (seat->kbd.xkb_state != NULL)
xkb_state_unref(seat->kbd.xkb_state);
if (seat->kbd.xkb != NULL)
xkb_context_unref(seat->kbd.xkb);
if (seat->kbd.repeat.fd >= 0)
fdm_del(seat->wayl->fdm, seat->kbd.repeat.fd);
if (seat->pointer.theme != NULL)
wl_cursor_theme_destroy(seat->pointer.theme);
if (seat->pointer.surface != NULL)
wl_surface_destroy(seat->pointer.surface);
if (seat->pointer.xcursor_callback != NULL)
wl_callback_destroy(seat->pointer.xcursor_callback);
if (seat->clipboard.data_source != NULL)
wl_data_source_destroy(seat->clipboard.data_source);
if (seat->clipboard.data_offer != NULL)
wl_data_offer_destroy(seat->clipboard.data_offer);
if (seat->primary.data_source != NULL)
zwp_primary_selection_source_v1_destroy(seat->primary.data_source);
if (seat->primary.data_offer != NULL)
zwp_primary_selection_offer_v1_destroy(seat->primary.data_offer);
if (seat->primary_selection_device != NULL)
zwp_primary_selection_device_v1_destroy(seat->primary_selection_device);
if (seat->data_device != NULL)
wl_data_device_destroy(seat->data_device);
if (seat->wl_keyboard != NULL)
wl_keyboard_destroy(seat->wl_keyboard);
if (seat->wl_pointer != NULL)
wl_pointer_destroy(seat->wl_pointer);
if (seat->wl_seat != NULL)
wl_seat_destroy(seat->wl_seat);
free(seat->clipboard.text);
free(seat->primary.text);
free(seat->name);
}
static void static void
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{ {
@ -98,29 +155,50 @@ static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat, seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) enum wl_seat_capability caps)
{ {
struct wayland *wayl = data; struct seat *seat = data;
assert(seat->wl_seat == wl_seat);
LOG_DBG("%s: keyboard=%s, pointer=%s", seat->name,
(caps & WL_SEAT_CAPABILITY_KEYBOARD) ? "yes" : "no",
(caps & WL_SEAT_CAPABILITY_POINTER) ? "yes" : "no");
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
if (wayl->keyboard == NULL) { if (seat->wl_keyboard == NULL) {
wayl->keyboard = wl_seat_get_keyboard(wl_seat); seat->wl_keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, seat);
} }
} else { } else {
if (wayl->keyboard != NULL) { if (seat->wl_keyboard != NULL) {
wl_keyboard_release(wayl->keyboard); wl_keyboard_release(seat->wl_keyboard);
wayl->keyboard = NULL; seat->wl_keyboard = NULL;
} }
} }
if (caps & WL_SEAT_CAPABILITY_POINTER) { if (caps & WL_SEAT_CAPABILITY_POINTER) {
if (wayl->pointer.pointer == NULL) { if (seat->wl_pointer == NULL) {
wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); assert(seat->pointer.surface == NULL);
wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); seat->pointer.surface = wl_compositor_create_surface(seat->wayl->compositor);
if (seat->pointer.surface == NULL) {
LOG_ERR("%s: failed to create pointer surface", seat->name);
return;
}
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
} }
} else { } else {
if (wayl->pointer.pointer != NULL) { if (seat->wl_pointer != NULL) {
wl_pointer_release(wayl->pointer.pointer); wl_pointer_release(seat->wl_pointer);
wayl->pointer.pointer = NULL; wl_surface_destroy(seat->pointer.surface);
if (seat->pointer.theme != NULL)
wl_cursor_theme_destroy(seat->pointer.theme);
seat->wl_pointer = NULL;
seat->pointer.surface = NULL;
seat->pointer.theme = NULL;
seat->pointer.cursor = NULL;
} }
} }
} }
@ -128,6 +206,9 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
static void static void
seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name)
{ {
struct seat *seat = data;
free(seat->name);
seat->name = strdup(name);
} }
static const struct wl_seat_listener seat_listener = { static const struct wl_seat_listener seat_listener = {
@ -144,7 +225,6 @@ update_term_for_output_change(struct terminal *term)
render_resize(term, term->width / term->scale, term->height / term->scale); render_resize(term, term->width / term->scale, term->height / term->scale);
term_font_dpi_changed(term); term_font_dpi_changed(term);
term_font_subpixel_changed(term); term_font_subpixel_changed(term);
wayl_reload_cursor_theme(term->wl, term);
} }
static void static void
@ -533,6 +613,32 @@ static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration
.configure = &xdg_toplevel_decoration_configure, .configure = &xdg_toplevel_decoration_configure,
}; };
static bool
fdm_repeat(struct fdm *fdm, int fd, int events, void *data)
{
if (events & EPOLLHUP)
return false;
struct seat *seat = data;
uint64_t expiration_count;
ssize_t ret = read(
seat->kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
if (ret < 0) {
if (errno == EAGAIN)
return true;
LOG_ERRNO("failed to read repeat key from repeat timer fd");
return false;
}
seat->kbd.repeat.dont_re_repeat = true;
for (size_t i = 0; i < expiration_count; i++)
input_repeat(seat, seat->kbd.repeat.key);
seat->kbd.repeat.dont_re_repeat = false;
return true;
}
static void static void
handle_global(void *data, struct wl_registry *registry, handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) uint32_t name, const char *interface, uint32_t version)
@ -566,7 +672,6 @@ handle_global(void *data, struct wl_registry *registry,
wayl->shm = wl_registry_bind( wayl->shm = wl_registry_bind(
wayl->registry, name, &wl_shm_interface, required); wayl->registry, name, &wl_shm_interface, required);
wl_shm_add_listener(wayl->shm, &shm_listener, wayl); wl_shm_add_listener(wayl->shm, &shm_listener, wayl);
wl_display_roundtrip(wayl->display);
} }
else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
@ -593,10 +698,55 @@ handle_global(void *data, struct wl_registry *registry,
if (!verify_iface_version(interface, version, required)) if (!verify_iface_version(interface, version, required))
return; return;
wayl->seat = wl_registry_bind( int repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
if (repeat_fd == -1) {
LOG_ERRNO("failed to create keyboard repeat timer FD");
return;
}
struct wl_seat *wl_seat = wl_registry_bind(
wayl->registry, name, &wl_seat_interface, required); wayl->registry, name, &wl_seat_interface, required);
wl_seat_add_listener(wayl->seat, &seat_listener, wayl);
wl_display_roundtrip(wayl->display); /* Clipboard */
struct wl_data_device *data_device = wl_data_device_manager_get_data_device(
wayl->data_device_manager, wl_seat);
/* Primary selection */
struct zwp_primary_selection_device_v1 *primary_selection_device;
if (wayl->primary_selection_device_manager != NULL) {
primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
wayl->primary_selection_device_manager, wl_seat);
} else
primary_selection_device = NULL;
tll_push_back(wayl->seats, ((struct seat){
.wayl = wayl,
.wl_seat = wl_seat,
.wl_name = name,
.kbd = {
.repeat = {
.fd = repeat_fd,
},
},
.data_device = data_device,
.primary_selection_device = primary_selection_device,
}));
struct seat *seat = &tll_back(wayl->seats);
if (!fdm_add(wayl->fdm, repeat_fd, EPOLLIN, &fdm_repeat, seat)) {
close(repeat_fd);
seat->kbd.repeat.fd = -1;
seat_destroy(seat);
return;
}
wl_seat_add_listener(wl_seat, &seat_listener, seat);
wl_data_device_add_listener(data_device, &data_device_listener, seat);
if (primary_selection_device != NULL) {
zwp_primary_selection_device_v1_add_listener(
primary_selection_device, &primary_selection_device_listener, seat);
}
} }
else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
@ -629,7 +779,6 @@ handle_global(void *data, struct wl_registry *registry,
wayl->xdg_output_manager, mon->output); wayl->xdg_output_manager, mon->output);
zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon); zxdg_output_v1_add_listener(mon->xdg, &xdg_output_listener, mon);
} }
wl_display_roundtrip(wayl->display);
} }
else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) { else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
@ -685,7 +834,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
struct wayland *wayl = data; struct wayland *wayl = data;
/* For now, we only support removal of outputs */ /* Check if this is an output */
tll_foreach(wayl->monitors, it) { tll_foreach(wayl->monitors, it) {
struct monitor *mon = &it->item; struct monitor *mon = &it->item;
@ -702,11 +851,40 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
tll_foreach(wayl->terms, t) tll_foreach(wayl->terms, t)
surface_leave(t->item->window, NULL /* wl_surface - unused */, mon->output); surface_leave(t->item->window, NULL /* wl_surface - unused */, mon->output);
monitor_destroy(&it->item); monitor_destroy(mon);
tll_remove(wayl->monitors, it); tll_remove(wayl->monitors, it);
return; return;
} }
/* A seat? */
tll_foreach(wayl->seats, it) {
struct seat *seat = &it->item;
if (seat->wl_name != name)
continue;
LOG_INFO("seat destroyed: %s", seat->name);
if (seat->kbd_focus != NULL) {
LOG_WARN("compositor destroyed seat '%s' "
"without sending keyboard/pointer leave events",
seat->name);
struct terminal *term = seat->kbd_focus;
if (seat->wl_keyboard != NULL)
keyboard_listener.leave(
seat, seat->wl_keyboard, -1, term->window->surface);
if (seat->wl_pointer != NULL)
pointer_listener.leave(
seat, seat->wl_pointer, -1, term->window->surface);
}
seat_destroy(seat);
tll_remove(wayl->seats, it);
}
LOG_WARN("unknown global removed: 0x%08x", name); LOG_WARN("unknown global removed: 0x%08x", name);
} }
@ -747,32 +925,6 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data)
return event_count != -1; return event_count != -1;
} }
static bool
fdm_repeat(struct fdm *fdm, int fd, int events, void *data)
{
if (events & EPOLLHUP)
return false;
struct wayland *wayl = data;
uint64_t expiration_count;
ssize_t ret = read(
wayl->kbd.repeat.fd, &expiration_count, sizeof(expiration_count));
if (ret < 0) {
if (errno == EAGAIN)
return true;
LOG_ERRNO("failed to read repeat key from repeat timer fd");
return false;
}
wayl->kbd.repeat.dont_re_repeat = true;
for (size_t i = 0; i < expiration_count; i++)
input_repeat(wayl, wayl->kbd.repeat.key);
wayl->kbd.repeat.dont_re_repeat = false;
return true;
}
struct wayland * struct wayland *
wayl_init(const struct config *conf, struct fdm *fdm) wayl_init(const struct config *conf, struct fdm *fdm)
{ {
@ -780,7 +932,6 @@ wayl_init(const struct config *conf, struct fdm *fdm)
wayl->conf = conf; wayl->conf = conf;
wayl->fdm = fdm; wayl->fdm = fdm;
wayl->fd = -1; wayl->fd = -1;
wayl->kbd.repeat.fd = -1;
if (!fdm_hook_add(fdm, &fdm_hook, wayl, FDM_HOOK_PRIORITY_LOW)) { if (!fdm_hook_add(fdm, &fdm_hook, wayl, FDM_HOOK_PRIORITY_LOW)) {
LOG_ERR("failed to add FDM hook"); LOG_ERR("failed to add FDM hook");
@ -818,20 +969,11 @@ wayl_init(const struct config *conf, struct fdm *fdm)
LOG_ERR("no XDG shell interface"); LOG_ERR("no XDG shell interface");
goto out; goto out;
} }
if (wayl->seat == NULL) {
LOG_ERR("no seat available");
goto out;
}
if (wayl->data_device_manager == NULL) { if (wayl->data_device_manager == NULL) {
LOG_ERR("no clipboard available " LOG_ERR("no clipboard available "
"(wl_data_device_manager not implemented by server)"); "(wl_data_device_manager not implemented by server)");
goto out; goto out;
} }
if (!wayl->have_argb8888) {
LOG_ERR("compositor does not support ARGB surfaces");
goto out;
}
if (wayl->primary_selection_device_manager == NULL) if (wayl->primary_selection_device_manager == NULL)
LOG_WARN("no primary selection available"); LOG_WARN("no primary selection available");
@ -840,6 +982,14 @@ wayl_init(const struct config *conf, struct fdm *fdm)
goto out; goto out;
} }
/* Trigger listeners registered when handling globals */
wl_display_roundtrip(wayl->display);
if (!wayl->have_argb8888) {
LOG_ERR("compositor does not support ARGB surfaces");
goto out;
}
tll_foreach(wayl->monitors, it) { tll_foreach(wayl->monitors, it) {
LOG_INFO( LOG_INFO(
"%s: %dx%d+%dx%d@%dHz %s %.2f\" scale=%d PPI=%dx%d (physical) PPI=%dx%d (logical)", "%s: %dx%d+%dx%d@%dHz %s %.2f\" scale=%d PPI=%dx%d (physical) PPI=%dx%d (logical)",
@ -851,46 +1001,6 @@ wayl_init(const struct config *conf, struct fdm *fdm)
it->item.ppi.scaled.x, it->item.ppi.scaled.y); it->item.ppi.scaled.x, it->item.ppi.scaled.y);
} }
/* Clipboard */
wayl->data_device = wl_data_device_manager_get_data_device(
wayl->data_device_manager, wayl->seat);
wl_data_device_add_listener(wayl->data_device, &data_device_listener, wayl);
/* Primary selection */
if (wayl->primary_selection_device_manager != NULL) {
wayl->primary_selection_device = zwp_primary_selection_device_manager_v1_get_device(
wayl->primary_selection_device_manager, wayl->seat);
zwp_primary_selection_device_v1_add_listener(
wayl->primary_selection_device, &primary_selection_device_listener, wayl);
}
/* Cursor */
unsigned cursor_size = 24;
const char *cursor_theme = getenv("XCURSOR_THEME");
{
const char *env_cursor_size = getenv("XCURSOR_SIZE");
if (env_cursor_size != NULL) {
unsigned size;
if (sscanf(env_cursor_size, "%u", &size) == 1)
cursor_size = size;
}
}
/* Note: theme is (re)loaded on scale and output changes */
LOG_INFO("cursor theme: %s, size: %u", cursor_theme, cursor_size);
wayl->pointer.size = cursor_size;
wayl->pointer.theme_name = cursor_theme != NULL ? strdup(cursor_theme) : NULL;
wayl->pointer.surface = wl_compositor_create_surface(wayl->compositor);
if (wayl->pointer.surface == NULL) {
LOG_ERR("failed to create cursor surface");
goto out;
}
/* All wayland initialization done - make it so */
wl_display_roundtrip(wayl->display);
wayl->fd = wl_display_get_fd(wayl->display); wayl->fd = wl_display_get_fd(wayl->display);
if (fcntl(wayl->fd, F_SETFL, fcntl(wayl->fd, F_GETFL) | O_NONBLOCK) < 0) { if (fcntl(wayl->fd, F_SETFL, fcntl(wayl->fd, F_GETFL) | O_NONBLOCK) < 0) {
LOG_ERRNO("failed to make Wayland socket non-blocking"); LOG_ERRNO("failed to make Wayland socket non-blocking");
@ -900,15 +1010,6 @@ wayl_init(const struct config *conf, struct fdm *fdm)
if (!fdm_add(fdm, wayl->fd, EPOLLIN, &fdm_wayl, wayl)) if (!fdm_add(fdm, wayl->fd, EPOLLIN, &fdm_wayl, wayl))
goto out; goto out;
wayl->kbd.repeat.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
if (wayl->kbd.repeat.fd == -1) {
LOG_ERRNO("failed to create keyboard repeat timer FD");
goto out;
}
if (!fdm_add(fdm, wayl->kbd.repeat.fd, EPOLLIN, &fdm_repeat, wayl))
goto out;
if (wl_display_prepare_read(wayl->display) != 0) { if (wl_display_prepare_read(wayl->display) != 0) {
LOG_ERRNO("failed to prepare for reading wayland events"); LOG_ERRNO("failed to prepare for reading wayland events");
goto out; goto out;
@ -941,77 +1042,26 @@ wayl_destroy(struct wayland *wayl)
fdm_hook_del(wayl->fdm, &fdm_hook, FDM_HOOK_PRIORITY_LOW); fdm_hook_del(wayl->fdm, &fdm_hook, FDM_HOOK_PRIORITY_LOW);
if (wayl->kbd.repeat.fd != -1) tll_foreach(wayl->monitors, it)
fdm_del(wayl->fdm, wayl->kbd.repeat.fd);
tll_foreach(wayl->monitors, it) {
monitor_destroy(&it->item); monitor_destroy(&it->item);
tll_remove(wayl->monitors, it); tll_free(wayl->monitors);
}
if (wayl->pointer.xcursor_callback != NULL) tll_foreach(wayl->seats, it)
wl_callback_destroy(wayl->pointer.xcursor_callback); seat_destroy(&it->item);
tll_free(wayl->seats);
if (wayl->xdg_output_manager != NULL) if (wayl->xdg_output_manager != NULL)
zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); zxdg_output_manager_v1_destroy(wayl->xdg_output_manager);
if (wayl->shell != NULL) if (wayl->shell != NULL)
xdg_wm_base_destroy(wayl->shell); xdg_wm_base_destroy(wayl->shell);
if (wayl->xdg_decoration_manager != NULL) if (wayl->xdg_decoration_manager != NULL)
zxdg_decoration_manager_v1_destroy(wayl->xdg_decoration_manager); zxdg_decoration_manager_v1_destroy(wayl->xdg_decoration_manager);
if (wayl->presentation != NULL) if (wayl->presentation != NULL)
wp_presentation_destroy(wayl->presentation); wp_presentation_destroy(wayl->presentation);
tll_foreach(wayl->kbd.bindings.key, it)
tll_free(it->item.bind.key_codes);
tll_free(wayl->kbd.bindings.key);
tll_foreach(wayl->kbd.bindings.search, it)
tll_free(it->item.bind.key_codes);
tll_free(wayl->kbd.bindings.search);
if (wayl->kbd.xkb_compose_state != NULL)
xkb_compose_state_unref(wayl->kbd.xkb_compose_state);
if (wayl->kbd.xkb_compose_table != NULL)
xkb_compose_table_unref(wayl->kbd.xkb_compose_table);
if (wayl->kbd.xkb_keymap != NULL)
xkb_keymap_unref(wayl->kbd.xkb_keymap);
if (wayl->kbd.xkb_state != NULL)
xkb_state_unref(wayl->kbd.xkb_state);
if (wayl->kbd.xkb != NULL)
xkb_context_unref(wayl->kbd.xkb);
if (wayl->clipboard.data_source != NULL)
wl_data_source_destroy(wayl->clipboard.data_source);
if (wayl->clipboard.data_offer != NULL)
wl_data_offer_destroy(wayl->clipboard.data_offer);
free(wayl->clipboard.text);
if (wayl->primary.data_source != NULL)
zwp_primary_selection_source_v1_destroy(wayl->primary.data_source);
if (wayl->primary.data_offer != NULL)
zwp_primary_selection_offer_v1_destroy(wayl->primary.data_offer);
free(wayl->primary.text);
free(wayl->pointer.theme_name);
if (wayl->pointer.theme != NULL)
wl_cursor_theme_destroy(wayl->pointer.theme);
if (wayl->pointer.pointer != NULL)
wl_pointer_destroy(wayl->pointer.pointer);
if (wayl->pointer.surface != NULL)
wl_surface_destroy(wayl->pointer.surface);
if (wayl->keyboard != NULL)
wl_keyboard_destroy(wayl->keyboard);
if (wayl->data_device != NULL)
wl_data_device_destroy(wayl->data_device);
if (wayl->data_device_manager != NULL) if (wayl->data_device_manager != NULL)
wl_data_device_manager_destroy(wayl->data_device_manager); wl_data_device_manager_destroy(wayl->data_device_manager);
if (wayl->primary_selection_device != NULL)
zwp_primary_selection_device_v1_destroy(wayl->primary_selection_device);
if (wayl->primary_selection_device_manager != NULL) if (wayl->primary_selection_device_manager != NULL)
zwp_primary_selection_device_manager_v1_destroy(wayl->primary_selection_device_manager); zwp_primary_selection_device_manager_v1_destroy(wayl->primary_selection_device_manager);
if (wayl->seat != NULL)
wl_seat_destroy(wayl->seat);
if (wayl->shm != NULL) if (wayl->shm != NULL)
wl_shm_destroy(wayl->shm); wl_shm_destroy(wayl->shm);
if (wayl->sub_compositor != NULL) if (wayl->sub_compositor != NULL)
@ -1022,9 +1072,8 @@ wayl_destroy(struct wayland *wayl)
wl_registry_destroy(wayl->registry); wl_registry_destroy(wayl->registry);
if (wayl->fd != -1) if (wayl->fd != -1)
fdm_del_no_close(wayl->fdm, wayl->fd); fdm_del_no_close(wayl->fdm, wayl->fd);
if (wayl->display != NULL) { if (wayl->display != NULL)
wl_display_disconnect(wayl->display); wl_display_disconnect(wayl->display);
}
free(wayl); free(wayl);
} }
@ -1160,30 +1209,46 @@ wayl_win_destroy(struct wl_window *win)
free(win); free(win);
} }
static bool bool
wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term) wayl_reload_xcursor_theme(struct seat *seat, int new_scale)
{ {
if (wayl->pointer.size == 0) if (seat->pointer.theme != NULL && seat->pointer.scale == new_scale) {
/* We already have a theme loaded, and the scale hasn't changed */
return true; return true;
if (wayl->pointer.theme != NULL) {
wl_cursor_theme_destroy(wayl->pointer.theme);
wayl->pointer.theme = NULL;
wayl->pointer.cursor = NULL;
} }
LOG_DBG("reloading cursor theme: %s@%d", if (seat->pointer.theme != NULL) {
wayl->pointer.theme_name, wayl->pointer.size); assert(seat->pointer.scale != new_scale);
wl_cursor_theme_destroy(seat->pointer.theme);
seat->pointer.theme = NULL;
seat->pointer.cursor = NULL;
}
wayl->pointer.theme = wl_cursor_theme_load( const char *xcursor_theme = getenv("XCURSOR_THEME");
wayl->pointer.theme_name, wayl->pointer.size * term->scale, wayl->shm); int xcursor_size = 24;
if (wayl->pointer.theme == NULL) { {
const char *env_cursor_size = getenv("XCURSOR_SIZE");
if (env_cursor_size != NULL) {
unsigned size;
if (sscanf(env_cursor_size, "%u", &size) == 1)
xcursor_size = size;
}
}
LOG_INFO("cursor theme: %s, size: %u, scale: %d",
xcursor_theme, xcursor_size, new_scale);
seat->pointer.theme = wl_cursor_theme_load(
xcursor_theme, xcursor_size * new_scale, seat->wayl->shm);
if (seat->pointer.theme == NULL) {
LOG_ERR("failed to load cursor theme"); LOG_ERR("failed to load cursor theme");
return false; return false;
} }
return render_xcursor_set(term); seat->pointer.scale = new_scale;
return true;
} }
void void

289
wayland.h
View file

@ -15,65 +15,6 @@
#include "fdm.h" #include "fdm.h"
struct monitor {
struct wayland *wayl;
struct wl_output *output;
struct zxdg_output_v1 *xdg;
uint32_t wl_name;
int x;
int y;
struct {
/* Physical size, in mm */
struct {
int width;
int height;
} mm;
/* Physical size, in pixels */
struct {
int width;
int height;
} px_real;
/* Scaled size, in pixels */
struct {
int width;
int height;
} px_scaled;
} dim;
struct {
/* PPI, based on physical size */
struct {
int x;
int y;
} real;
/* PPI, logical, based on scaled size */
struct {
int x;
int y;
} scaled;
} ppi;
int scale;
float refresh;
enum wl_output_subpixel subpixel;
/* From wl_output */
char *make;
char *model;
/* From xdg_output */
char *name;
char *description;
float inch; /* e.g. 24" */
};
typedef tll(xkb_keycode_t) xkb_keycode_list_t; typedef tll(xkb_keycode_t) xkb_keycode_list_t;
struct key_binding { struct key_binding {
@ -138,38 +79,6 @@ struct key_binding_search {
enum bind_action_search action; enum bind_action_search action;
}; };
struct kbd {
struct xkb_context *xkb;
struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state;
struct xkb_compose_table *xkb_compose_table;
struct xkb_compose_state *xkb_compose_state;
struct {
int fd;
bool dont_re_repeat;
int32_t delay;
int32_t rate;
uint32_t key;
} repeat;
xkb_mod_index_t mod_shift;
xkb_mod_index_t mod_alt;
xkb_mod_index_t mod_ctrl;
xkb_mod_index_t mod_meta;
/* Enabled modifiers */
bool shift;
bool alt;
bool ctrl;
bool meta;
struct {
tll(struct key_binding_normal) key;
tll(struct key_binding_search) search;
} bindings;
};
struct wl_clipboard { struct wl_clipboard {
struct wl_data_source *data_source; struct wl_data_source *data_source;
struct wl_data_offer *data_offer; struct wl_data_offer *data_offer;
@ -184,6 +93,91 @@ struct wl_primary {
uint32_t serial; uint32_t serial;
}; };
struct seat {
struct wayland *wayl;
struct wl_seat *wl_seat;
uint32_t wl_name;
char *name;
/* Focused terminals */
struct terminal *kbd_focus;
struct terminal *mouse_focus;
/* Keyboard state */
struct wl_keyboard *wl_keyboard;
struct {
uint32_t serial;
struct xkb_context *xkb;
struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state;
struct xkb_compose_table *xkb_compose_table;
struct xkb_compose_state *xkb_compose_state;
struct {
int fd;
bool dont_re_repeat;
int32_t delay;
int32_t rate;
uint32_t key;
} repeat;
xkb_mod_index_t mod_shift;
xkb_mod_index_t mod_alt;
xkb_mod_index_t mod_ctrl;
xkb_mod_index_t mod_meta;
/* Enabled modifiers */
bool shift;
bool alt;
bool ctrl;
bool meta;
struct {
tll(struct key_binding_normal) key;
tll(struct key_binding_search) search;
} bindings;
} kbd;
/* Pointer state */
struct wl_pointer *wl_pointer;
struct {
uint32_t serial;
struct wl_surface *surface;
struct wl_cursor_theme *theme;
struct wl_cursor *cursor;
int scale;
const char *xcursor;
struct wl_callback *xcursor_callback;
bool xcursor_pending;
} pointer;
struct {
int x;
int y;
int col;
int row;
int button;
int count;
int last_button;
struct timeval last_time;
/* We used a discrete axis event in the current pointer frame */
double axis_aggregated;
bool have_discrete;
} mouse;
/* Clipboard */
struct wl_data_device *data_device;
struct zwp_primary_selection_device_v1 *primary_selection_device;
struct wl_clipboard clipboard;
struct wl_primary primary;
};
enum csd_surface { enum csd_surface {
CSD_SURF_TITLE, CSD_SURF_TITLE,
CSD_SURF_LEFT, CSD_SURF_LEFT,
@ -196,6 +190,64 @@ enum csd_surface {
CSD_SURF_COUNT, CSD_SURF_COUNT,
}; };
struct monitor {
struct wayland *wayl;
struct wl_output *output;
struct zxdg_output_v1 *xdg;
uint32_t wl_name;
int x;
int y;
struct {
/* Physical size, in mm */
struct {
int width;
int height;
} mm;
/* Physical size, in pixels */
struct {
int width;
int height;
} px_real;
/* Scaled size, in pixels */
struct {
int width;
int height;
} px_scaled;
} dim;
struct {
/* PPI, based on physical size */
struct {
int x;
int y;
} real;
/* PPI, logical, based on scaled size */
struct {
int x;
int y;
} scaled;
} ppi;
int scale;
float refresh;
enum wl_output_subpixel subpixel;
/* From wl_output */
char *make;
char *model;
/* From xdg_output */
char *name;
char *description;
float inch; /* e.g. 24" */
};
struct wayland; struct wayland;
struct wl_window { struct wl_window {
struct terminal *term; struct terminal *term;
@ -248,67 +300,22 @@ struct wayland {
struct wl_subcompositor *sub_compositor; struct wl_subcompositor *sub_compositor;
struct wl_shm *shm; struct wl_shm *shm;
struct wl_seat *seat;
struct wl_keyboard *keyboard;
struct zxdg_output_manager_v1 *xdg_output_manager; struct zxdg_output_manager_v1 *xdg_output_manager;
struct xdg_wm_base *shell; struct xdg_wm_base *shell;
struct zxdg_decoration_manager_v1 *xdg_decoration_manager; struct zxdg_decoration_manager_v1 *xdg_decoration_manager;
struct wl_data_device_manager *data_device_manager;
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;
struct wp_presentation *presentation; struct wp_presentation *presentation;
uint32_t presentation_clock_id; uint32_t presentation_clock_id;
/* Keyboard */
struct kbd kbd;
/* Clipboard */
uint32_t input_serial;
struct wl_data_device_manager *data_device_manager;
struct wl_data_device *data_device;
struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;
struct zwp_primary_selection_device_v1 *primary_selection_device;
struct wl_clipboard clipboard;
struct wl_primary primary;
/* Cursor */
struct {
struct wl_pointer *pointer;
uint32_t serial;
struct wl_surface *surface;
struct wl_cursor_theme *theme;
struct wl_cursor *cursor;
int size;
char *theme_name;
const char *xcursor;
const struct terminal *pending_terminal;
struct wl_callback *xcursor_callback;
} pointer;
struct {
int x;
int y;
int col;
int row;
int button;
int count;
int last_button;
struct timeval last_time;
/* We used a discrete axis event in the current pointer frame */
double axis_aggregated;
bool have_discrete;
} mouse;
bool have_argb8888; bool have_argb8888;
tll(struct monitor) monitors; /* All available outputs */ tll(struct monitor) monitors; /* All available outputs */
tll(struct seat) seats;
tll(struct terminal *) terms; tll(struct terminal *) terms;
struct terminal *kbd_focus;
struct terminal *mouse_focus;
}; };
struct wayland *wayl_init(const struct config *conf, struct fdm *fdm); struct wayland *wayl_init(const struct config *conf, struct fdm *fdm);
@ -319,3 +326,5 @@ void wayl_roundtrip(struct wayland *wayl);
struct wl_window *wayl_win_init(struct terminal *term); struct wl_window *wayl_win_init(struct terminal *term);
void wayl_win_destroy(struct wl_window *win); void wayl_win_destroy(struct wl_window *win);
bool wayl_reload_xcursor_theme(struct seat *seat, int new_scale);