Work on keyboard grabbing functionality

This commit is contained in:
Kovid Goyal
2025-05-18 11:37:11 +05:30
parent e687d6db05
commit 6f689f3221
14 changed files with 74 additions and 3 deletions

View File

@@ -828,7 +828,7 @@ int _glfwPlatformInit(bool *supports_window_occlusion)
{
debug_key("---------------- key down -------------------\n");
debug_key("%s\n", [[event description] UTF8String]);
if (!_glfw.ignoreOSKeyboardProcessing) {
if (!_glfw.ignoreOSKeyboardProcessing && !_glfw.keyboard_grabbed) {
// first check if there is a global menu bar shortcut
if ([[NSApp mainMenu] performKeyEquivalent:event]) {
debug_key("keyDown triggered global menu bar action ignoring\n");
@@ -1144,3 +1144,4 @@ void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval,
}
void _glfwPlatformInputColorScheme(GLFWColorScheme appearance UNUSED) { }
bool _glfwPlatformGrabKeyboard(bool grab UNUSED) { return true; /* directly uses _glfw.keyboard_grabbed */ }

1
glfw/glfw3.h vendored
View File

@@ -4247,6 +4247,7 @@ GLFWAPI void glfwPostEmptyEvent(void);
GLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void);
GLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled);
GLFWAPI bool glfwGrabKeyboard(int grab);
/*! @brief Returns the value of an input option for the specified window.
*

7
glfw/input.c vendored
View File

@@ -684,6 +684,13 @@ GLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled) {
_glfw.ignoreOSKeyboardProcessing = enabled;
}
GLFWAPI bool glfwGrabKeyboard(int grab) {
if (grab == 0 || grab == 1) {
if (_glfwPlatformGrabKeyboard(grab)) _glfw.keyboard_grabbed = grab;
}
return _glfw.keyboard_grabbed;
}
GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
{
_GLFWwindow* window = (_GLFWwindow*) handle;

3
glfw/internal.h vendored
View File

@@ -616,7 +616,7 @@ struct _GLFWlibrary
_GLFWtls contextSlot;
_GLFWmutex errorLock;
bool ignoreOSKeyboardProcessing;
bool ignoreOSKeyboardProcessing, keyboard_grabbed;
struct {
bool available;
@@ -884,6 +884,7 @@ void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval,
void _glfwPlatformRemoveTimer(unsigned long long timer_id);
int _glfwPlatformSetWindowBlur(_GLFWwindow* handle, int value);
MonitorGeometry _glfwPlatformGetMonitorGeometry(_GLFWmonitor* monitor);
bool _glfwPlatformGrabKeyboard(bool grab);
char* _glfw_strdup(const char* source);

View File

@@ -84,6 +84,7 @@
"staging/fractional-scale/fractional-scale-v1.xml",
"staging/single-pixel-buffer/single-pixel-buffer-v1.xml",
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
"unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml",
"staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml",
"staging/xdg-system-bell/xdg-system-bell-v1.xml",
"staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml",

7
glfw/wl_init.c vendored
View File

@@ -600,6 +600,9 @@ static void registryHandleGlobal(void* data UNUSED,
else if (is(zwp_idle_inhibit_manager_v1)) {
_glfw.wl.idle_inhibit_manager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1);
}
else if (is(zwp_keyboard_shortcuts_inhibit_manager_v1)) {
_glfw.wl.keyboard_shortcuts_inhibit_manager = wl_registry_bind(registry, name, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
}
else if (is(xdg_toplevel_icon_manager_v1)) {
_glfw.wl.xdg_toplevel_icon_manager_v1 = wl_registry_bind(registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);
}
@@ -718,7 +721,7 @@ get_compositor_missing_capabilities(void) {
C(cursor_shape, wp_cursor_shape_manager_v1); C(layer_shell, zwlr_layer_shell_v1);
C(single_pixel_buffer, wp_single_pixel_buffer_manager_v1); C(preferred_scale, has_preferred_buffer_scale);
C(idle_inhibit, idle_inhibit_manager); C(icon, xdg_toplevel_icon_manager_v1); C(bell, xdg_system_bell_v1);
C(window-tag, xdg_toplevel_tag_manager_v1);
C(window-tag, xdg_toplevel_tag_manager_v1); C(keyboard_shortcuts_inhibit, keyboard_shortcuts_inhibit_manager);
if (_glfw.wl.xdg_wm_base_version < 6) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", "window-state-suspended");
if (_glfw.wl.xdg_wm_base_version < 5) p += snprintf(p, sizeof(buf) - (p - buf), "%s ", "window-capabilities");
#undef C
@@ -913,6 +916,8 @@ void _glfwPlatformTerminate(void)
zwlr_layer_shell_v1_destroy(_glfw.wl.zwlr_layer_shell_v1);
if (_glfw.wl.idle_inhibit_manager)
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idle_inhibit_manager);
if (_glfw.wl.keyboard_shortcuts_inhibit_manager)
zwp_keyboard_shortcuts_inhibit_manager_v1_destroy(_glfw.wl.keyboard_shortcuts_inhibit_manager);
if (_glfw.wl.registry)
wl_registry_destroy(_glfw.wl.registry);

3
glfw/wl_platform.h vendored
View File

@@ -66,6 +66,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#include "wayland-wlr-layer-shell-unstable-v1-client-protocol.h"
#include "wayland-single-pixel-buffer-v1-client-protocol.h"
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
#include "wayland-keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
#include "wayland-xdg-toplevel-icon-v1-client-protocol.h"
#include "wayland-xdg-system-bell-v1-client-protocol.h"
#include "wayland-xdg-toplevel-tag-v1-client-protocol.h"
@@ -290,6 +291,7 @@ typedef struct _GLFWwindowWayland
WaylandWindowState toplevel_states;
uint32_t decoration_mode;
} current, pending;
struct zwp_keyboard_shortcuts_inhibitor_v1 *keyboard_shortcuts_inhibitor;
} _GLFWwindowWayland;
typedef enum _GLFWWaylandOfferType
@@ -350,6 +352,7 @@ typedef struct _GLFWlibraryWayland
struct zwlr_layer_shell_v1* zwlr_layer_shell_v1; uint32_t zwlr_layer_shell_v1_version;
struct wp_single_pixel_buffer_manager_v1 *wp_single_pixel_buffer_manager_v1;
struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager;
struct zwp_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit_manager;
int compositorVersion;
int seatVersion;

18
glfw/wl_window.c vendored
View File

@@ -1469,6 +1469,8 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
if (window->id == _glfw.wl.keyRepeatInfo.keyboardFocusId) {
_glfw.wl.keyRepeatInfo.keyboardFocusId = 0;
}
if (window->wl.keyboard_shortcuts_inhibitor)
zwp_keyboard_shortcuts_inhibitor_v1_destroy(window->wl.keyboard_shortcuts_inhibitor);
if (window->wl.temp_buffer_used_during_window_creation)
wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
@@ -2820,6 +2822,22 @@ _glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {
return has_blur ? 1 : 0;
}
bool
_glfwPlatformGrabKeyboard(bool grab) {
if (!_glfw.wl.keyboard_shortcuts_inhibit_manager) return false;
for (_GLFWwindow* window = _glfw.windowListHead; window; window = window->next) {
if (grab) {
if (window->wl.keyboard_shortcuts_inhibitor) break;
window->wl.keyboard_shortcuts_inhibitor = zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(_glfw.wl.keyboard_shortcuts_inhibit_manager, window->wl.surface, _glfw.wl.seat);
} else {
if (!window->wl.keyboard_shortcuts_inhibitor) break;
zwp_keyboard_shortcuts_inhibitor_v1_destroy(window->wl.keyboard_shortcuts_inhibitor);
window->wl.keyboard_shortcuts_inhibitor = NULL;
}
}
return true;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////

6
glfw/x11_window.c vendored
View File

@@ -3384,6 +3384,12 @@ _glfwPlatformSetWindowBlur(_GLFWwindow *window, int blur_radius) {
}
bool
_glfwPlatformGrabKeyboard(bool grab) {
if (grab) _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Grabbing of keyboard is not currently supported");
return false;
}
//////////////////////////////////////////////////////////////////////////
////// GLFW native API //////
//////////////////////////////////////////////////////////////////////////

View File

@@ -87,6 +87,7 @@ from .fast_data_types import (
get_os_window_size,
glfw_get_monitor_workarea,
global_font_size,
grab_keyboard,
is_layer_shell_supported,
last_focused_os_window_id,
mark_os_window_for_close,
@@ -3202,3 +3203,15 @@ class Boss:
if wid == exception:
continue
window.screen.clear_selection()
@ac('misc', '''
Grab the keyboard. This means global shortcuts defined in the OS will be handled in kitty instead. How well this
works depends on the OS/window manager/desktop environment. On Wayland it works only if the compositor implements
the :link:`inhibit-keyboard-shortcuts protocol <https://wayland.app/protocols/keyboard-shortcuts-inhibit-unstable-v1>`.
''')
def grab_keyboard(self) -> None:
grab_keyboard(True)
@ac('misc', 'Ungrab the keyboard if it was previously grabbed')
def ungrab_keyboard(self) -> None:
grab_keyboard(False)

View File

@@ -1748,6 +1748,7 @@ def buffer_keys_in_window(os_window_id: int, tab_id: int, window_id: int, enable
def sprite_idx_to_pos(idx: int, xnum: int, ynum: int) -> tuple[int, int, int]: ...
def render_box_char(ch: int, width: int, height: int, scale: float = 1.0, dpi_x: float = 96.0, dpi_y: float = 96.0) -> bytes: ...
def run_at_exit_cleanup_functions() -> None: ...
def grab_keyboard(grab: bool | None) -> bool: ...
DecorationTypes = Literal[
'curl', 'dashed', 'dotted', 'double', 'straight', 'strikethrough', 'beam_cursor', 'underline_cursor', 'hollow_cursor', 'missing']
def render_decoration(

3
kitty/glfw-wrapper.c generated
View File

@@ -293,6 +293,9 @@ load_glfw(const char* path) {
*(void **) (&glfwSetIgnoreOSKeyboardProcessing_impl) = dlsym(handle, "glfwSetIgnoreOSKeyboardProcessing");
if (glfwSetIgnoreOSKeyboardProcessing_impl == NULL) fail("Failed to load glfw function glfwSetIgnoreOSKeyboardProcessing with error: %s", dlerror());
*(void **) (&glfwGrabKeyboard_impl) = dlsym(handle, "glfwGrabKeyboard");
if (glfwGrabKeyboard_impl == NULL) fail("Failed to load glfw function glfwGrabKeyboard with error: %s", dlerror());
*(void **) (&glfwGetInputMode_impl) = dlsym(handle, "glfwGetInputMode");
if (glfwGetInputMode_impl == NULL) fail("Failed to load glfw function glfwGetInputMode with error: %s", dlerror());

5
kitty/glfw-wrapper.h generated
View File

@@ -794,6 +794,7 @@ typedef enum {
#define GLFW_WAYLAND_APP_ID 0x00025001
#define GLFW_WAYLAND_BGCOLOR 0x00025002
#define GLFW_WAYLAND_WINDOW_TAG 0x00025003
/*! @} */
#define GLFW_NO_API 0
@@ -2089,6 +2090,10 @@ typedef void (*glfwSetIgnoreOSKeyboardProcessing_func)(bool);
GFW_EXTERN glfwSetIgnoreOSKeyboardProcessing_func glfwSetIgnoreOSKeyboardProcessing_impl;
#define glfwSetIgnoreOSKeyboardProcessing glfwSetIgnoreOSKeyboardProcessing_impl
typedef bool (*glfwGrabKeyboard_func)(int);
GFW_EXTERN glfwGrabKeyboard_func glfwGrabKeyboard_impl;
#define glfwGrabKeyboard glfwGrabKeyboard_impl
typedef int (*glfwGetInputMode_func)(GLFWwindow*, int);
GFW_EXTERN glfwGetInputMode_func glfwGetInputMode_impl;
#define glfwGetInputMode glfwGetInputMode_impl

View File

@@ -2612,6 +2612,11 @@ set_layer_shell_config(PyObject *self UNUSED, PyObject *args) {
return Py_NewRef(set_layer_shell_config_for(window, &lsc) ? Py_True : Py_False);
}
static PyObject*
grab_keyboard(PyObject *self UNUSED, PyObject *action) {
if (action == Py_None) return Py_NewRef(glfwGrabKeyboard(2) ? Py_True : Py_False);
return Py_NewRef(glfwGrabKeyboard(PyObject_IsTrue(action)) ? Py_True : Py_False);
}
// Boilerplate {{{
@@ -2621,6 +2626,7 @@ static PyMethodDef module_methods[] = {
METHODB(toggle_os_window_visibility, METH_VARARGS),
METHODB(layer_shell_config_for_os_window, METH_O),
METHODB(set_layer_shell_config, METH_VARARGS),
METHODB(grab_keyboard, METH_O),
METHODB(pointer_name_to_css_name, METH_O),
{"create_os_window", (PyCFunction)(void (*) (void))(create_os_window), METH_VARARGS | METH_KEYWORDS, NULL},
METHODB(set_default_window_icon, METH_VARARGS),