Skip to content

Commit

Permalink
core: add initial fullscreen support for x11
Browse files Browse the repository at this point in the history
  • Loading branch information
joshua-holmes authored and emidoots committed Nov 24, 2024
1 parent 15c63e8 commit f997859
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 8 deletions.
9 changes: 7 additions & 2 deletions src/core/Linux.zig
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,13 @@ pub fn setTitle(linux: *Linux, title: [:0]const u8) void {
}
}

pub fn setDisplayMode(_: *Linux, _: DisplayMode) void {
return;
pub fn setDisplayMode(linux: *Linux, display_mode: DisplayMode) void {
// const old_display_mode = linux.display_mode;
linux.display_mode = display_mode;
switch (linux.backend) {
.wayland => linux.backend.wayland.setDisplayMode(display_mode),
.x11 => linux.backend.x11.setDisplayMode(linux, display_mode),
}
}

pub fn setBorder(_: *Linux, _: bool) void {
Expand Down
6 changes: 6 additions & 0 deletions src/core/linux/Wayland.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const Linux = @import("../Linux.zig");
const Core = @import("../../Core.zig");
const InitOptions = Core.InitOptions;
const KeyEvent = Core.KeyEvent;
const DisplayMode = Core.DisplayMode;
const log = std.log.scoped(.mach);

pub const Wayland = @This();
Expand Down Expand Up @@ -210,6 +211,11 @@ pub fn setTitle(wl: *Wayland, title: [:0]const u8) void {
c.xdg_toplevel_set_title(wl.toplevel, title);
}

pub fn setDisplayMode(wl: *Wayland, display_mode: DisplayMode) void {
_ = wl;
_ = display_mode;
}

const LibXkbCommon = struct {
handle: std.DynLib,

Expand Down
129 changes: 123 additions & 6 deletions src/core/linux/X11.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ empty_event_pipe: [2]std.c.fd_t,
wm_protocols: c.Atom,
wm_delete_window: c.Atom,
net_wm_ping: c.Atom,
net_wm_state_fullscreen: c.Atom,
net_wm_state: c.Atom,
net_wm_state_above: c.Atom,
net_wm_bypass_compositor: c.Atom,
motif_wm_hints: c.Atom,
net_wm_window_type: c.Atom,
Expand Down Expand Up @@ -152,9 +149,6 @@ pub fn init(
.wm_protocols = libx11.XInternAtom(display, "WM_PROTOCOLS", c.False),
.wm_delete_window = libx11.XInternAtom(display, "WM_DELETE_WINDOW", c.False),
.net_wm_ping = libx11.XInternAtom(display, "NET_WM_PING", c.False),
.net_wm_state_fullscreen = libx11.XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", c.False),
.net_wm_state = libx11.XInternAtom(display, "_NET_WM_STATE", c.False),
.net_wm_state_above = libx11.XInternAtom(display, "_NET_WM_STATE_ABOVE", c.False),
.net_wm_window_type = libx11.XInternAtom(display, "_NET_WM_WINDOW_TYPE", c.False),
.net_wm_window_type_dock = libx11.XInternAtom(display, "_NET_WM_WINDOW_TYPE_DOCK", c.False),
.net_wm_bypass_compositor = libx11.XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", c.False),
Expand Down Expand Up @@ -269,6 +263,129 @@ pub fn setTitle(x11: *X11, title: [:0]const u8) void {
_ = x11.libx11.XStoreName(x11.display, x11.window, title);
}

pub fn setDisplayMode(x11: *X11, linux: *Linux, display_mode: DisplayMode) void {
const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False);
const wm_fullscreen = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False);
switch (display_mode) {
.windowed => {
var atoms = std.BoundedArray(c.Atom, 5){};
if (display_mode == .fullscreen) {
atoms.append(wm_fullscreen) catch unreachable;
}
atoms.append(x11.motif_wm_hints) catch unreachable;
// TODO
// if (x11.floating) {
// atoms.append(x11.net_wm_state_above) catch unreachable;
// }
_ = x11.libx11.XChangeProperty(
x11.display,
x11.window,
wm_state,
c.XA_ATOM,
32,
c.PropModeReplace,
@ptrCast(atoms.slice()),
@intCast(atoms.len),
);
x11.setFullscreen(false);
x11.setDecorated(linux.border);
x11.setFloating(false);
_ = x11.libx11.XMapWindow(x11.display, x11.window);
_ = x11.libx11.XFlush(x11.display);
},
.fullscreen => {
x11.setFullscreen(true);
_ = x11.libx11.XFlush(x11.display);
},
.borderless => {
x11.setDecorated(false);
x11.setFloating(true);
x11.setFullscreen(false);
_ = x11.libx11.XResizeWindow(
x11.display,
x11.window,
@intCast(c.DisplayWidth(x11.display, c.DefaultScreen(x11.display))),
@intCast(c.DisplayHeight(x11.display, c.DefaultScreen(x11.display))),
);
_ = x11.libx11.XFlush(x11.display);
},
}
}

fn setFullscreen(x11: *X11, enabled: bool) void {
const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False);
const wm_fullscreen = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_FULLSCREEN", c.False);
x11.sendEventToWM(wm_state, &.{ @intFromBool(enabled), @intCast(wm_fullscreen), 0, 1 });
// Force composition OFF to reduce overhead
const compositing_disable_on: c_long = @intFromBool(enabled);
const bypass_compositor = x11.libx11.XInternAtom(x11.display, "_NET_WM_BYPASS_COMPOSITOR", c.False);
if (bypass_compositor != c.None) {
_ = x11.libx11.XChangeProperty(
x11.display,
x11.window,
bypass_compositor,
c.XA_CARDINAL,
32,
c.PropModeReplace,
@ptrCast(&compositing_disable_on),
1,
);
}
}

fn setFloating(x11: *X11, enabled: bool) void {
const wm_state = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE", c.False);
const wm_above = x11.libx11.XInternAtom(x11.display, "_NET_WM_STATE_ABOVE", c.False);
const net_wm_state_remove = 0;
const net_wm_state_add = 1;
const action: c_long = if (enabled) net_wm_state_add else net_wm_state_remove;
x11.sendEventToWM(wm_state, &.{ action, @intCast(wm_above), 0, 1 });
}

fn sendEventToWM(x11: *X11, message_type: c.Atom, data: []const c_long) void {
var ev = std.mem.zeroes(c.XEvent);
ev.type = c.ClientMessage;
ev.xclient.window = x11.window;
ev.xclient.message_type = message_type;
ev.xclient.format = 32;
@memcpy(ev.xclient.data.l[0..data.len], data);
_ = x11.libx11.XSendEvent(
x11.display,
x11.root_window,
c.False,
c.SubstructureNotifyMask | c.SubstructureRedirectMask,
&ev,
);
_ = x11.libx11.XFlush(x11.display);
}

fn setDecorated(x11: *X11, enabled: bool) void {
const MWMHints = struct {
flags: u32,
functions: u32,
decorations: u32,
input_mode: i32,
status: u32,
};
const hints = MWMHints{
.functions = 0,
.flags = 2,
.decorations = if (enabled) 1 else 0,
.input_mode = 0,
.status = 0,
};
_ = x11.libx11.XChangeProperty(
x11.display,
x11.window,
x11.motif_wm_hints,
x11.motif_wm_hints,
32,
c.PropModeReplace,
@ptrCast(&hints),
5,
);
}

const LibX11 = struct {
handle: std.DynLib,
XInitThreads: *const @TypeOf(c.XInitThreads),
Expand Down

0 comments on commit f997859

Please sign in to comment.