mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-02 12:44:01 +02:00
Merge branch 'fix-tabs-mouse-handling-stuck' of https://github.com/ttys3/kitty
This commit is contained in:
8
glfw/wl_init.c
vendored
8
glfw/wl_init.c
vendored
@@ -102,6 +102,11 @@ pointerHandleEnter(
|
||||
|
||||
static void
|
||||
pointerHandleLeave(void* data UNUSED, struct wl_pointer* pointer UNUSED, uint32_t serial, struct wl_surface* surface) {
|
||||
// The pointer never leaves the surface during an implicit grab, so a
|
||||
// leave event means any implicit grab is over (e.g. the compositor took
|
||||
// over the pointer for drag-and-drop). The matching button releases will
|
||||
// never be delivered to us.
|
||||
_glfw.wl.pointer_button_count = 0;
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
if (!window) return;
|
||||
_glfw.wl.serial = serial;
|
||||
@@ -138,6 +143,9 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
{
|
||||
glfw_cancel_momentum_scroll();
|
||||
_glfw.wl.serial = serial; _glfw.wl.input_serial = serial; _glfw.wl.pointer_serial = serial;
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
if (_glfw.wl.pointer_button_count++ == 0) _glfw.wl.pointer_grab_serial = serial;
|
||||
} else if (_glfw.wl.pointer_button_count > 0) _glfw.wl.pointer_button_count--;
|
||||
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
if (!window) return;
|
||||
|
||||
6
glfw/wl_platform.h
vendored
6
glfw/wl_platform.h
vendored
@@ -374,6 +374,12 @@ typedef struct _GLFWlibraryWayland
|
||||
struct wl_surface* cursorSurface;
|
||||
GLFWCursorShape cursorPreviousShape;
|
||||
uint32_t serial, input_serial, pointer_serial, pointer_enter_serial, keyboard_enter_serial;
|
||||
// serial of the button press that started the current pointer implicit
|
||||
// grab, and the number of currently pressed pointer buttons. Requests
|
||||
// such as wl_data_device.start_drag are silently ignored by compositors
|
||||
// unless made with the serial of an active implicit grab.
|
||||
uint32_t pointer_grab_serial;
|
||||
unsigned pointer_button_count;
|
||||
|
||||
int32_t keyboardRepeatRate;
|
||||
monotonic_t keyboardRepeatDelay;
|
||||
|
||||
13
glfw/wl_window.c
vendored
13
glfw/wl_window.c
vendored
@@ -3496,6 +3496,17 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {
|
||||
return ENOTSUP;
|
||||
}
|
||||
|
||||
if (_glfw.wl.pointer_button_count == 0) {
|
||||
// start_drag requires the serial of an active pointer implicit grab,
|
||||
// without one the compositor silently ignores the request and the
|
||||
// data source never receives any events, so fail early instead.
|
||||
// This can happen as drags are started asynchronously and the button
|
||||
// may have been released by the time we get here. EPERM matches what
|
||||
// start_window_drag() in kitty/glfw.c reports for this situation.
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Refusing to start drag without an active pointer implicit grab");
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
// Create the data source
|
||||
_glfw.wl.drag.source = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager);
|
||||
if (!_glfw.wl.drag.source) {
|
||||
@@ -3568,7 +3579,7 @@ _glfwPlatformStartDrag(_GLFWwindow* window, const GLFWimage* thumbnail) {
|
||||
wl_data_device_start_drag(
|
||||
_glfw.wl.dataDevice, _glfw.wl.drag.source, window->wl.surface,
|
||||
_glfw.wl.drag.toplevel_drag ? NULL : _glfw.wl.drag.drag_icon,
|
||||
_glfw.wl.pointer_serial);
|
||||
_glfw.wl.pointer_grab_serial);
|
||||
|
||||
if (_glfw.wl.drag.toplevel_drag) {
|
||||
// Attach the toplevel AFTER start_drag, otherwise doesnt work on mutter
|
||||
|
||||
@@ -2046,8 +2046,9 @@ class Boss:
|
||||
self._move_window_to(window, target_os_window_id='new')
|
||||
return
|
||||
if (tab_id := int((data or {}).get(f'application/net.kovidgoyal.kitty-tab-{os.getpid()}', b'0').decode())
|
||||
) and get_tab_being_dragged()[0] == tab_id and (tab := self.tab_for_id(tab_id)):
|
||||
if needs_toplevel_on_wayland:
|
||||
) and get_tab_being_dragged()[0] == tab_id:
|
||||
tab = self.tab_for_id(tab_id)
|
||||
if tab is not None and needs_toplevel_on_wayland:
|
||||
for tm in self.all_tab_managers:
|
||||
if tm.tab_being_dropped:
|
||||
tm.on_tab_drop(0, 0, bypass_move=True)
|
||||
@@ -2055,7 +2056,7 @@ class Boss:
|
||||
set_tab_being_dragged()
|
||||
for tm in self.all_tab_managers:
|
||||
tm.on_tab_drop_move()
|
||||
if was_dropped: # detach tab into new OS Window
|
||||
if was_dropped and tab is not None: # detach tab into new OS Window
|
||||
self._move_tab_to(tab)
|
||||
|
||||
@ac('win', '''
|
||||
|
||||
@@ -957,6 +957,19 @@ static void
|
||||
handle_tab_bar_mouse(int button, int modifiers, int action) {
|
||||
set_currently_hovered_window(0, modifiers, false);
|
||||
OSWindow *w = global_state.callback_os_window;
|
||||
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE && global_state.tab_being_dragged.id
|
||||
&& global_state.tab_being_dragged.drag_started && !global_state.drag_source.is_active) {
|
||||
// Once a system drag and drop is active the release is consumed by it
|
||||
// and never delivered to us, so getting one here means the drag never
|
||||
// became a system DND: either glfwStartDrag failed/was not called yet
|
||||
// or the compositor silently ignored it (Wayland with a stale serial).
|
||||
// Clear the drag state so mouse handling is not redirected to the tab
|
||||
// bar forever, and swallow the release as it ended an aborted drag.
|
||||
zero_at_ptr(&global_state.tab_being_dragged);
|
||||
// re-render the tab bar in case it was drawn without the dragged tab
|
||||
if (w) w->tab_bar_data_updated = false;
|
||||
return;
|
||||
}
|
||||
// dont report motion events, as they are expensive and useless
|
||||
if (w && (button > -1 || global_state.tab_being_dragged.id)) {
|
||||
call_boss(handle_tab_bar_mouse, "Kddiii", w->id, w->mouse_x, w->mouse_y, button, modifiers, action);
|
||||
|
||||
@@ -1732,6 +1732,9 @@ class TabManager: # {{{
|
||||
if (td := self.tab_being_dropped) is None:
|
||||
return
|
||||
if (tab := get_boss().tab_for_id(td.data.tab_id)) is None:
|
||||
self.tab_being_dropped = None
|
||||
set_tab_being_dragged()
|
||||
self.layout_tab_bar()
|
||||
return
|
||||
if not bypass_move:
|
||||
self.on_tab_drop_move(td.data.tab_id, True, x, y)
|
||||
@@ -1779,7 +1782,12 @@ class TabManager: # {{{
|
||||
drag_data = {
|
||||
f'application/net.kovidgoyal.kitty-tab-{os.getpid()}': str(tab.id).encode(),
|
||||
}
|
||||
start_drag_with_data(self.os_window_id, drag_data, thumbnails)
|
||||
try:
|
||||
start_drag_with_data(self.os_window_id, drag_data, thumbnails)
|
||||
except OSError as e:
|
||||
log_error(f'Failed to start tab drag: {e}')
|
||||
set_tab_being_dragged()
|
||||
self.mark_tab_bar_dirty() # re-render the tab bar in case it was drawn without the dragged tab
|
||||
break
|
||||
else:
|
||||
set_tab_being_dragged()
|
||||
@@ -1797,16 +1805,21 @@ class TabManager: # {{{
|
||||
|
||||
tab_id_at_x = self.tab_bar.tab_id_at(int(x))
|
||||
self.recent_tab_bar_mouse_events.add(button, modifiers, action, x, y, tab_id_at_x)
|
||||
drag_started = get_tab_being_dragged()[1]
|
||||
is_left_release = button == GLFW_MOUSE_BUTTON_LEFT and action == GLFW_RELEASE
|
||||
if tab_id_at_x < 0: # synthetic tab (e.g. "+" new-tab button)
|
||||
if is_left_release and not drag_started:
|
||||
set_tab_being_dragged() # clear potential drag from a press on a tab
|
||||
if self.recent_tab_bar_mouse_events.click_count(GLFW_MOUSE_BUTTON_LEFT) == 1:
|
||||
self.new_tab()
|
||||
self.recent_tab_bar_mouse_events.clear()
|
||||
return
|
||||
drag_started = get_tab_being_dragged()[1]
|
||||
if drag_started:
|
||||
return
|
||||
tab = self.tab_for_id(tab_id_at_x)
|
||||
if tab is None:
|
||||
if is_left_release:
|
||||
set_tab_being_dragged() # clear potential drag from a press on a tab
|
||||
if self.recent_tab_bar_mouse_events.click_count(GLFW_MOUSE_BUTTON_LEFT) == 2:
|
||||
self.new_tab()
|
||||
self.recent_tab_bar_mouse_events.clear()
|
||||
|
||||
Reference in New Issue
Block a user