Compare commits

...

15 Commits
col ... decor

Author SHA1 Message Date
Kovid Goyal
139f56b2eb Port the code to set window size limits to wl_decor 2024-04-03 18:55:27 +05:30
Kovid Goyal
84ef1dce92 Migrate setting of title and app id to wl_decor 2024-04-03 18:55:27 +05:30
Kovid Goyal
14c0d76f86 Port set minimized to wl_decor 2024-04-03 18:55:27 +05:30
Kovid Goyal
fbc2b4fc1c Migrate maximized state management to wl_decor 2024-04-03 18:55:27 +05:30
Kovid Goyal
d557d4cbec Migrate setting of fullscreen to wl_decor 2024-04-03 18:55:27 +05:30
Kovid Goyal
16b5948c95 ... 2024-04-03 18:55:26 +05:30
Kovid Goyal
4e9e833397 Only initialize edge_spacing_func if glfw init succeeds 2024-04-03 18:55:26 +05:30
Kovid Goyal
1f15002ace Set GDK_BACKEND if needed before loading libdecor 2024-04-03 18:55:26 +05:30
Kovid Goyal
886ca2d0e6 Store library handle in funcs struct 2024-04-03 18:55:26 +05:30
Kovid Goyal
45f3577f35 O_CLOEXEC for linux joystick open 2024-04-03 18:55:26 +05:30
Kovid Goyal
8b66c3faa4 Propagate failures to get video mode 2024-04-03 18:55:26 +05:30
Kovid Goyal
5346c95ff4 Integrate libdecor into event loop 2024-04-03 18:55:26 +05:30
Kovid Goyal
aecacb0295 Note that file transfer wont work through tmux in the FAQ 2024-04-03 18:55:26 +05:30
Kovid Goyal
34ddcaaa57 Create decoration context 2024-04-03 18:55:26 +05:30
Kovid Goyal
92253521b1 dlopen libdecor 2024-04-03 18:55:26 +05:30
15 changed files with 346 additions and 89 deletions

View File

@@ -465,8 +465,9 @@ for tmux refusing to support images.
If you use any of the advanced features that kitty has innovated, such as If you use any of the advanced features that kitty has innovated, such as
:doc:`styled underlines </underlines>`, :doc:`desktop notifications :doc:`styled underlines </underlines>`, :doc:`desktop notifications
</desktop-notifications>`, :doc:`extended keyboard support </desktop-notifications>`, :doc:`extended keyboard support
</keyboard-protocol>`, etc. they may or may not work, depending on the whims of </keyboard-protocol>`, :doc:`file transfer </kittens/transfer>`, etc.
tmux's maintainer, your version of tmux, etc. they may or may not work, depending on the whims of tmux's maintainer,
your version of tmux, etc.
I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high? I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high?

View File

@@ -618,9 +618,13 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
return result; return result;
} }
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) bool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
{ {
CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID); CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
if (!native) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to query display mode");
return false;
}
*mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate); *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
CGDisplayModeRelease(native); CGDisplayModeRelease(native);
} }

2
glfw/internal.h vendored
View File

@@ -686,7 +686,7 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
float* xscale, float* yscale); float* xscale, float* yscale);
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height);
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); bool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);

View File

@@ -27,6 +27,8 @@
// It is fine to use C99 in this file because it will not be built with VS // It is fine to use C99 in this file because it will not be built with VS
//======================================================================== //========================================================================
#define _POSIX_C_SOURCE 200809L
#include "internal.h" #include "internal.h"
#include <sys/types.h> #include <sys/types.h>
@@ -39,6 +41,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
#ifndef SYN_DROPPED // < v2.6.39 kernel headers #ifndef SYN_DROPPED // < v2.6.39 kernel headers
// Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32 // Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32
@@ -135,7 +138,7 @@ static bool openJoystickDevice(const char* path)
} }
_GLFWjoystickLinux linjs = {0}; _GLFWjoystickLinux linjs = {0};
linjs.fd = open(path, O_RDONLY | O_NONBLOCK); linjs.fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (linjs.fd == -1) if (linjs.fd == -1)
return false; return false;

2
glfw/monitor.c vendored
View File

@@ -445,7 +445,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle)
_GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL);
_glfwPlatformGetVideoMode(monitor, &monitor->currentMode); if (!_glfwPlatformGetVideoMode(monitor, &monitor->currentMode)) return NULL;
return &monitor->currentMode; return &monitor->currentMode;
} }

View File

@@ -56,6 +56,7 @@
"posix_thread.h", "posix_thread.h",
"wl_cursors.h", "wl_cursors.h",
"wl_text_input.h", "wl_text_input.h",
"wl_decor.h",
"xkb_glfw.h", "xkb_glfw.h",
"dbus_glfw.h", "dbus_glfw.h",
"ibus_glfw.h", "ibus_glfw.h",
@@ -90,6 +91,7 @@
"wl_monitor.c", "wl_monitor.c",
"wl_window.c", "wl_window.c",
"wl_cursors.c", "wl_cursors.c",
"wl_decor.c",
"wl_text_input.c", "wl_text_input.c",
"wl_client_side_decorations.c", "wl_client_side_decorations.c",
"posix_thread.c", "posix_thread.c",

246
glfw/wl_decor.c vendored Normal file
View File

@@ -0,0 +1,246 @@
/*
* wl_decor.c
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#define _POSIX_C_SOURCE 200809L
#include "wl_decor.h"
#include "wl_client_side_decorations.h"
#include "libdecor-0/libdecor.h"
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
// Boilerplate to dynload libdecor {{{
#define funcs F(libdecor_new); F(libdecor_unref); F(libdecor_get_fd); F(libdecor_dispatch); F(libdecor_decorate); F(libdecor_frame_unref); \
F(libdecor_frame_set_app_id); F(libdecor_frame_set_title); F(libdecor_frame_set_minimized); F(libdecor_frame_set_fullscreen); \
F(libdecor_frame_unset_fullscreen); F(libdecor_frame_map); F(libdecor_frame_commit); F(libdecor_frame_set_min_content_size); \
F(libdecor_frame_set_max_content_size); F(libdecor_frame_set_maximized); F(libdecor_frame_unset_maximized); \
F(libdecor_frame_set_capabilities); F(libdecor_frame_unset_capabilities); F(libdecor_frame_set_visibility); \
F(libdecor_frame_get_xdg_toplevel); F(libdecor_configuration_get_content_size); F(libdecor_configuration_get_window_state); \
F(libdecor_state_new); F(libdecor_state_free);
#define F(name) __typeof__(name) (*name)
static struct {
void* libdecor_handle;
funcs
} libdecor_funcs = {0};
#undef F
#define LOAD_FUNC(handle, name) { \
glfw_dlsym(libdecor_funcs.name, handle, #name); \
if (!libdecor_funcs.name) { \
const char* error = dlerror(); \
_glfwInputError(GLFW_PLATFORM_ERROR, "failed to load libdecor function %s with error: %s", #name, error ? error : "(null)"); \
dlclose(handle); handle = NULL; return false; \
memset(&libdecor_funcs, 0, sizeof(libdecor_funcs)); \
} \
}
static bool
glfw_wl_load_libdecor(void) {
if (libdecor_funcs.libdecor_handle != NULL) return true;
const char* libnames[] = {
#ifdef _GLFW_DECOR_LIBRARY
_GLFW_DECOR_LIBRARY,
#else
"libdecor-0.so",
// some installs are missing the .so symlink, so try the full name
"libdecor-0.so.0",
#endif
NULL
};
for (int i = 0; libnames[i]; i++) {
libdecor_funcs.libdecor_handle = _glfw_dlopen(libnames[i]);
if (libdecor_funcs.libdecor_handle) break;
}
if (!libdecor_funcs.libdecor_handle) {
libdecor_funcs.libdecor_handle = _glfw_dlopen(libnames[0]);
if (!libdecor_funcs.libdecor_handle) {
_glfwInputError(GLFW_PLATFORM_ERROR, "failed to dlopen %s with error: %s", libnames[0], dlerror());
return false;
}
}
dlerror(); /* Clear any existing error */
#define F(name) LOAD_FUNC(libdecor_funcs.libdecor_handle, name)
funcs
#undef F
return true;
}
#define libdecor_new libdecor_funcs.libdecor_new
#define libdecor_unref libdecor_funcs.libdecor_unref
#define libdecor_get_fd libdecor_funcs.libdecor_get_fd
#define libdecor_dispatch libdecor_funcs.libdecor_dispatch
#define libdecor_decorate libdecor_funcs.libdecor_decorate
#define libdecor_frame_unref libdecor_funcs.libdecor_frame_unref
#define libdecor_frame_set_app_id libdecor_funcs.libdecor_frame_set_app_id
#define libdecor_frame_set_title libdecor_funcs.libdecor_frame_set_title
#define libdecor_frame_set_minimized libdecor_funcs.libdecor_frame_set_minimized
#define libdecor_frame_set_fullscreen libdecor_funcs.libdecor_frame_set_fullscreen
#define libdecor_frame_unset_fullscreen libdecor_funcs.libdecor_frame_unset_fullscreen
#define libdecor_frame_map libdecor_funcs.libdecor_frame_map
#define libdecor_frame_commit libdecor_funcs.libdecor_frame_commit
#define libdecor_frame_set_min_content_size libdecor_funcs.libdecor_frame_set_min_content_size
#define libdecor_frame_set_max_content_size libdecor_funcs.libdecor_frame_set_max_content_size
#define libdecor_frame_set_maximized libdecor_funcs.libdecor_frame_set_maximized
#define libdecor_frame_unset_maximized libdecor_funcs.libdecor_frame_unset_maximized
#define libdecor_frame_set_capabilities libdecor_funcs.libdecor_frame_set_capabilities
#define lilibdecor_frame_set_capabilities libdecor_funcs.lilibdecor_frame_set_capabilities
#define libdecor_frame_set_visibility libdecor_funcs.libdecor_frame_set_visibility
#define libdecor_frame_get_xdg_toplevel libdecor_funcs.libdecor_frame_get_xdg_toplevel
#define libdecor_configuration_get_content_size libdecor_funcs.libdecor_configuration_get_content_size
#define libdecor_configuration_get_window_state libdecor_funcs.libdecor_configuration_get_window_state
#define libdecor_state_new libdecor_funcs.libdecor_state_new
#define libdecor_state_free libdecor_funcs.libdecor_state_free
// }}}
typedef struct DecorLibState {
struct libdecor* libdecor;
} DecorLibState;
void handle_libdecor_error(struct libdecor* context UNUSED, enum libdecor_error error, const char* message) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: libdecor error %u: %s", error, message);
}
static struct libdecor_interface libdecor_interface = {
.error = handle_libdecor_error
};
static DECOR_LIB_HANDLE
glfw_wl_load_decorations_library_(struct wl_display *display) {
if (!glfw_wl_load_libdecor()) return NULL;
DecorLibState *ans = calloc(1, sizeof(DecorLibState));
if (!ans) { _glfwInputError(GLFW_PLATFORM_ERROR, "Out of memory"); return NULL; }
ans->libdecor = libdecor_new(display, &libdecor_interface);
if (!ans->libdecor) _glfwInputError(GLFW_PLATFORM_ERROR, "libdecor_new() returned NULL");
return (DECOR_LIB_HANDLE) ans;
}
DECOR_LIB_HANDLE
glfw_wl_load_decorations_library(struct wl_display *display) {
// See https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/65 for why we need this nautanki with GDK_BACKEND
char *gdk_backend = getenv("GDK_BACKEND");
if (gdk_backend && strcmp(gdk_backend, "wayland") != 0) {
gdk_backend = strdup(gdk_backend);
setenv("GDK_BACKEND", "wayland", 1);
}
DECOR_LIB_HANDLE ans = glfw_wl_load_decorations_library_(display);
if (gdk_backend) {
setenv("GDK_BACKEND", gdk_backend, 1);
free(gdk_backend);
}
return ans;
}
void
glfw_wl_unload_decorations_library(DECOR_LIB_HANDLE h_) {
if (h_) {
DecorLibState *h = (DecorLibState*)h_;
if (h->libdecor) { libdecor_unref(h->libdecor); }
free(h);
}
if (libdecor_funcs.libdecor_handle) {
dlclose(libdecor_funcs.libdecor_handle); libdecor_funcs.libdecor_handle = NULL;
memset(&libdecor_funcs, 0, sizeof(libdecor_funcs));
}
}
int
glfw_wl_dispatch_decor_events(void) {
// TODO: change this to just call while (g_main_context_iteration(NULL, FALSE)); when using the gtk plugin
// will require a patch to libdecor. The libdecor API currently has no way to either tell what plugin
// is being used or to just dispatch non-Wayland events.
// https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/70
return libdecor_dispatch(((DecorLibState*)_glfw.wl.decor)->libdecor, 0);
}
typedef struct Frame {
struct libdecor_frame *libdecor;
} Frame;
void
glfw_wl_set_fullscreen(_GLFWwindow *w, bool on, struct wl_output *monitor) {
Frame *d = (Frame*)w->wl.frame;
if (d && d->libdecor) {
if (on) libdecor_frame_set_fullscreen(d->libdecor, monitor);
else libdecor_frame_unset_fullscreen(d->libdecor);
} else if (w->wl.xdg.toplevel) {
if (on) {
xdg_toplevel_set_fullscreen(w->wl.xdg.toplevel, monitor);
if (!w->wl.decorations.serverSide) free_csd_surfaces(w);
} else {
xdg_toplevel_unset_fullscreen(w->wl.xdg.toplevel);
ensure_csd_resources(w);
}
}
}
void
glfw_wl_set_maximized(_GLFWwindow *w, bool on) {
Frame *d = (Frame*)w->wl.frame;
if (d && d->libdecor) {
if (on) libdecor_frame_set_maximized(d->libdecor);
else libdecor_frame_unset_maximized(d->libdecor);
} else if (w->wl.xdg.toplevel) {
if (on) xdg_toplevel_set_maximized(w->wl.xdg.toplevel);
else xdg_toplevel_unset_maximized(w->wl.xdg.toplevel);
}
}
void
glfw_wl_set_minimized(_GLFWwindow *w) {
Frame *d = (Frame*)w->wl.frame;
if (d && d->libdecor) libdecor_frame_set_minimized(d->libdecor);
else if (w->wl.xdg.toplevel) xdg_toplevel_set_minimized(w->wl.xdg.toplevel);
}
void
glfw_wl_set_title(_GLFWwindow *w, const char *title) {
// Wayland cannot handle requests larger than ~8200 bytes. Sending
// one causes an abort(). Since titles this large are meaningless anyway
// ensure they do not happen.
if (!title) title = "";
char *safe_title = utf_8_strndup(title, 2048);
if (!safe_title) return;
if (w->wl.title && strcmp(w->wl.title, safe_title) == 0) { free(safe_title); return; }
free(w->wl.title); w->wl.title = safe_title;
Frame *d = (Frame*)w->wl.frame;
if (d && d->libdecor) libdecor_frame_set_title(d->libdecor, w->wl.title);
else if (w->wl.xdg.toplevel) {
xdg_toplevel_set_title(w->wl.xdg.toplevel, w->wl.title);
change_csd_title(w);
}
}
void
glfw_wl_set_app_id(_GLFWwindow *w, const char *appid) {
if (!appid || !appid[0]) return;
Frame *d = (Frame*)w->wl.frame;
if (d && d->libdecor) libdecor_frame_set_app_id(d->libdecor, appid);
else if (w->wl.xdg.toplevel) xdg_toplevel_set_app_id(w->wl.xdg.toplevel, appid);
}
void
glfw_wl_set_size_limits(_GLFWwindow *w, int minwidth, int minheight, int maxwidth, int maxheight) {
if (w->wl.xdg.toplevel) {
minwidth = minwidth == GLFW_DONT_CARE ? 0 : minwidth;
minheight = minheight == GLFW_DONT_CARE ? 0 : minheight;
maxwidth = maxwidth == GLFW_DONT_CARE ? 0 : maxwidth;
maxheight = maxheight == GLFW_DONT_CARE ? 0 : maxheight;
Frame *d = (Frame*)w->wl.frame;
if (d && d->libdecor) {
libdecor_frame_set_min_content_size(d->libdecor, minwidth, minheight);
libdecor_frame_set_max_content_size(d->libdecor, maxwidth, maxheight);
} else {
xdg_toplevel_set_min_size(w->wl.xdg.toplevel, minwidth, minheight);
xdg_toplevel_set_max_size(w->wl.xdg.toplevel, maxwidth, maxheight);
}
}
}

22
glfw/wl_decor.h vendored Normal file
View File

@@ -0,0 +1,22 @@
/*
* wl_decor.h
* Copyright (C) 2024 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include <wayland-client.h>
#include <stdbool.h>
#include "internal.h"
DECOR_LIB_HANDLE glfw_wl_load_decorations_library(struct wl_display*);
void glfw_wl_unload_decorations_library(DECOR_LIB_HANDLE);
int glfw_wl_dispatch_decor_events(void);
void glfw_wl_set_fullscreen(_GLFWwindow *w, bool on, struct wl_output *monitor);
void glfw_wl_set_maximized(_GLFWwindow *w, bool on);
void glfw_wl_set_minimized(_GLFWwindow *w);
void glfw_wl_set_title(_GLFWwindow *w, const char *title);
void glfw_wl_set_app_id(_GLFWwindow *w, const char *appid);
void glfw_wl_set_size_limits(_GLFWwindow *w, int minwidth, int minheight, int maxwidth, int maxheight);

8
glfw/wl_init.c vendored
View File

@@ -28,6 +28,7 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include "internal.h" #include "internal.h"
#include "wl_decor.h"
#include "backend_utils.h" #include "backend_utils.h"
#include "linux_desktop_settings.h" #include "linux_desktop_settings.h"
#include "../kitty/monotonic.h" #include "../kitty/monotonic.h"
@@ -254,10 +255,7 @@ static void pointerHandleButton(void* data UNUSED,
window->wl.decorations.last_click_on_top_decoration_at = monotonic(); window->wl.decorations.last_click_on_top_decoration_at = monotonic();
if (window->wl.decorations.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) { if (window->wl.decorations.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) {
window->wl.decorations.last_click_on_top_decoration_at = 0; window->wl.decorations.last_click_on_top_decoration_at = 0;
if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) glfw_wl_set_maximized(window, !_glfwPlatformWindowMaximized(window));
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
else
xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
return; return;
} }
} }
@@ -883,6 +881,7 @@ int _glfwPlatformInit(void)
wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL); wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);
if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return false; if (!glfw_xkb_create_context(&_glfw.wl.xkb)) return false;
if (!(_glfw.wl.decor = glfw_wl_load_decorations_library(_glfw.wl.display))) return false;
// Sync so we got all registry objects // Sync so we got all registry objects
wl_display_roundtrip(_glfw.wl.display); wl_display_roundtrip(_glfw.wl.display);
@@ -925,6 +924,7 @@ int _glfwPlatformInit(void)
void _glfwPlatformTerminate(void) void _glfwPlatformTerminate(void)
{ {
glfw_wl_unload_decorations_library(_glfw.wl.decor);
if (_glfw.wl.activation_requests.array) { if (_glfw.wl.activation_requests.array) {
for (size_t i=0; i < _glfw.wl.activation_requests.sz; i++) { for (size_t i=0; i < _glfw.wl.activation_requests.sz; i++) {
glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + i; glfw_wl_xdg_activation_request *r = _glfw.wl.activation_requests.array + i;

8
glfw/wl_monitor.c vendored
View File

@@ -195,9 +195,13 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found)
return monitor->modes; return monitor->modes;
} }
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) bool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
{ {
*mode = monitor->modes[monitor->wl.currentMode]; if (monitor->modeCount > monitor->wl.currentMode) {
*mode = monitor->modes[monitor->wl.currentMode];
return true;
}
return false;
} }
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED) bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED)

5
glfw/wl_platform.h vendored
View File

@@ -117,6 +117,9 @@ typedef struct _GLFWWaylandCSDEdge {
int x, y; int x, y;
} _GLFWWaylandCSDEdge; } _GLFWWaylandCSDEdge;
typedef struct {int x;} *DECOR_LIB_HANDLE;
typedef struct {int x;} *DECOR_FRAME_HANDLE;
typedef enum WaylandWindowState { typedef enum WaylandWindowState {
TOPLEVEL_STATE_NONE = 0, TOPLEVEL_STATE_NONE = 0,
@@ -254,6 +257,7 @@ typedef struct _GLFWwindowWayland
uint32_t toplevel_states; uint32_t toplevel_states;
uint32_t decoration_mode; uint32_t decoration_mode;
} current, pending; } current, pending;
DECOR_FRAME_HANDLE frame;
} _GLFWwindowWayland; } _GLFWwindowWayland;
typedef enum _GLFWWaylandOfferType typedef enum _GLFWWaylandOfferType
@@ -357,6 +361,7 @@ typedef struct _GLFWlibraryWayland
size_t dataOffersCounter; size_t dataOffersCounter;
_GLFWWaylandDataOffer dataOffers[8]; _GLFWWaylandDataOffer dataOffers[8];
bool has_preferred_buffer_scale; bool has_preferred_buffer_scale;
DECOR_LIB_HANDLE decor;
} _GLFWlibraryWayland; } _GLFWlibraryWayland;
// Wayland-specific per-monitor data // Wayland-specific per-monitor data

99
glfw/wl_window.c vendored
View File

@@ -28,7 +28,7 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include "internal.h" #include "wl_decor.h"
#include "backend_utils.h" #include "backend_utils.h"
#include "linux_notify.h" #include "linux_notify.h"
#include "wl_client_side_decorations.h" #include "wl_client_side_decorations.h"
@@ -597,20 +597,8 @@ static bool createSurface(_GLFWwindow* window,
return true; return true;
} }
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on) static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on) {
{ glfw_wl_set_fullscreen(window, on, monitor ? monitor->wl.output : NULL);
if (window->wl.xdg.toplevel)
{
if (on) {
xdg_toplevel_set_fullscreen(
window->wl.xdg.toplevel,
monitor ? monitor->wl.output : NULL);
if (!window->wl.decorations.serverSide) free_csd_surfaces(window);
} else {
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
ensure_csd_resources(window);
}
}
} }
@@ -1022,33 +1010,20 @@ create_window_desktop_surface(_GLFWwindow* window)
&xdgToplevelListener, &xdgToplevelListener,
window); window);
if (window->wl.title) glfw_wl_set_size_limits(window, window->minwidth, window->minheight, window->maxwidth, window->maxheight);
xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE)
xdg_toplevel_set_min_size(window->wl.xdg.toplevel,
window->minwidth, window->minheight);
if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE)
xdg_toplevel_set_max_size(window->wl.xdg.toplevel,
window->maxwidth, window->maxheight);
if (window->monitor) if (window->monitor)
{ {
xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, glfw_wl_set_fullscreen(window, true, window->monitor->wl.output);
window->monitor->wl.output);
} }
else if (window->wl.maximize_on_first_show) else if (window->wl.maximize_on_first_show)
{ {
window->wl.maximize_on_first_show = false; window->wl.maximize_on_first_show = false;
xdg_toplevel_set_maximized(window->wl.xdg.toplevel); glfw_wl_set_maximized(window, true);
setXdgDecorations(window);
} }
else if (!window->monitor) setXdgDecorations(window);
{ glfw_wl_set_app_id(window, window->wl.appId);
setXdgDecorations(window); if (window->wl.title)
} glfw_wl_set_title(window, window->wl.title);
if (strlen(window->wl.appId))
xdg_toplevel_set_app_id(window->wl.xdg.toplevel, window->wl.appId);
wl_surface_commit(window->wl.surface); wl_surface_commit(window->wl.surface);
wl_display_roundtrip(_glfw.wl.display); wl_display_roundtrip(_glfw.wl.display);
@@ -1104,8 +1079,8 @@ wayland_read_events(int poll_result, int events, void *data UNUSED) {
else wl_display_cancel_read(_glfw.wl.display); else wl_display_cancel_read(_glfw.wl.display);
} }
static void handleEvents(monotonic_t timeout) static void
{ handleEvents(monotonic_t timeout) {
struct wl_display* display = _glfw.wl.display; struct wl_display* display = _glfw.wl.display;
errno = 0; errno = 0;
EVDBG("starting handleEvents(%.2f)", monotonic_t_to_s_double(timeout)); EVDBG("starting handleEvents(%.2f)", monotonic_t_to_s_double(timeout));
@@ -1141,6 +1116,13 @@ static void handleEvents(monotonic_t timeout)
} }
glfw_ibus_dispatch(&_glfw.wl.xkb.ibus); glfw_ibus_dispatch(&_glfw.wl.xkb.ibus);
glfw_dbus_session_bus_dispatch(); glfw_dbus_session_bus_dispatch();
// technically we should wait on the libdecor fd but
// for both cairo and gtk plugins the fd is just the wayland display fd so
// no need to wait on it
if (_glfw.wl.decor) {
int num = glfw_wl_dispatch_decor_events(); (void)num;
EVDBG("dispatched %d Wayland events", num);
}
EVDBG("other dispatch done"); EVDBG("other dispatch done");
if (_glfw.wl.eventLoopData.wakeup_fd_ready) check_for_wakeup_events(&_glfw.wl.eventLoopData); if (_glfw.wl.eventLoopData.wakeup_fd_ready) check_for_wakeup_events(&_glfw.wl.eventLoopData);
} }
@@ -1333,19 +1315,7 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
wl_callback_destroy(window->wl.frameCallbackData.current_wl_callback); wl_callback_destroy(window->wl.frameCallbackData.current_wl_callback);
} }
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { glfw_wl_set_title(window, title); }
{
if (window->wl.title) {
if (title && strcmp(title, window->wl.title) == 0) return;
free(window->wl.title);
} else if (!title) return;
// Wayland cannot handle requests larger than ~8200 bytes. Sending
// one causes an abort(). Since titles this large are meaningless anyway
// ensure they do not happen.
window->wl.title = utf_8_strndup(title, 2048);
if (window->wl.xdg.toplevel) xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
change_csd_title(window);
}
void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED, void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED,
int count UNUSED, const GLFWimage* images UNUSED) int count UNUSED, const GLFWimage* images UNUSED)
@@ -1397,20 +1367,9 @@ void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
} }
} }
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) {
int minwidth, int minheight, glfw_wl_set_size_limits(window, minwidth, minheight, maxwidth, maxheight);
int maxwidth, int maxheight) commit_window_surface_if_safe(window);
{
if (window->wl.xdg.toplevel)
{
if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE)
minwidth = minheight = 0;
if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)
maxwidth = maxheight = 0;
xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight);
xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight);
commit_window_surface_if_safe(window);
}
} }
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED, void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED,
@@ -1472,20 +1431,16 @@ monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
return ms_to_monotonic_t(500ll); return ms_to_monotonic_t(500ll);
} }
void _glfwPlatformIconifyWindow(_GLFWwindow* window) void _glfwPlatformIconifyWindow(_GLFWwindow* window) { glfw_wl_set_minimized(window); }
{
if (window->wl.xdg.toplevel)
xdg_toplevel_set_minimized(window->wl.xdg.toplevel);
}
void _glfwPlatformRestoreWindow(_GLFWwindow* window) void _glfwPlatformRestoreWindow(_GLFWwindow* window)
{ {
if (window->wl.xdg.toplevel) if (window->wl.xdg.toplevel)
{ {
if (window->monitor) if (window->monitor)
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); glfw_wl_set_fullscreen(window, false, window->monitor->wl.output);
if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED) if (window->wl.current.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); glfw_wl_set_maximized(window, false);
// There is no way to unset minimized, or even to know if we are // There is no way to unset minimized, or even to know if we are
// minimized, so there is nothing to do in this case. // minimized, so there is nothing to do in this case.
} }
@@ -1496,7 +1451,7 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
{ {
if (window->wl.xdg.toplevel) if (window->wl.xdg.toplevel)
{ {
xdg_toplevel_set_maximized(window->wl.xdg.toplevel); glfw_wl_set_maximized(window, true);
} }
} }

10
glfw/x11_monitor.c vendored
View File

@@ -494,8 +494,8 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
return result; return result;
} }
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) bool _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) {
{ bool ok = false;
if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken)
{ {
XRRScreenResources* sr = XRRScreenResources* sr =
@@ -505,8 +505,10 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
if (ci) if (ci)
{ {
const XRRModeInfo* mi = getModeInfo(sr, ci->mode); const XRRModeInfo* mi = getModeInfo(sr, ci->mode);
if (mi) // mi can be NULL if the monitor has been disconnected if (mi) { // mi can be NULL if the monitor has been disconnected
*mode = vidmodeFromModeInfo(mi, ci); *mode = vidmodeFromModeInfo(mi, ci);
ok = true;
}
XRRFreeCrtcInfo(ci); XRRFreeCrtcInfo(ci);
} }
@@ -515,6 +517,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
} }
else else
{ {
ok = true;
mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen);
mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen);
mode->refreshRate = 0; mode->refreshRate = 0;
@@ -522,6 +525,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
_glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen),
&mode->redBits, &mode->greenBits, &mode->blueBits); &mode->redBits, &mode->greenBits, &mode->blueBits);
} }
return ok;
} }
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)

View File

@@ -1462,8 +1462,8 @@ glfw_init(PyObject UNUSED *self, PyObject *args) {
glfwSetDrawTextFunction(draw_text_callback); glfwSetDrawTextFunction(draw_text_callback);
#endif #endif
get_window_dpi(NULL, &global_state.default_dpi.x, &global_state.default_dpi.y); get_window_dpi(NULL, &global_state.default_dpi.x, &global_state.default_dpi.y);
edge_spacing_func = edge_sf; Py_INCREF(edge_spacing_func);
} }
edge_spacing_func = edge_sf; Py_INCREF(edge_spacing_func);
Py_INCREF(ans); Py_INCREF(ans);
return ans; return ans;
} }
@@ -1823,6 +1823,7 @@ static PyObject*
primary_monitor_size(PYNOARG) { primary_monitor_size(PYNOARG) {
GLFWmonitor* monitor = glfwGetPrimaryMonitor(); GLFWmonitor* monitor = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(monitor); const GLFWvidmode* mode = glfwGetVideoMode(monitor);
if (mode == NULL) { PyErr_SetString(PyExc_ValueError, "Failed to get video mode for primary monitor"); return NULL; }
return Py_BuildValue("ii", mode->width, mode->height); return Py_BuildValue("ii", mode->width, mode->height);
} }

View File

@@ -198,6 +198,7 @@ class Options:
startup_notification_library: Optional[str] = os.getenv('KITTY_STARTUP_NOTIFICATION_LIBRARY') startup_notification_library: Optional[str] = os.getenv('KITTY_STARTUP_NOTIFICATION_LIBRARY')
canberra_library: Optional[str] = os.getenv('KITTY_CANBERRA_LIBRARY') canberra_library: Optional[str] = os.getenv('KITTY_CANBERRA_LIBRARY')
fontconfig_library: Optional[str] = os.getenv('KITTY_FONTCONFIG_LIBRARY') fontconfig_library: Optional[str] = os.getenv('KITTY_FONTCONFIG_LIBRARY')
decor_library: Optional[str] = os.getenv('KITTY_DECOR_LIBRARY')
building_arch: str = '' building_arch: str = ''
# Extras # Extras
@@ -451,6 +452,7 @@ def init_env(
startup_notification_library: Optional[str] = None, startup_notification_library: Optional[str] = None,
canberra_library: Optional[str] = None, canberra_library: Optional[str] = None,
fontconfig_library: Optional[str] = None, fontconfig_library: Optional[str] = None,
decor_library: Optional[str] = None,
extra_logging: Iterable[str] = (), extra_logging: Iterable[str] = (),
extra_include_dirs: Iterable[str] = (), extra_include_dirs: Iterable[str] = (),
ignore_compiler_warnings: bool = False, ignore_compiler_warnings: bool = False,
@@ -535,6 +537,7 @@ def init_env(
library_paths.setdefault(which, []).append(f'{name}="{val}"') library_paths.setdefault(which, []).append(f'{name}="{val}"')
add_lpath('glfw/egl_context.c', '_GLFW_EGL_LIBRARY', egl_library) add_lpath('glfw/egl_context.c', '_GLFW_EGL_LIBRARY', egl_library)
add_lpath('glfw/wl_decor.c', '_GLFW_DECOR_LIBRARY', decor_library)
add_lpath('kitty/desktop.c', '_KITTY_STARTUP_NOTIFICATION_LIBRARY', startup_notification_library) add_lpath('kitty/desktop.c', '_KITTY_STARTUP_NOTIFICATION_LIBRARY', startup_notification_library)
add_lpath('kitty/desktop.c', '_KITTY_CANBERRA_LIBRARY', canberra_library) add_lpath('kitty/desktop.c', '_KITTY_CANBERRA_LIBRARY', canberra_library)
add_lpath('kitty/fontconfig.c', '_KITTY_FONTCONFIG_LIBRARY', fontconfig_library) add_lpath('kitty/fontconfig.c', '_KITTY_FONTCONFIG_LIBRARY', fontconfig_library)
@@ -973,7 +976,7 @@ def init_env_from_args(args: Options, native_optimizations: bool = False) -> Non
global env global env
env = init_env( env = init_env(
args.debug, args.sanitize, native_optimizations, args.link_time_optimization, args.profile, args.debug, args.sanitize, native_optimizations, args.link_time_optimization, args.profile,
args.egl_library, args.startup_notification_library, args.canberra_library, args.fontconfig_library, args.egl_library, args.startup_notification_library, args.canberra_library, args.fontconfig_library, args.decor_library,
args.extra_logging, args.extra_include_dirs, args.ignore_compiler_warnings, args.extra_logging, args.extra_include_dirs, args.ignore_compiler_warnings,
args.building_arch, args.extra_library_dirs, verbose=args.verbose > 0, vcs_rev=args.vcs_rev, args.building_arch, args.extra_library_dirs, verbose=args.verbose > 0, vcs_rev=args.vcs_rev,
) )
@@ -1936,6 +1939,13 @@ def option_parser() -> argparse.ArgumentParser: # {{{
help='The filename argument passed to dlopen for libfontconfig.' help='The filename argument passed to dlopen for libfontconfig.'
' This can be used to change the name of the loaded library or specify an absolute path.' ' This can be used to change the name of the loaded library or specify an absolute path.'
) )
p.add_argument(
'--decor-library',
type=str,
default=Options.decor_library,
help='The filename argument passed to dlopen for libdecor.'
' This can be used to change the name of the loaded library or specify an absolute path.'
)
p.add_argument( p.add_argument(
'--disable-link-time-optimization', '--disable-link-time-optimization',
dest='link_time_optimization', dest='link_time_optimization',