diff --git a/CHANGELOG.md b/CHANGELOG.md index f85f6187..1e931cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ * **blink** option to `footrc`; a boolean that lets you control whether the cursor should blink or not by default. Note that applications can override this. +* Multi-seat support ### Changed diff --git a/README.md b/README.md index 62b9b964..3f628269 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator. * Scrollback search * Color emoji support * Server/daemon mode (one master process, many windows) +* Multi-seat * [Synchronized Updates](https://gitlab.freedesktop.org/terminal-wg/specifications/-/merge_requests/2) support * [Sixel image support](https://en.wikipedia.org/wiki/Sixel) diff --git a/input.c b/input.c index edfca2f7..bc35ad56 100644 --- a/input.c +++ b/input.c @@ -33,8 +33,8 @@ #include "vt.h" static void -execute_binding(struct terminal *term, enum bind_action_normal action, - uint32_t serial) +execute_binding(struct seat *seat, struct terminal *term, + enum bind_action_normal action, uint32_t serial) { switch (action) { case BIND_ACTION_NONE: @@ -49,16 +49,16 @@ execute_binding(struct terminal *term, enum bind_action_normal action, break; case BIND_ACTION_CLIPBOARD_COPY: - selection_to_clipboard(term, serial); + selection_to_clipboard(seat, term, serial); break; case BIND_ACTION_CLIPBOARD_PASTE: - selection_from_clipboard(term, serial); + selection_from_clipboard(seat, term, serial); term_reset_view(term); break; case BIND_ACTION_PRIMARY_PASTE: - selection_from_primary(term); + selection_from_primary(seat, term); break; 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)", 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); - if (wayl->kbd.xkb_compose_state != NULL) { - xkb_compose_state_unref(wayl->kbd.xkb_compose_state); - wayl->kbd.xkb_compose_state = NULL; + if (seat->kbd.xkb_compose_state != NULL) { + xkb_compose_state_unref(seat->kbd.xkb_compose_state); + seat->kbd.xkb_compose_state = NULL; } - if (wayl->kbd.xkb_compose_table != NULL) { - xkb_compose_table_unref(wayl->kbd.xkb_compose_table); - wayl->kbd.xkb_compose_table = NULL; + if (seat->kbd.xkb_compose_table != NULL) { + xkb_compose_table_unref(seat->kbd.xkb_compose_table); + seat->kbd.xkb_compose_table = NULL; } - if (wayl->kbd.xkb_keymap != NULL) { - xkb_keymap_unref(wayl->kbd.xkb_keymap); - wayl->kbd.xkb_keymap = NULL; + if (seat->kbd.xkb_keymap != NULL) { + xkb_keymap_unref(seat->kbd.xkb_keymap); + seat->kbd.xkb_keymap = NULL; } - if (wayl->kbd.xkb_state != NULL) { - xkb_state_unref(wayl->kbd.xkb_state); - wayl->kbd.xkb_state = NULL; + if (seat->kbd.xkb_state != NULL) { + xkb_state_unref(seat->kbd.xkb_state); + seat->kbd.xkb_state = NULL; } - if (wayl->kbd.xkb != NULL) { - xkb_context_unref(wayl->kbd.xkb); - wayl->kbd.xkb = NULL; + if (seat->kbd.xkb != NULL) { + xkb_context_unref(seat->kbd.xkb); + 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(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(wayl->kbd.bindings.search); + tll_free(seat->kbd.bindings.search); - wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - wayl->kbd.xkb_keymap = xkb_keymap_new_from_string( - wayl->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, + seat->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + seat->kbd.xkb_keymap = xkb_keymap_new_from_string( + seat->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); /* TODO: initialize in enter? */ - wayl->kbd.xkb_state = xkb_state_new(wayl->kbd.xkb_keymap); + 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"); - wayl->kbd.mod_alt = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod1") ; - wayl->kbd.mod_ctrl = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Control"); - wayl->kbd.mod_meta = xkb_keymap_mod_get_index(wayl->kbd.xkb_keymap, "Mod4"); + seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift"); + seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ; + seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control"); + seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4"); /* Compose (dead keys) */ - wayl->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( - wayl->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); - wayl->kbd.xkb_compose_state = xkb_compose_state_new( - wayl->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( + seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS); + seat->kbd.xkb_compose_state = xkb_compose_state_new( + seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); munmap(map_str, size); 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++) { key_binding_list_t bindings = tll_init(); 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_push_back( - wayl->kbd.bindings.key, + seat->kbd.bindings.key, ((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++) { key_binding_list_t bindings = tll_init(); 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_push_back( - wayl->kbd.bindings.search, + seat->kbd.bindings.search, ((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); - struct wayland *wayl = data; + struct seat *seat = data; struct wl_window *win = wl_surface_get_user_data(surface); struct terminal *term = win->term; LOG_DBG("keyboard_enter: keyboard=%p, serial=%u, surface=%p", wl_keyboard, serial, surface); - wayl->kbd_focus = term; - wayl->input_serial = serial; - - term_kbd_focus_in(wayl->kbd_focus); + term_kbd_focus_in(term); + seat->kbd_focus = term; + seat->kbd.serial = serial; } 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; struct itimerspec t = { - .it_value = {.tv_sec = 0, .tv_nsec = wayl->kbd.repeat.delay * 1000000}, - .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / wayl->kbd.repeat.rate}, + .it_value = {.tv_sec = 0, .tv_nsec = seat->kbd.repeat.delay * 1000000}, + .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / seat->kbd.repeat.rate}, }; 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_nsec %= 1000000000; } - if (timerfd_settime(wayl->kbd.repeat.fd, 0, &t, NULL) < 0) { - LOG_ERRNO("failed to arm keyboard repeat timer"); + if (timerfd_settime(seat->kbd.repeat.fd, 0, &t, NULL) < 0) { + LOG_ERRNO("%s: failed to arm keyboard repeat timer", seat->name); return false; } - wayl->kbd.repeat.key = key; + seat->kbd.repeat.key = key; return true; } 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; - if (timerfd_settime(wayl->kbd.repeat.fd, 0, &(struct itimerspec){}, NULL) < 0) { - LOG_ERRNO("failed to disarm keyboard repeat timer"); + if (timerfd_settime(seat->kbd.repeat.fd, 0, &(struct itimerspec){}, NULL) < 0) { + LOG_ERRNO("%s: failed to disarm keyboard repeat timer", seat->name); return false; } @@ -355,26 +355,26 @@ static void keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { - struct wayland *wayl = data; + struct seat *seat = data; LOG_DBG("keyboard_leave: keyboard=%p, serial=%u, surface=%p", wl_keyboard, serial, surface); assert( - wayl->kbd_focus == NULL || + seat->kbd_focus == NULL || 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; - wayl->kbd_focus = NULL; + struct terminal *old_focused = seat->kbd_focus; + seat->kbd_focus = NULL; - stop_repeater(wayl, -1); - wayl->kbd.shift = false;; - wayl->kbd.alt = false;; - wayl->kbd.ctrl = false;; - wayl->kbd.meta = false;; - xkb_compose_state_reset(wayl->kbd.xkb_compose_state); + stop_repeater(seat, -1); + seat->kbd.shift = false; + seat->kbd.alt = false; + seat->kbd.ctrl = false; + seat->kbd.meta = false; + xkb_compose_state_reset(seat->kbd.xkb_compose_state); if (old_focused != NULL) term_kbd_focus_out(old_focused); @@ -508,24 +508,24 @@ static void keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - struct wayland *wayl = data; - struct terminal *term = wayl->kbd_focus; + struct seat *seat = data; + struct terminal *term = seat->kbd_focus; assert(term != NULL); - const xkb_mod_mask_t ctrl = 1 << wayl->kbd.mod_ctrl; - const xkb_mod_mask_t alt = 1 << wayl->kbd.mod_alt; - const xkb_mod_mask_t shift = 1 << wayl->kbd.mod_shift; - const xkb_mod_mask_t meta = 1 << wayl->kbd.mod_meta; + const xkb_mod_mask_t ctrl = 1 << seat->kbd.mod_ctrl; + const xkb_mod_mask_t alt = 1 << seat->kbd.mod_alt; + const xkb_mod_mask_t shift = 1 << seat->kbd.mod_shift; + const xkb_mod_mask_t meta = 1 << seat->kbd.mod_meta; if (state == XKB_KEY_UP) { - stop_repeater(wayl, key); + stop_repeater(seat, key); return; } key += 8; - bool should_repeat = xkb_keymap_key_repeats(wayl->kbd.xkb_keymap, key); - xkb_keysym_t sym = xkb_state_key_get_one_sym(wayl->kbd.xkb_state, key); + bool should_repeat = xkb_keymap_key_repeats(seat->kbd.xkb_keymap, key); + xkb_keysym_t sym = xkb_state_key_get_one_sym(seat->kbd.xkb_state, key); #if 0 char foo[100]; @@ -533,9 +533,9 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, LOG_INFO("%s", foo); #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( - wayl->kbd.xkb_compose_state); + seat->kbd.xkb_compose_state); if (compose_status == XKB_COMPOSE_COMPOSING) { /* 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( - wayl->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); - //xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods(wayl->kbd.xkb_state, key); + seat->kbd.xkb_state, XKB_STATE_MODS_DEPRESSED); + //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 significant = ctrl | alt | shift | meta; xkb_mod_mask_t effective_mods = mods & ~consumed & significant; if (term->is_searching) { if (should_repeat) - start_repeater(wayl, key - 8); - search_input(term, key, sym, effective_mods, serial); + start_repeater(seat, key - 8); + search_input(seat, term, key, sym, effective_mods, serial); return; } #if 0 for (size_t i = 0; i < 32; i++) { if (mods & (1 << i)) { - LOG_INFO("%s", xkb_keymap_mod_get_name(wayl->kbd.xkb_keymap, i)); + LOG_INFO("%s", xkb_keymap_mod_get_name(seat->kbd.xkb_keymap, i)); } } #endif @@ -573,20 +573,20 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, /* * User configurable bindings */ - tll_foreach(wayl->kbd.bindings.key, it) { + tll_foreach(seat->kbd.bindings.key, it) { if (it->item.bind.mods != effective_mods) continue; /* Match symbol */ if (it->item.bind.sym == sym) { - execute_binding(term, it->item.action, serial); + execute_binding(seat, term, it->item.action, serial); goto maybe_repeat; } /* Match raw key code */ tll_foreach(it->item.bind.key_codes, code) { if (code->item == key) { - execute_binding(term, it->item.action, serial); + execute_binding(seat, term, it->item.action, serial); 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; - keymap_mods |= wayl->kbd.shift ? MOD_SHIFT : MOD_NONE; - keymap_mods |= wayl->kbd.alt ? MOD_ALT : MOD_NONE; - keymap_mods |= wayl->kbd.ctrl ? MOD_CTRL : MOD_NONE; - keymap_mods |= wayl->kbd.meta ? MOD_META : MOD_NONE; + keymap_mods |= seat->kbd.shift ? MOD_SHIFT : MOD_NONE; + keymap_mods |= seat->kbd.alt ? MOD_ALT : MOD_NONE; + keymap_mods |= seat->kbd.ctrl ? MOD_CTRL : MOD_NONE; + keymap_mods |= seat->kbd.meta ? MOD_META : MOD_NONE; const struct key_data *keymap = keymap_lookup(term, sym, keymap_mods); 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) { count = xkb_compose_state_get_utf8( - wayl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(wayl->kbd.xkb_compose_state); + seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(seat->kbd.xkb_compose_state); } else if (compose_status == XKB_COMPOSE_CANCELLED) { goto maybe_repeat; } else { 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) @@ -720,7 +720,7 @@ maybe_repeat: term->wl->presentation_clock_id, &term->render.input_time); 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_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", mods_depressed, mods_latched, mods_locked, group); 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 */ - wayl->kbd.shift = xkb_state_mod_index_is_active( - wayl->kbd.xkb_state, wayl->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); - wayl->kbd.alt = xkb_state_mod_index_is_active( - wayl->kbd.xkb_state, wayl->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); - wayl->kbd.ctrl = xkb_state_mod_index_is_active( - wayl->kbd.xkb_state, wayl->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); - wayl->kbd.meta = xkb_state_mod_index_is_active( - wayl->kbd.xkb_state, wayl->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); + seat->kbd.shift = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); + seat->kbd.alt = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); + seat->kbd.ctrl = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); + seat->kbd.meta = xkb_state_mod_index_is_active( + seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED); - if (wayl->kbd_focus && wayl->kbd_focus->active_surface == TERM_SURF_GRID) - term_xcursor_update(wayl->kbd_focus); + if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID) + term_xcursor_update(seat->kbd_focus); } static void keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { - struct wayland *wayl = data; + struct seat *seat = data; LOG_DBG("keyboard repeat: rate=%d, delay=%d", rate, delay); - wayl->kbd.repeat.rate = rate; - wayl->kbd.repeat.delay = delay; + seat->kbd.repeat.rate = rate; + seat->kbd.repeat.delay = delay; } const struct wl_keyboard_listener keyboard_listener = { @@ -771,9 +771,9 @@ const struct wl_keyboard_listener keyboard_listener = { }; 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 @@ -817,14 +817,14 @@ is_bottom_right(const struct terminal *term, int x, int y) static const char * xcursor_for_csd_border(struct terminal *term, int x, int y) { - if (is_top_left(term, x, y)) return "top_left_corner"; - else if (is_top_right(term, x, y)) return "top_right_corner"; - else if (is_bottom_left(term, x, y)) return "bottom_left_corner"; - else if (is_bottom_right(term, x, y)) return "bottom_right_corner"; - else if (term->active_surface == TERM_SURF_BORDER_LEFT) return "left_side"; - else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return "right_side"; - else if (term->active_surface == TERM_SURF_BORDER_TOP) return "top_side"; - else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return"bottom_side"; + if (is_top_left(term, x, y)) return XCURSOR_TOP_LEFT_CORNER; + else if (is_top_right(term, x, y)) return XCURSOR_TOP_RIGHT_CORNER; + else if (is_bottom_left(term, x, y)) return XCURSOR_BOTTOM_LEFT_CORNER; + else if (is_bottom_right(term, x, y)) return XCURSOR_BOTTOM_RIGHT_CORNER; + else if (term->active_surface == TERM_SURF_BORDER_LEFT) return XCURSOR_LEFT_SIDE; + else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return XCURSOR_RIGHT_SIDE; + else if (term->active_surface == TERM_SURF_BORDER_TOP) return XCURSOR_TOP_SIDE; + else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return XCURSOR_BOTTOM_SIDE; else { assert(false); return NULL; @@ -838,44 +838,46 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, { assert(surface != NULL); - struct wayland *wayl = data; + struct seat *seat = data; struct wl_window *win = wl_surface_get_user_data(surface); struct terminal *term = win->term; + seat->pointer.serial = serial; + LOG_DBG("pointer-enter: pointer=%p, serial=%u, surface = %p, new-moused = %p", 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 y = wl_fixed_to_int(surface_y) * term->scale; switch ((term->active_surface = term_surface_kind(term, surface))) { case TERM_SURF_GRID: - wayl->mouse.col = x / term->cell_width; - wayl->mouse.row = y / term->cell_height; + seat->mouse.col = x / term->cell_width; + seat->mouse.row = y / term->cell_height; term_xcursor_update(term); break; case TERM_SURF_SEARCH: case TERM_SURF_TITLE: - term->xcursor = "left_ptr"; - render_xcursor_set(term); + render_xcursor_set(seat, term, XCURSOR_LEFT_PTR); break; case TERM_SURF_BORDER_LEFT: case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_BOTTOM: - term->xcursor = xcursor_for_csd_border(term, x, y); - render_xcursor_set(term); + render_xcursor_set(seat, term, xcursor_for_csd_border(term, x, y)); break; case TERM_SURF_BUTTON_MINIMIZE: case TERM_SURF_BUTTON_MAXIMIZE: case TERM_SURF_BUTTON_CLOSE: - term->xcursor = "left_ptr"; - render_xcursor_set(term); + render_xcursor_set(seat, term, XCURSOR_LEFT_PTR); render_refresh_csd(term); break; @@ -889,25 +891,25 @@ static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { - struct wayland *wayl = data; - struct terminal *old_moused = wayl->mouse_focus; + struct seat *seat = data; + struct terminal *old_moused = seat->mouse_focus; LOG_DBG( "pointer-leave: pointer=%p, serial=%u, surface = %p, old-moused = %p", 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 */ - wl_callback_destroy(wayl->pointer.xcursor_callback); - wayl->pointer.xcursor_callback = NULL; - wayl->pointer.pending_terminal = NULL; - wayl->pointer.xcursor = NULL; + wl_callback_destroy(seat->pointer.xcursor_callback); + seat->pointer.xcursor_callback = NULL; + seat->pointer.xcursor_pending = false; + seat->pointer.xcursor = NULL; } /* 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) { LOG_WARN( "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, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - struct wayland *wayl = data; - struct terminal *term = wayl->mouse_focus; + struct seat *seat = data; + struct wayland *wayl = seat->wayl; + struct terminal *term = seat->mouse_focus; struct wl_window *win = term->window; 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 y = wl_fixed_to_int(surface_y) * term->scale; - wayl->mouse.x = x; - wayl->mouse.y = y; + seat->mouse.x = x; + seat->mouse.y = y; switch (term->active_surface) { 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 * right away - abort the timer and initiate the actual move * 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); 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; @@ -991,8 +994,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_BOTTOM: - term->xcursor = xcursor_for_csd_border(term, x, y); - render_xcursor_set(term); + render_xcursor_set(seat, term, xcursor_for_csd_border(term, x, y)); break; 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) 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; if (update_selection && update_selection_early) selection_update(term, col, row); - if (col == wayl->mouse.col && row == wayl->mouse.row) + if (col == seat->mouse.col && row == seat->mouse.row) break; - wayl->mouse.col = col; - wayl->mouse.row = row; + seat->mouse.col = col; + seat->mouse.row = row; if (update_selection && !update_selection_early) selection_update(term, col, row); - term_mouse_motion( - term, wayl->mouse.button, wayl->mouse.row, wayl->mouse.col); + if (!term_mouse_grabbed(term, seat)) { + term_mouse_motion( + term, seat->mouse.button, seat->mouse.row, seat->mouse.col, + seat->kbd.shift, seat->kbd.alt, seat->kbd.ctrl); + } break; } } @@ -1027,13 +1032,20 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, static bool fdm_csd_move(struct fdm *fdm, int fd, int events, void *data) { - struct wl_window *win = data; - struct wayland *wayl = win->term->wl; - + struct seat *seat = data; 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; } @@ -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", wl_pointer, serial, button, state); - struct wayland *wayl = data; - struct terminal *term = wayl->mouse_focus; + struct seat *seat = data; + struct wayland *wayl = seat->wayl; + struct terminal *term = seat->mouse_focus; assert(term != NULL); @@ -1054,22 +1067,22 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, /* Time since last click */ struct timeval now, since_last; gettimeofday(&now, NULL); - timersub(&now, &wayl->mouse.last_time, &since_last); + timersub(&now, &seat->mouse.last_time, &since_last); /* Double- or triple click? */ - if (button == wayl->mouse.last_button && + if (button == seat->mouse.last_button && since_last.tv_sec == 0 && since_last.tv_usec <= 300 * 1000) { - wayl->mouse.count++; + seat->mouse.count++; } else - wayl->mouse.count = 1; + seat->mouse.count = 1; - wayl->mouse.button = button; /* For motion events */ - wayl->mouse.last_button = button; - wayl->mouse.last_time = now; + seat->mouse.button = button; /* For motion events */ + seat->mouse.last_button = button; + seat->mouse.last_time = now; } else - wayl->mouse.button = 0; /* For motion events */ + seat->mouse.button = 0; /* For motion events */ switch (term->active_surface) { case TERM_SURF_TITLE: @@ -1078,7 +1091,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, struct wl_window *win = term->window; /* 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) xdg_toplevel_unset_maximized(win->xdg_toplevel); 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); if (fd >= 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.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) { enum xdg_toplevel_resize_edge resize_type; - int x = wayl->mouse.x; - int y = wayl->mouse.y; + int x = seat->mouse.x; + int y = seat->mouse.y; if (is_top_left(term, x, y)) 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]; xdg_toplevel_resize( - term->window->xdg_toplevel, term->wl->seat, serial, resize_type); + term->window->xdg_toplevel, seat->wl_seat, serial, resize_type); } return; } @@ -1174,32 +1187,35 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, switch (state) { 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); - if (selection_enabled(term)) { - switch (wayl->mouse.count) { + if (selection_enabled(term, seat)) { + switch (seat->mouse.count) { case 1: selection_start( - term, wayl->mouse.col, wayl->mouse.row, - wayl->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); + term, seat->mouse.col, seat->mouse.row, + seat->kbd.ctrl ? SELECTION_BLOCK : SELECTION_NORMAL); break; case 2: - selection_mark_word(term, wayl->mouse.col, wayl->mouse.row, - wayl->kbd.ctrl, serial); + selection_mark_word( + seat, term, seat->mouse.col, seat->mouse.row, + seat->kbd.ctrl, serial); break; case 3: - selection_mark_row(term, wayl->mouse.row, serial); + selection_mark_row(seat, term, seat->mouse.row, serial); break; } } } - else if (button == BTN_RIGHT && wayl->mouse.count == 1) { - if (selection_enabled(term)) - selection_extend(term, wayl->mouse.col, wayl->mouse.row, serial); + else if (button == BTN_RIGHT && seat->mouse.count == 1) { + if (selection_enabled(term, seat)) { + selection_extend( + seat, term, seat->mouse.col, seat->mouse.row, serial); + } } else { @@ -1212,25 +1228,33 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, continue; } - if (binding->count != wayl->mouse.count) { + if (binding->count != seat->mouse.count) { /* Not correct click count */ continue; } - execute_binding(term, binding->action, serial); + execute_binding(seat, term, binding->action, serial); 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; } case WL_POINTER_BUTTON_STATE_RELEASED: 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; @@ -1244,9 +1268,9 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } 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); 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; if (key_arrow_up == 0) { - key_arrow_up = xkb_keymap_key_by_name(wayl->kbd.xkb_keymap, "UP"); - key_arrow_down = xkb_keymap_key_by_name(wayl->kbd.xkb_keymap, "DOWN"); + key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP"); + 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; for (int i = 0; i < amount; i++) - keyboard_key(wayl, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_DOWN); - keyboard_key(wayl, NULL, wayl->input_serial, 0, key - 8, XKB_KEY_UP); + keyboard_key(seat, NULL, seat->kbd.serial, 0, key - 8, XKB_KEY_DOWN); + keyboard_key(seat, NULL, seat->kbd.serial, 0, key - 8, XKB_KEY_UP); } else { - for (int i = 0; i < amount; i++) - term_mouse_down(term, button, wayl->mouse.row, wayl->mouse.col); - term_mouse_up(term, button, wayl->mouse.row, wayl->mouse.col); - + if (!term_mouse_grabbed(term, seat)) { + for (int i = 0; i < amount; i++) { + 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); } } @@ -1293,9 +1323,9 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; - struct wayland *wayl = data; + struct seat *seat = data; - if (wayl->mouse.have_discrete) + if (seat->mouse.have_discrete) return; /* @@ -1304,11 +1334,11 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, * Without this, very slow scrolling will never actually scroll * 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.) { - mouse_scroll(wayl, round(wayl->mouse.axis_aggregated)); - wayl->mouse.axis_aggregated = 0.; + if (fabs(seat->mouse.axis_aggregated) >= 1.) { + mouse_scroll(seat, round(seat->mouse.axis_aggregated)); + 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) return; - struct wayland *wayl = data; - wayl->mouse.have_discrete = true; - mouse_scroll(wayl, discrete); + struct seat *seat = data; + seat->mouse.have_discrete = true; + mouse_scroll(seat, discrete); } static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) { - struct wayland *wayl = data; - wayl->mouse.have_discrete = false; + struct seat *seat = data; + seat->mouse.have_discrete = false; } static void @@ -1344,8 +1374,8 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer, if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL) return; - struct wayland *wayl = data; - wayl->mouse.axis_aggregated = 0.; + struct seat *seat = data; + seat->mouse.axis_aggregated = 0.; } const struct wl_pointer_listener pointer_listener = { diff --git a/input.h b/input.h index 553a6249..c1c14f7c 100644 --- a/input.h +++ b/input.h @@ -8,7 +8,7 @@ extern const struct wl_keyboard_listener keyboard_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, key_binding_list_t *bindings); diff --git a/osc.c b/osc.c index a2702595..42bef722 100644 --- a/osc.c +++ b/osc.c @@ -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) { 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); } if (to_primary) { 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); } @@ -68,6 +82,7 @@ osc_to_clipboard(struct terminal *term, const char *target, } struct clip_context { + struct seat *seat; struct terminal *term; uint8_t buf[3]; int idx; @@ -151,23 +166,37 @@ osc_from_clipboard(struct terminal *term, const char *source) if (src == 0) 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, &src, 1); term_to_slave(term, ";", 1); struct clip_context *ctx = malloc(sizeof(*ctx)); - *ctx = (struct clip_context) {.term = term}; + *ctx = (struct clip_context) {.seat = seat, .term = term}; switch (src) { case 'c': text_from_clipboard( - term, term->wl->input_serial, - &from_clipboard_cb, &from_clipboard_done, ctx); + seat, term, &from_clipboard_cb, &from_clipboard_done, ctx); break; case 's': 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; } } diff --git a/render.c b/render.c index 391115d6..670b9282 100644 --- a/render.c +++ b/render.c @@ -1941,62 +1941,60 @@ render_resize_force(struct terminal *term, int width, int height) static void xcursor_callback( void *data, struct wl_callback *wl_callback, uint32_t callback_data); - static const struct wl_callback_listener xcursor_listener = { .done = &xcursor_callback, }; 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 (wayl->mouse_focus != term) + if (!seat->mouse_focus) return; - wayl->pointer.cursor = wl_cursor_theme_get_cursor(wayl->pointer.theme, term->xcursor); - if (wayl->pointer.cursor == NULL) { - LOG_ERR("%s: failed to load xcursor pointer '%s'", - wayl->pointer.theme_name, term->xcursor); + seat->pointer.cursor = wl_cursor_theme_get_cursor( + seat->pointer.theme, seat->pointer.xcursor); + + if (seat->pointer.cursor == NULL) { + LOG_ERR("failed to load xcursor pointer '%s'", seat->pointer.xcursor); return; } - wayl->pointer.xcursor = term->xcursor; - - const int scale = term->scale; - struct wl_cursor_image *image = wayl->pointer.cursor->images[0]; + const int scale = seat->pointer.scale; + struct wl_cursor_image *image = seat->pointer.cursor->images[0]; 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( - wayl->pointer.pointer, wayl->pointer.serial, - wayl->pointer.surface, + seat->wl_pointer, seat->pointer.serial, + seat->pointer.surface, image->hotspot_x / scale, image->hotspot_y / scale); 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); - wayl->pointer.xcursor_callback = wl_surface_frame(wayl->pointer.surface); - wl_callback_add_listener(wayl->pointer.xcursor_callback, &xcursor_listener, wayl); + assert(seat->pointer.xcursor_callback == NULL); + seat->pointer.xcursor_callback = wl_surface_frame(seat->pointer.surface); + 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 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); - wayl->pointer.xcursor_callback = NULL; + seat->pointer.xcursor_callback = NULL; - if (wayl->pointer.pending_terminal != NULL) { - render_xcursor_update(wayl, wayl->pointer.pending_terminal); - wayl->pointer.pending_terminal = NULL; + if (seat->pointer.xcursor_pending) { + render_xcursor_update(seat); + 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) { - if (wayl->pointer.xcursor_callback == NULL) { - render_xcursor_update(wayl, wayl->pointer.pending_terminal); - wayl->pointer.pending_terminal = NULL; - } else { - /* Frame callback will call render_xcursor_update() */ + tll_foreach(wayl->seats, it) { + if (it->item.pointer.xcursor_pending) { + if (it->item.pointer.xcursor_callback == NULL) { + render_xcursor_update(&it->item); + it->item.pointer.xcursor_pending = false; + } else { + /* Frame callback will call render_xcursor_update() */ + } } } } @@ -2098,28 +2098,26 @@ render_refresh_search(struct terminal *term) } 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 (wayl->pointer.theme == NULL) + if (seat->pointer.theme == NULL) return false; - if (wayl->mouse_focus == NULL) { - wayl->pointer.xcursor = NULL; - wayl->pointer.pending_terminal = NULL; + if (seat->mouse_focus == NULL) { + seat->pointer.xcursor = NULL; return true; } - if (wayl->mouse_focus != term) { + if (seat->mouse_focus != term) { /* This terminal doesn't have mouse focus */ return true; } - if (wayl->pointer.xcursor == term->xcursor) + if (seat->pointer.xcursor == xcursor) return true; /* FDM hook takes care of actual rendering */ - wayl->pointer.pending_terminal = term; + seat->pointer.xcursor_pending = true; + seat->pointer.xcursor = xcursor; return true; } diff --git a/render.h b/render.h index 0c03f125..05c79322 100644 --- a/render.h +++ b/render.h @@ -16,7 +16,7 @@ void render_refresh(struct terminal *term); void render_refresh_csd(struct terminal *term); void render_refresh_search(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 { int my_id; diff --git a/search.c b/search.c index eef7b032..17b1a05e 100644 --- a/search.c +++ b/search.c @@ -429,8 +429,10 @@ execute_binding(struct terminal *term, enum bind_action_search action, return true; case BIND_ACTION_SEARCH_COMMIT: +#if 0 selection_finalize(term, term->wl->input_serial); search_cancel_keep_selection(term); +#endif return true; case BIND_ACTION_SEARCH_FIND_PREV: @@ -567,16 +569,16 @@ execute_binding(struct terminal *term, enum bind_action_search action, } void -search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, - xkb_mod_mask_t mods, uint32_t serial) +search_input(struct seat *seat, struct terminal *term, uint32_t key, + 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); enum xkb_compose_status compose_status = xkb_compose_state_get_status( - term->wl->kbd.xkb_compose_state); + seat->kbd.xkb_compose_state); /* Key bindings */ - tll_foreach(term->wl->kbd.bindings.search, it) { + tll_foreach(seat->kbd.bindings.search, it) { if (it->item.bind.mods != mods) continue; @@ -602,13 +604,13 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, if (compose_status == XKB_COMPOSE_COMPOSED) { count = xkb_compose_state_get_utf8( - term->wl->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); - xkb_compose_state_reset(term->wl->kbd.xkb_compose_state); + seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); + xkb_compose_state_reset(seat->kbd.xkb_compose_state); } else if (compose_status == XKB_COMPOSE_CANCELLED) { count = 0; } else { 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; diff --git a/search.h b/search.h index 69930c07..178b6140 100644 --- a/search.h +++ b/search.h @@ -5,5 +5,5 @@ void search_begin(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, - uint32_t serial); +void search_input(struct seat *seat, struct terminal *term, uint32_t key, + xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial); diff --git a/selection.c b/selection.c index ae6758f4..62f131b1 100644 --- a/selection.c +++ b/selection.c @@ -21,11 +21,11 @@ #include "vt.h" bool -selection_enabled(const struct terminal *term) +selection_enabled(const struct terminal *term, struct seat *seat) { return term->mouse_tracking == MOUSE_NONE || - term_mouse_grabbed(term) || + term_mouse_grabbed(term, seat) || term->is_searching; } @@ -591,7 +591,8 @@ selection_extend_block(struct terminal *term, int col, int row, uint32_t serial) } 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) { /* No existing selection */ @@ -621,13 +622,13 @@ selection_extend(struct terminal *term, int col, int row, uint32_t serial) 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 -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) return; @@ -645,7 +646,7 @@ selection_finalize(struct terminal *term, uint32_t serial) } assert(term->selection.start.row <= term->selection.end.row); - selection_to_primary(term, serial); + selection_to_primary(seat, term, serial); } void @@ -668,8 +669,8 @@ selection_cancel(struct terminal *term) } void -selection_mark_word(struct terminal *term, int col, int row, bool spaces_only, - uint32_t serial) +selection_mark_word(struct seat *seat, struct terminal *term, int col, int row, + bool spaces_only, uint32_t serial) { 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_update(term, end.col, end.row); - selection_finalize(term, serial); + selection_finalize(seat, term, serial); } 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_update(term, term->cols - 1, row); - selection_finalize(term, serial); + selection_finalize(seat, term, serial); } + static void 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, int32_t fd) { - struct wayland *wayl = data; - const struct wl_clipboard *clipboard = &wayl->clipboard; + struct seat *seat = data; + const struct wl_clipboard *clipboard = &seat->clipboard; assert(clipboard != 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, }; - if (fdm_add(wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx)) + if (fdm_add(seat->wayl->fdm, fd, EPOLLOUT, &fdm_send, ctx)) return; free(ctx->data); @@ -839,8 +842,8 @@ send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, static void cancelled(void *data, struct wl_data_source *wl_data_source) { - struct wayland *wayl = data; - struct wl_clipboard *clipboard = &wayl->clipboard; + struct seat *seat = data; + struct wl_clipboard *clipboard = &seat->clipboard; assert(clipboard->data_source == wl_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, const char *mime_type, int32_t fd) { - struct wayland *wayl = data; - const struct wl_primary *primary = &wayl->primary; + struct seat *seat = data; + const struct wl_primary *primary = &seat->primary; assert(primary != NULL); assert(primary->text != NULL); @@ -907,7 +910,7 @@ primary_send(void *data, .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; free(ctx->data); @@ -932,8 +935,8 @@ static void primary_cancelled(void *data, struct zwp_primary_selection_source_v1 *zwp_primary_selection_source_v1) { - struct wayland *wayl = data; - struct wl_primary *primary = &wayl->primary; + struct seat *seat = data; + struct wl_primary *primary = &seat->primary; zwp_primary_selection_source_v1_destroy(primary->data_source); primary->data_source = NULL; @@ -949,14 +952,14 @@ static const struct zwp_primary_selection_source_v1_listener primary_selection_s }; 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) { /* Kill previous data source */ 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); free(clipboard->text); @@ -976,8 +979,8 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial) /* Configure source */ 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_device_set_selection(term->wl->data_device, clipboard->data_source, serial); + wl_data_source_add_listener(clipboard->data_source, &data_source_listener, seat); + wl_data_device_set_selection(seat->data_device, clipboard->data_source, serial); /* Needed when sending the selection to other client */ assert(serial != 0); @@ -986,14 +989,14 @@ text_to_clipboard(struct terminal *term, char *text, uint32_t serial) } 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) return; /* Get selection as a string */ char *text = extract_selection(term); - if (!text_to_clipboard(term, text, serial)) + if (!text_to_clipboard(seat, term, text, serial)) free(text); } @@ -1004,6 +1007,7 @@ struct clipboard_receive { void *user; }; + static bool 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 -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 (*done)(void *user), void *user) { - struct wl_clipboard *clipboard = &term->wl->clipboard; + struct wl_clipboard *clipboard = &seat->clipboard; if (clipboard->data_offer == NULL) return done(user); @@ -1128,9 +1132,9 @@ from_clipboard_done(void *user) } 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) return; @@ -1138,24 +1142,24 @@ selection_from_clipboard(struct terminal *term, uint32_t serial) term_to_slave(term, "\033[200~", 6); text_from_clipboard( - term, serial, &from_clipboard_cb, &from_clipboard_done, term); + seat, term, &from_clipboard_cb, &from_clipboard_done, term); } 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) return false; - struct wl_primary *primary = &term->wl->primary; + struct wl_primary *primary = &seat->primary; /* 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 */ assert(primary->serial != 0); 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); free(primary->text); @@ -1177,8 +1181,8 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) /* Configure source */ 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_device_v1_set_selection(term->wl->primary_selection_device, primary->data_source, serial); + zwp_primary_selection_source_v1_add_listener(primary->data_source, &primary_selection_source_listener, seat); + zwp_primary_selection_device_v1_set_selection(seat->primary_selection_device, primary->data_source, serial); /* Needed when sending the selection to other client */ primary->serial = serial; @@ -1186,27 +1190,27 @@ text_to_primary(struct terminal *term, char *text, uint32_t serial) } 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) return; /* Get selection as a string */ char *text = extract_selection(term); - if (!text_to_primary(term, text, serial)) + if (!text_to_primary(seat, term, text, serial)) free(text); } void text_from_primary( - struct terminal *term, + struct seat *seat, struct terminal *term, void (*cb)(const char *data, size_t size, void *user), void (*done)(void *user), void *user) { if (term->wl->primary_selection_device_manager == NULL) return done(user); - struct wl_primary *primary = &term->wl->primary; + struct wl_primary *primary = &seat->primary; if (primary->data_offer == NULL) return done(user); @@ -1231,19 +1235,19 @@ text_from_primary( } void -selection_from_primary(struct terminal *term) +selection_from_primary(struct seat *seat, struct terminal *term) { if (term->wl->primary_selection_device_manager == NULL) return; - struct wl_clipboard *clipboard = &term->wl->clipboard; + struct wl_clipboard *clipboard = &seat->clipboard; if (clipboard->data_offer == NULL) return; if (term->bracketed_paste) 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 @@ -1305,8 +1309,8 @@ selection(void *data, struct wl_data_device *wl_data_device, { /* Selection offer from other client */ - struct wayland *wayl = data; - struct wl_clipboard *clipboard = &wayl->clipboard; + struct seat *seat = data; + struct wl_clipboard *clipboard = &seat->clipboard; if (clipboard->data_offer != NULL) wl_data_offer_destroy(clipboard->data_offer); @@ -1354,8 +1358,8 @@ primary_selection(void *data, { /* Selection offer from other client, for primary */ - struct wayland *wayl = data; - struct wl_primary *primary = &wayl->primary; + struct seat *seat = data; + struct wl_primary *primary = &seat->primary; if (primary->data_offer != NULL) zwp_primary_selection_offer_v1_destroy(primary->data_offer); diff --git a/selection.h b/selection.h index 758d5d29..bd995855 100644 --- a/selection.h +++ b/selection.h @@ -8,32 +8,41 @@ extern const struct wl_data_device_listener data_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( struct terminal *term, int col, int row, enum selection_kind kind); 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_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); void selection_view_up(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, - bool spaces_only, uint32_t serial); -void selection_mark_row(struct terminal *term, int row, uint32_t serial); +void selection_mark_word( + struct seat *seat, struct terminal *term, int col, int row, + 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_from_clipboard(struct terminal *term, uint32_t serial); -void selection_to_primary(struct terminal *term, uint32_t serial); -void selection_from_primary(struct terminal *term); +void selection_to_clipboard( + struct seat *seat, struct terminal *term, uint32_t serial); +void selection_from_clipboard( + 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 */ -bool text_to_clipboard(struct terminal *term, char *text, uint32_t serial); -bool text_to_primary(struct terminal *term, char *text, uint32_t serial); +bool text_to_clipboard( + 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 @@ -50,11 +59,11 @@ bool text_to_primary(struct terminal *term, char *text, uint32_t serial); * point). */ 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 (*done)(void *user), void *user); void text_from_primary( - struct terminal *term, + struct seat *seat, struct terminal *term, void (*cb)(const char *data, size_t size, void *user), void (*dont)(void *user), void *user); diff --git a/terminal.c b/terminal.c index 0bb545e2..1e7ddabe 100644 --- a/terminal.c +++ b/terminal.c @@ -34,9 +34,17 @@ #define PTMX_TIMING 0 -static const char *const XCURSOR_LEFT_PTR = "left_ptr"; -static const char *const XCURSOR_TEXT = "text"; -//static const char *const XCURSOR_HAND2 = "hand2"; +const char *const XCURSOR_LEFT_PTR = "left_ptr"; +const char *const XCURSOR_TEXT = "text"; +//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 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, .cursor = conf->cursor.color.cursor, }, - .xcursor = "text", .selection = { .start = {-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 * thus we can get here without having been unmapped. */ - if (wayl->kbd_focus == term) - wayl->kbd_focus = NULL; - if (wayl->mouse_focus == term) - wayl->mouse_focus = NULL; - - assert(wayl->kbd_focus != term); - assert(wayl->mouse_focus != term); + tll_foreach(wayl->seats, it) { + if (it->item.kbd_focus == term) + it->item.kbd_focus = NULL; + if (it->item.mouse_focus == term) + it->item.mouse_focus = NULL; + } void (*cb)(void *, int) = term->shutdown_cb; void *cb_data = term->shutdown_data; @@ -1711,7 +1717,7 @@ void term_cursor_blink_enable(struct terminal *term) { 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; } @@ -1728,7 +1734,7 @@ term_cursor_blink_restart(struct terminal *term) { if (term->cursor_blink.active) { 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; } } @@ -1951,9 +1957,23 @@ term_visual_focus_out(struct terminal *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 term_kbd_focus_in(struct terminal *term) { + if (term_has_kbd_focus(term)) + return; + if (term->focus_events) term_to_slave(term, "\033[I", 3); } @@ -1961,6 +1981,9 @@ term_kbd_focus_in(struct terminal *term) void term_kbd_focus_out(struct terminal *term) { + if (term_has_kbd_focus(term)) + return; + if (term->focus_events) 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 -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. */ - return - term->wl->kbd_focus == term && - term->wl->kbd.shift && - !term->wl->kbd.alt && /*!term->wl->kbd.ctrl &&*/ !term->wl->kbd.meta; + return seat->kbd_focus == term && + seat->kbd.shift && + !seat->kbd.alt && /*!seat->kbd.ctrl &&*/ !seat->kbd.meta; } 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 */ int xbutton = linux_mouse_button_to_x(button); if (xbutton == -1) @@ -2079,10 +2099,10 @@ term_mouse_down(struct terminal *term, int button, int row, int col) return; - bool has_focus = term->wl->kbd_focus == term; - bool shift = has_focus ? term->wl->kbd.shift : false; - bool alt = has_focus ? term->wl->kbd.alt : false; - bool ctrl = has_focus ? term->wl->kbd.ctrl : false; + bool has_focus = term_has_kbd_focus(term); + bool shift = has_focus ? _shift : false; + bool alt = has_focus ? _alt : false; + bool ctrl = has_focus ? _ctrl : false; 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 -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 */ int xbutton = linux_mouse_button_to_x(button); if (xbutton == -1) @@ -2123,10 +2141,10 @@ term_mouse_up(struct terminal *term, int button, int row, int col) if (encoded == -1) return; - bool has_focus = term->wl->kbd_focus == term; - bool shift = has_focus ? term->wl->kbd.shift : false; - bool alt = has_focus ? term->wl->kbd.alt : false; - bool ctrl = has_focus ? term->wl->kbd.ctrl : false; + bool has_focus = term_has_kbd_focus(term); + bool shift = has_focus ? _shift : false; + bool alt = has_focus ? _alt : false; + bool ctrl = has_focus ? _ctrl : false; 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 -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; if (button != 0) { @@ -2167,10 +2183,10 @@ term_mouse_motion(struct terminal *term, int button, int row, int col) } else encoded = 3; /* "released" */ - bool has_focus = term->wl->kbd_focus == term; - bool shift = has_focus ? term->wl->kbd.shift : false; - bool alt = has_focus ? term->wl->kbd.alt : false; - bool ctrl = has_focus ? term->wl->kbd.ctrl : false; + bool has_focus = term_has_kbd_focus(term); + bool shift = has_focus ? _shift : false; + bool alt = has_focus ? _alt : false; + bool ctrl = has_focus ? _ctrl : false; encoded += 32; /* Motion event */ 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 term_xcursor_update(struct terminal *term) { - term->xcursor = - term->is_searching ? XCURSOR_LEFT_PTR : /* TODO: something different? */ - selection_enabled(term) ? XCURSOR_TEXT : - XCURSOR_LEFT_PTR; + tll_foreach(term->wl->seats, it) { + struct seat *seat = &it->item; - 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 diff --git a/terminal.h b/terminal.h index bcbfbf4d..8316795f 100644 --- a/terminal.h +++ b/terminal.h @@ -305,7 +305,6 @@ struct terminal { uint32_t text; uint32_t cursor; } cursor_color; - const char *xcursor; struct { enum selection_kind kind; @@ -439,6 +438,18 @@ struct terminal { 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 terminal *term_init( 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_out(struct terminal *term); +bool term_has_kbd_focus(struct terminal *term); void term_kbd_focus_in(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_up(struct terminal *term, int button, int row, int col); -void term_mouse_motion(struct terminal *term, int button, int row, int col); -bool term_mouse_grabbed(const struct terminal *term); +void term_mouse_down( + struct terminal *term, int button, int row, int col, + bool shift, bool alt, bool ctrl); +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_set_window_title(struct terminal *term, const char *title); diff --git a/wayland.c b/wayland.c index ae7ead36..1320fc3a 100644 --- a/wayland.c +++ b/wayland.c @@ -30,9 +30,6 @@ #include "selection.h" #include "util.h" -static bool wayl_reload_cursor_theme( - struct wayland *wayl, struct terminal *term); - static void 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 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, 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 (wayl->keyboard == NULL) { - wayl->keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(wayl->keyboard, &keyboard_listener, wayl); + if (seat->wl_keyboard == NULL) { + seat->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(seat->wl_keyboard, &keyboard_listener, seat); } } else { - if (wayl->keyboard != NULL) { - wl_keyboard_release(wayl->keyboard); - wayl->keyboard = NULL; + if (seat->wl_keyboard != NULL) { + wl_keyboard_release(seat->wl_keyboard); + seat->wl_keyboard = NULL; } } if (caps & WL_SEAT_CAPABILITY_POINTER) { - if (wayl->pointer.pointer == NULL) { - wayl->pointer.pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(wayl->pointer.pointer, &pointer_listener, wayl); + if (seat->wl_pointer == NULL) { + assert(seat->pointer.surface == NULL); + 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 { - if (wayl->pointer.pointer != NULL) { - wl_pointer_release(wayl->pointer.pointer); - wayl->pointer.pointer = NULL; + if (seat->wl_pointer != NULL) { + wl_pointer_release(seat->wl_pointer); + 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 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 = { @@ -144,7 +225,6 @@ update_term_for_output_change(struct terminal *term) render_resize(term, term->width / term->scale, term->height / term->scale); term_font_dpi_changed(term); term_font_subpixel_changed(term); - wayl_reload_cursor_theme(term->wl, term); } static void @@ -533,6 +613,32 @@ static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration .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 handle_global(void *data, struct wl_registry *registry, 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->registry, name, &wl_shm_interface, required); wl_shm_add_listener(wayl->shm, &shm_listener, wayl); - wl_display_roundtrip(wayl->display); } 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)) 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); - 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) { @@ -629,7 +779,6 @@ handle_global(void *data, struct wl_registry *registry, wayl->xdg_output_manager, mon->output); 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) { @@ -685,7 +834,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) struct wayland *wayl = data; - /* For now, we only support removal of outputs */ + /* Check if this is an output */ tll_foreach(wayl->monitors, it) { 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) surface_leave(t->item->window, NULL /* wl_surface - unused */, mon->output); - monitor_destroy(&it->item); + monitor_destroy(mon); tll_remove(wayl->monitors, it); 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); } @@ -747,32 +925,6 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data) 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 * 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->fdm = fdm; wayl->fd = -1; - wayl->kbd.repeat.fd = -1; if (!fdm_hook_add(fdm, &fdm_hook, wayl, FDM_HOOK_PRIORITY_LOW)) { 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"); goto out; } - if (wayl->seat == NULL) { - LOG_ERR("no seat available"); - goto out; - } if (wayl->data_device_manager == NULL) { LOG_ERR("no clipboard available " "(wl_data_device_manager not implemented by server)"); goto out; } - if (!wayl->have_argb8888) { - LOG_ERR("compositor does not support ARGB surfaces"); - goto out; - } - if (wayl->primary_selection_device_manager == NULL) LOG_WARN("no primary selection available"); @@ -840,6 +982,14 @@ wayl_init(const struct config *conf, struct fdm *fdm) 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) { LOG_INFO( "%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); } - /* 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); if (fcntl(wayl->fd, F_SETFL, fcntl(wayl->fd, F_GETFL) | O_NONBLOCK) < 0) { 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)) 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) { LOG_ERRNO("failed to prepare for reading wayland events"); goto out; @@ -941,77 +1042,26 @@ wayl_destroy(struct wayland *wayl) fdm_hook_del(wayl->fdm, &fdm_hook, FDM_HOOK_PRIORITY_LOW); - if (wayl->kbd.repeat.fd != -1) - fdm_del(wayl->fdm, wayl->kbd.repeat.fd); - - tll_foreach(wayl->monitors, it) { + tll_foreach(wayl->monitors, it) monitor_destroy(&it->item); - tll_remove(wayl->monitors, it); - } + tll_free(wayl->monitors); - if (wayl->pointer.xcursor_callback != NULL) - wl_callback_destroy(wayl->pointer.xcursor_callback); + tll_foreach(wayl->seats, it) + seat_destroy(&it->item); + tll_free(wayl->seats); if (wayl->xdg_output_manager != NULL) zxdg_output_manager_v1_destroy(wayl->xdg_output_manager); if (wayl->shell != NULL) xdg_wm_base_destroy(wayl->shell); - if (wayl->xdg_decoration_manager != NULL) zxdg_decoration_manager_v1_destroy(wayl->xdg_decoration_manager); - if (wayl->presentation != NULL) 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) 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) 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) wl_shm_destroy(wayl->shm); if (wayl->sub_compositor != NULL) @@ -1022,9 +1072,8 @@ wayl_destroy(struct wayland *wayl) wl_registry_destroy(wayl->registry); if (wayl->fd != -1) fdm_del_no_close(wayl->fdm, wayl->fd); - if (wayl->display != NULL) { + if (wayl->display != NULL) wl_display_disconnect(wayl->display); - } free(wayl); } @@ -1160,30 +1209,46 @@ wayl_win_destroy(struct wl_window *win) free(win); } -static bool -wayl_reload_cursor_theme(struct wayland *wayl, struct terminal *term) +bool +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; - - 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", - wayl->pointer.theme_name, wayl->pointer.size); + if (seat->pointer.theme != NULL) { + 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( - wayl->pointer.theme_name, wayl->pointer.size * term->scale, wayl->shm); + const char *xcursor_theme = getenv("XCURSOR_THEME"); + 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"); return false; } - return render_xcursor_set(term); + seat->pointer.scale = new_scale; + return true; } void diff --git a/wayland.h b/wayland.h index ccc03ad3..224853a6 100644 --- a/wayland.h +++ b/wayland.h @@ -15,65 +15,6 @@ #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; struct key_binding { @@ -138,38 +79,6 @@ struct key_binding_search { 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_data_source *data_source; struct wl_data_offer *data_offer; @@ -184,6 +93,91 @@ struct wl_primary { 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 { CSD_SURF_TITLE, CSD_SURF_LEFT, @@ -196,6 +190,64 @@ enum csd_surface { 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 wl_window { struct terminal *term; @@ -248,67 +300,22 @@ struct wayland { struct wl_subcompositor *sub_compositor; struct wl_shm *shm; - struct wl_seat *seat; - struct wl_keyboard *keyboard; struct zxdg_output_manager_v1 *xdg_output_manager; struct xdg_wm_base *shell; 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; 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; tll(struct monitor) monitors; /* All available outputs */ + tll(struct seat) seats; tll(struct terminal *) terms; - struct terminal *kbd_focus; - struct terminal *mouse_focus; }; 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); void wayl_win_destroy(struct wl_window *win); + +bool wayl_reload_xcursor_theme(struct seat *seat, int new_scale);