From f57073f02fc924c6499bd77ebdfbb38db62675d1 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sat, 28 Jan 2023 12:03:03 -0700 Subject: [PATCH] core: add borderless window fullscreen support Signed-off-by: Stephen Gutekanst --- libs/core/src/Core.zig | 14 +++++- libs/core/src/platform/native/Core.zig | 67 +++++++++++++++++++------- libs/core/src/platform/wasm/Core.zig | 4 ++ 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/libs/core/src/Core.zig b/libs/core/src/Core.zig index 6ddaa40d..7bfa7079 100644 --- a/libs/core/src/Core.zig +++ b/libs/core/src/Core.zig @@ -373,9 +373,21 @@ pub const KeyMods = packed struct { }; pub const DisplayMode = enum { + /// Windowed mode. windowed, + + /// Fullscreen mode, using this option may change the display's video mode. fullscreen, - // TODO: fullscreen_windowed, + + /// Borderless fullscreen window. + /// + /// Beware that true .fullscreen is also a hint to the OS that is used in various contexts, e.g. + /// + /// * macOS: Moving to a virtual space dedicated to fullscreen windows as the user expects + /// * macOS: .borderless windows cannot prevent the system menu bar from being displayed + /// + /// Always allow users to choose their preferred display mode. + borderless, }; pub const CursorMode = enum { diff --git a/libs/core/src/platform/native/Core.zig b/libs/core/src/platform/native/Core.zig index b61e3836..0548be71 100644 --- a/libs/core/src/platform/native/Core.zig +++ b/libs/core/src/platform/native/Core.zig @@ -38,6 +38,8 @@ last_size: glfw.Window.Size, last_pos: glfw.Window.Pos, size_limit: SizeLimit, frame_buffer_resized: bool, +display_mode: DisplayMode, +border: bool, current_cursor: CursorShape, cursors: [@typeInfo(CursorShape).Enum.fields.len]?glfw.Cursor, @@ -191,6 +193,8 @@ pub fn init(core: *Core, allocator: std.mem.Allocator, options: Options) !void { .max = .{ .width = null, .height = null }, }, .frame_buffer_resized = false, + .display_mode = .windowed, + .border = true, .current_cursor = .arrow, .cursors = std.mem.zeroes([@typeInfo(CursorShape).Enum.fields.len]?glfw.Cursor), @@ -391,10 +395,12 @@ pub fn setTitle(self: *Core, title: [:0]const u8) void { self.window.setTitle(title); } -pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor_index: ?usize) !void { +pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor_index: ?usize) void { switch (mode) { .windowed => { - try self.window.setMonitor( + self.window.setAttrib(.decorated, self.border); + self.window.setAttrib(.floating, false); + self.window.setMonitor( null, @intCast(i32, self.last_pos.x), @intCast(i32, self.last_pos.y), @@ -404,41 +410,68 @@ pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor_index: ?usize) !vo ); }, .fullscreen => { - if (try self.displayMode() == .windowed) { - self.last_size = try self.window.getSize(); - self.last_pos = try self.window.getPos(); + if (self.display_mode == .windowed) { + self.last_size = self.window.getSize(); + self.last_pos = self.window.getPos(); } const monitor = blk: { if (monitor_index) |i| { - const monitor_list = try glfw.Monitor.getAll(self.allocator); + // TODO(core): handle OOM via error flag + const monitor_list = glfw.Monitor.getAll(self.allocator) catch unreachable; defer self.allocator.free(monitor_list); break :blk monitor_list[i]; } break :blk glfw.Monitor.getPrimary(); }; + if (monitor) |m| { + const video_mode = m.getVideoMode(); + if (video_mode) |v| { + self.window.setMonitor(m, 0, 0, v.getWidth(), v.getHeight(), null); + } + } + }, + .borderless => { + if (self.display_mode == .windowed) { + self.last_size = self.window.getSize(); + self.last_pos = self.window.getPos(); + } - const video_mode = try monitor.?.getVideoMode(); - try self.window.setMonitor(monitor, 0, 0, video_mode.getWidth(), video_mode.getHeight(), null); + const monitor = blk: { + if (monitor_index) |i| { + // TODO(core): handle OOM via error flag + const monitor_list = glfw.Monitor.getAll(self.allocator) catch unreachable; + defer self.allocator.free(monitor_list); + break :blk monitor_list[i]; + } + break :blk glfw.Monitor.getPrimary(); + }; + if (monitor) |m| { + const video_mode = m.getVideoMode(); + if (video_mode) |v| { + self.window.setAttrib(.decorated, false); + self.window.setAttrib(.floating, true); + self.window.setMonitor(null, 0, 0, v.getWidth(), v.getHeight(), null); + } + } }, } + self.display_mode = mode; } pub fn displayMode(self: *Core) DisplayMode { - if (self.window.getMonitor()) |_| { - return .fullscreen; - } else { - return .windowed; - } + return self.display_mode; } pub fn setBorder(self: *Core, value: bool) void { - self.window.setAttrib(.decorated, value); + if (self.border != value) { + self.border = value; + if (self.display_mode != .borderless) self.window.setAttrib(.decorated, value); + } } -pub fn border(self: *Core) !bool { - const decorated = try self.window.getAttrib(.decorated); - return decorated == 1; +pub fn border(self: *Core) bool { + return self.border; } pub fn setHeadless(self: *Core, value: bool) void { diff --git a/libs/core/src/platform/wasm/Core.zig b/libs/core/src/platform/wasm/Core.zig index e46f7fd5..6fecb985 100644 --- a/libs/core/src/platform/wasm/Core.zig +++ b/libs/core/src/platform/wasm/Core.zig @@ -183,6 +183,10 @@ pub fn setTitle(self: *Core, title: [:0]const u8) void { pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor: ?usize) void { _ = monitor; + if (mode == .borderless) { + // borderless fullscreen window has no meaning in web + mode = .fullscreen; + } js.machCanvasSetDisplayMode(self.id, @enumToInt(mode)); }