Merge branch 'wayland-initial-window-size' of https://github.com/egnor/kitty
Some checks are pending
CI / Linux (python=3.14 cc=clang sanitize=1) (push) Waiting to run
CI / Linux (python=3.12 cc=gcc sanitize=0) (push) Waiting to run
CI / Linux (python=3.13 cc=gcc sanitize=1) (push) Waiting to run
CI / Linux package (push) Waiting to run
CI / Bundle test (macos-latest) (push) Waiting to run
CI / Bundle test (ubuntu-latest) (push) Waiting to run
CI / macOS Brew (push) Waiting to run
CI / Test ./dev.sh and benchmark (push) Waiting to run
CodeQL / CodeQL-Build (actions, ubuntu-latest) (push) Waiting to run
CodeQL / CodeQL-Build (c, macos-latest) (push) Waiting to run
CodeQL / CodeQL-Build (c, ubuntu-latest) (push) Waiting to run
CodeQL / CodeQL-Build (go, ubuntu-latest) (push) Waiting to run
CodeQL / CodeQL-Build (python, ubuntu-latest) (push) Waiting to run
Depscan / Scan dependencies for vulnerabilities (push) Waiting to run

This commit is contained in:
Kovid Goyal 2026-07-19 13:49:39 +05:30
commit 0e38ca2b86
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 86 additions and 7 deletions

View file

@ -318,6 +318,7 @@ def generate_wrappers(glfw_header: str) -> None:
void glfwCocoaSetWindowChrome(GLFWwindow* window, unsigned int color, bool use_system_color, unsigned int system_color,\
int background_blur, unsigned int hide_window_decorations, bool show_text_in_titlebar, int color_space, float background_opacity, bool resizable)
void glfwCocoaRegisterMIMETypes(GLFWwindow *window, const char **mimes, size_t count)
void glfwCocoaSetWindowLevel(GLFWwindow *window, const char *level_spec)
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
int glfwGetNativeKeyForName(const char* key_name, int case_sensitive)
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
@ -326,6 +327,7 @@ def generate_wrappers(glfw_header: str) -> None:
void glfwWaylandRunWithActivationToken(GLFWwindow *handle, GLFWactivationcallback cb, void *cb_data)
bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color)
void glfwWaylandSetTitlebarHidden(GLFWwindow *handle, bool hidden)
void glfwWaylandSetInitialWindowSizeCallback(GLFWwaylandinitialsizefun callback)
void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle)
bool glfwWaylandIsWindowFullyCreated(GLFWwindow *handle)
bool glfwWaylandBeep(GLFWwindow *handle)

1
glfw/glfw3.h vendored
View file

@ -1933,6 +1933,7 @@ typedef void (* GLFWjoystickfun)(int,int);
typedef void (* GLFWuserdatafun)(unsigned long long, void*);
typedef void (* GLFWtickcallback)(void*);
typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);
typedef void (* GLFWwaylandinitialsizefun)(GLFWwindow *window, float xscale, float yscale, int *width, int *height);
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool is_single_glyph);
typedef char* (* GLFWcurrentselectionfun)(void);
typedef bool (* GLFWhascurrentselectionfun)(void);

33
glfw/wl_window.c vendored
View file

@ -440,8 +440,41 @@ clipboard_mime(void) {
return buf;
}
static GLFWwaylandinitialsizefun initial_window_size_callback = NULL;
GLFWAPI void
glfwWaylandSetInitialWindowSizeCallback(GLFWwaylandinitialsizefun callback) {
initial_window_size_callback = callback;
}
static void
maybe_recompute_initial_window_size(_GLFWwindow *window) {
// Before the initial window is mapped, the compositor tells us the real
// (possibly fractional) scale. For sizes specified in cells this changes
// the correct logical window size, because cell metrics are not a linear
// function of scale (integer-pixel rounding). Ask the embedder to recompute
// the logical size now, while the window is still unmapped, so it is mapped
// at the right size and the compositor's authoritative configure agrees.
if (window->wl.window_fully_created) return;
if (!initial_window_size_callback) return;
if (!window->wl.xdg.toplevel) return; // only ordinary toplevels have cell-based sizes
if (window->wl.layer_shell.zwlr_layer_surface_v1) return; // layer-shell surfaces size themselves
double scale = _glfwWaylandWindowScale(window);
int w = window->wl.width, h = window->wl.height;
initial_window_size_callback((GLFWwindow*)window, (float)scale, (float)scale, &w, &h);
if (w > 0 && h > 0 && (w != window->wl.width || h != window->wl.height)) {
debug("Recomputed initial size of window %llu for scale %.3f: %dx%d -> %dx%d\n",
window->id, scale, window->wl.width, window->wl.height, w, h);
window->wl.width = w; window->wl.height = h;
window->wl.user_requested_content_size.width = w;
window->wl.user_requested_content_size.height = h;
update_regions(window);
}
}
static void
apply_scale_changes(_GLFWwindow *window, bool resize_framebuffer, bool update_csd) {
maybe_recompute_initial_window_size(window);
double scale = _glfwWaylandWindowScale(window);
if (resize_framebuffer) resizeFramebuffer(window);
_glfwInputWindowContentScale(window, (float)scale, (float)scale);

9
kitty/glfw-wrapper.c generated
View file

@ -497,15 +497,15 @@ load_glfw(const char* path) {
*(void **) (&glfwCocoaCycleThroughOSWindows_impl) = dlsym(handle, "glfwCocoaCycleThroughOSWindows");
if (glfwCocoaCycleThroughOSWindows_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwCocoaSetWindowLevel_impl) = dlsym(handle, "glfwCocoaSetWindowLevel");
if (glfwCocoaSetWindowLevel_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwCocoaSetWindowChrome_impl) = dlsym(handle, "glfwCocoaSetWindowChrome");
if (glfwCocoaSetWindowChrome_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwCocoaRegisterMIMETypes_impl) = dlsym(handle, "glfwCocoaRegisterMIMETypes");
if (glfwCocoaRegisterMIMETypes_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwCocoaSetWindowLevel_impl) = dlsym(handle, "glfwCocoaSetWindowLevel");
if (glfwCocoaSetWindowLevel_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwGetPrimarySelectionString_impl) = dlsym(handle, "glfwGetPrimarySelectionString");
if (glfwGetPrimarySelectionString_impl == NULL) dlerror(); // clear error indicator
@ -530,6 +530,9 @@ load_glfw(const char* path) {
*(void **) (&glfwWaylandSetTitlebarHidden_impl) = dlsym(handle, "glfwWaylandSetTitlebarHidden");
if (glfwWaylandSetTitlebarHidden_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwWaylandSetInitialWindowSizeCallback_impl) = dlsym(handle, "glfwWaylandSetInitialWindowSizeCallback");
if (glfwWaylandSetInitialWindowSizeCallback_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwWaylandRedrawCSDWindowTitle_impl) = dlsym(handle, "glfwWaylandRedrawCSDWindowTitle");
if (glfwWaylandRedrawCSDWindowTitle_impl == NULL) dlerror(); // clear error indicator

13
kitty/glfw-wrapper.h generated
View file

@ -1661,6 +1661,7 @@ typedef void (* GLFWjoystickfun)(int,int);
typedef void (* GLFWuserdatafun)(unsigned long long, void*);
typedef void (* GLFWtickcallback)(void*);
typedef void (* GLFWactivationcallback)(GLFWwindow *window, const char *token, void *data);
typedef void (* GLFWwaylandinitialsizefun)(GLFWwindow *window, float xscale, float yscale, int *width, int *height);
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin, bool is_single_glyph);
typedef char* (* GLFWcurrentselectionfun)(void);
typedef bool (* GLFWhascurrentselectionfun)(void);
@ -2479,10 +2480,6 @@ typedef void (*glfwCocoaCycleThroughOSWindows_func)(bool);
GFW_EXTERN glfwCocoaCycleThroughOSWindows_func glfwCocoaCycleThroughOSWindows_impl;
#define glfwCocoaCycleThroughOSWindows glfwCocoaCycleThroughOSWindows_impl
typedef void (*glfwCocoaSetWindowLevel_func)(GLFWwindow*, const char*);
GFW_EXTERN glfwCocoaSetWindowLevel_func glfwCocoaSetWindowLevel_impl;
#define glfwCocoaSetWindowLevel glfwCocoaSetWindowLevel_impl
typedef void (*glfwCocoaSetWindowChrome_func)(GLFWwindow*, unsigned int, bool, unsigned int, int, unsigned int, bool, int, float, bool);
GFW_EXTERN glfwCocoaSetWindowChrome_func glfwCocoaSetWindowChrome_impl;
#define glfwCocoaSetWindowChrome glfwCocoaSetWindowChrome_impl
@ -2491,6 +2488,10 @@ typedef void (*glfwCocoaRegisterMIMETypes_func)(GLFWwindow*, const char**, size_
GFW_EXTERN glfwCocoaRegisterMIMETypes_func glfwCocoaRegisterMIMETypes_impl;
#define glfwCocoaRegisterMIMETypes glfwCocoaRegisterMIMETypes_impl
typedef void (*glfwCocoaSetWindowLevel_func)(GLFWwindow*, const char*);
GFW_EXTERN glfwCocoaSetWindowLevel_func glfwCocoaSetWindowLevel_impl;
#define glfwCocoaSetWindowLevel glfwCocoaSetWindowLevel_impl
typedef const char* (*glfwGetPrimarySelectionString_func)(GLFWwindow*);
GFW_EXTERN glfwGetPrimarySelectionString_func glfwGetPrimarySelectionString_impl;
#define glfwGetPrimarySelectionString glfwGetPrimarySelectionString_impl
@ -2523,6 +2524,10 @@ typedef void (*glfwWaylandSetTitlebarHidden_func)(GLFWwindow*, bool);
GFW_EXTERN glfwWaylandSetTitlebarHidden_func glfwWaylandSetTitlebarHidden_impl;
#define glfwWaylandSetTitlebarHidden glfwWaylandSetTitlebarHidden_impl
typedef void (*glfwWaylandSetInitialWindowSizeCallback_func)(GLFWwaylandinitialsizefun);
GFW_EXTERN glfwWaylandSetInitialWindowSizeCallback_func glfwWaylandSetInitialWindowSizeCallback_impl;
#define glfwWaylandSetInitialWindowSizeCallback glfwWaylandSetInitialWindowSizeCallback_impl
typedef void (*glfwWaylandRedrawCSDWindowTitle_func)(GLFWwindow*);
GFW_EXTERN glfwWaylandRedrawCSDWindowTitle_func glfwWaylandRedrawCSDWindowTitle_impl;
#define glfwWaylandRedrawCSDWindowTitle glfwWaylandRedrawCSDWindowTitle_impl

View file

@ -1705,6 +1705,32 @@ os_window_update_size_increments(OSWindow *window) {
}
// Borrowed reference to the get_window_size callable, valid only for the
// duration of a create_os_window() call. Used by the Wayland initial-size hook
// below, which fires synchronously from inside glfwCreateWindow().
static PyObject *initial_window_size_py_callback = NULL;
static void
wayland_initial_size_callback(GLFWwindow *window UNUSED, float xscale, float yscale, int *width, int *height) {
// The compositor has told us the real scale before the window is mapped;
// recompute the cell-based logical size for that scale so the window is
// mapped at the correct size in the first place.
if (!initial_window_size_py_callback) return;
double xdpi, ydpi;
dpi_from_scale(xscale, yscale, &xdpi, &ydpi);
FONTS_DATA_HANDLE fonts_data = load_fonts_data(OPT(font_size), xdpi, ydpi);
if (!fonts_data) return;
PyObject *ret = PyObject_CallFunction(initial_window_size_py_callback, "IIddff",
fonts_data->fcm.cell_width, fonts_data->fcm.cell_height,
fonts_data->logical_dpi_x, fonts_data->logical_dpi_y, xscale, yscale);
if (ret) {
int w = PyLong_AsLong(PyTuple_GET_ITEM(ret, 0)), h = PyLong_AsLong(PyTuple_GET_ITEM(ret, 1));
if (!PyErr_Occurred() && w > 0 && h > 0) { *width = w; *height = h; }
else PyErr_Clear();
Py_DECREF(ret);
} else PyErr_Clear();
}
static PyObject*
create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
int x = INT_MIN, y = INT_MIN, window_state = WINDOW_NORMAL, disallow_override_title = 0;
@ -1822,7 +1848,16 @@ create_os_window(PyObject UNUSED *self, PyObject *args, PyObject *kw) {
if (!layer_shell_config_from_python(layer_shell_config, lsc)) return NULL;
lsc->expected.xscale = xscale; lsc->expected.yscale = yscale;
}
// On Wayland the true (fractional) scale is only known after the surface
// exists. Register a hook so the window is mapped at the correct cell-based
// size once the compositor reveals the scale, rather than being resized
// afterwards (which loses to the compositor's authoritative configure).
if (global_state.is_wayland && glfwWaylandSetInitialWindowSizeCallback) {
glfwWaylandSetInitialWindowSizeCallback(wayland_initial_size_callback);
initial_window_size_py_callback = get_window_size;
}
GLFWwindow *glfw_window = glfwCreateWindow(width, height, title, NULL, temp_window ? temp_window : common_context, lsc);
initial_window_size_py_callback = NULL;
if (temp_window) { glfwDestroyWindow(temp_window); temp_window = NULL; }
if (glfw_window == NULL) glfw_failure;
#undef glfw_failure