apprt/gtk: add support for _NET_WM_STATE

This commit is contained in:
Adam Wolf
2025-01-09 22:30:58 -06:00
parent 6ef757a8f8
commit 8bc257b476
2 changed files with 72 additions and 0 deletions

View File

@ -856,6 +856,37 @@ pub fn setInitialWindowSize(self: *const Surface, width: u32, height: u32) !void
@intCast(width), @intCast(width),
@intCast(height), @intCast(height),
); );
// TODO: move this check somewhere else so we can reuse it
// If the current WM doesn't support _NET_WM_STATE, we shouldn't try to set it.
const gdk_surface = c.gtk_native_get_surface(@ptrCast(window.window));
if (c.g_type_check_instance_is_a(@ptrCast(@alignCast(gdk_surface)), c.gdk_x11_surface_get_type()) == 0) return; // X11 only, sorry Wayland
if (c.XInternAtom(c.gdk_x11_display_get_xdisplay(c.gdk_surface_get_display(gdk_surface)), "_NET_WM_STATE", 1) == 0) {
log.warn("current WM does not support _NET_WM_STATE", .{});
return;
}
const workarea = self.getWorkarea(gdk_surface) orelse return;
if (height >= workarea.height and width >= workarea.width) {
c.gtk_window_maximize(@ptrCast(window.window));
}
}
fn getWorkarea(self: *const Surface, gdk_surface: ?*c.GdkSurface) ?c.GdkRectangle {
const monitor = c.gdk_display_get_monitor_at_surface(
c.gdk_display_get_default(),
gdk_surface,
);
var workarea: c.GdkRectangle = std.mem.zeroes(c.GdkRectangle);
if (self.app.wayland != null) {
c.gdk_monitor_get_geometry(monitor, &workarea);
} else {
c.gdk_x11_monitor_get_workarea(monitor, &workarea);
}
return workarea;
} }
pub fn setSizeLimits(self: *const Surface, min: apprt.SurfaceSize, max_: ?apprt.SurfaceSize) !void { pub fn setSizeLimits(self: *const Surface, min: apprt.SurfaceSize, max_: ?apprt.SurfaceSize) !void {

View File

@ -211,6 +211,7 @@ pub fn init(self: *Window, app: *App) !void {
} }
_ = c.g_signal_connect_data(gtk_window, "notify::decorated", c.G_CALLBACK(&gtkWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT); _ = c.g_signal_connect_data(gtk_window, "notify::decorated", c.G_CALLBACK(&gtkWindowNotifyDecorated), self, null, c.G_CONNECT_DEFAULT);
_ = c.g_signal_connect_data(gtk_window, "notify::maximized", c.G_CALLBACK(&gtkWindowNotifyMaximized), self, null, c.G_CONNECT_DEFAULT);
// If we are disabling decorations then disable them right away. // If we are disabling decorations then disable them right away.
if (!app.config.@"window-decoration") { if (!app.config.@"window-decoration") {
@ -613,6 +614,46 @@ fn gtkWindowNotifyDecorated(
} }
} }
/// Handle setting or removing the _NET_WM_STATE property on the window.
/// This is used to notify the window manager if the window is maximized.
/// Though, this depends on the window manager supporting _NET_WM_STATE.
fn gtkWindowNotifyMaximized(
window: *c.GtkWindow,
_: *c.GParamSpec,
_: ?*anyopaque,
) callconv(.C) void {
const gdk_surface = c.gtk_native_get_surface(@ptrCast(window));
if (c.g_type_check_instance_is_a(@ptrCast(@alignCast(gdk_surface)), c.gdk_x11_surface_get_type()) == 0) return; // X11 only, sorry Wayland
const xdisplay = c.gdk_x11_display_get_xdisplay(c.gdk_surface_get_display(gdk_surface)) orelse return;
// If the WM doesn't support _NET_WM_STATE, no need to continue.
if (c.XInternAtom(xdisplay, "_NET_WM_STATE", 1) == 0) {
log.warn("current WM does not support _NET_WM_STATE", .{});
return;
}
// https://tronche.com/gui/x/xlib/events/client-communication/client-message.html#XClientMessageEvent
var client_message_event: c.XClientMessageEvent = std.mem.zeroes(c.XClientMessageEvent);
client_message_event.type = c.ClientMessage;
client_message_event.window = c.gdk_x11_surface_get_xid(gdk_surface);
client_message_event.message_type = c.XInternAtom(xdisplay, "_NET_WM_STATE", 0);
client_message_event.format = 32;
client_message_event.data.l[0] = c.gtk_window_is_maximized(window);
client_message_event.data.l[1] = @intCast(c.XInternAtom(xdisplay, "_NET_WM_STATE_MAXIMIZED_VERT", 0));
// https://tronche.com/gui/x/xlib/event-handling/XSendEvent.html
_ = c.XSendEvent(
xdisplay,
c.DefaultRootWindow(xdisplay),
0,
c.SubstructureRedirectMask | c.SubstructureNotifyMask,
@ptrCast(&client_message_event),
);
_ = c.XFlush(xdisplay);
}
// Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab // Note: we MUST NOT use the GtkButton parameter because gtkActionNewTab
// sends an undefined value. // sends an undefined value.
fn gtkTabNewClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void { fn gtkTabNewClick(_: *c.GtkButton, ud: ?*anyopaque) callconv(.C) void {