core: add borderless window fullscreen support

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2023-01-28 12:03:03 -07:00
parent cf1e870688
commit f57073f02f
3 changed files with 67 additions and 18 deletions

View file

@ -373,9 +373,21 @@ pub const KeyMods = packed struct {
}; };
pub const DisplayMode = enum { pub const DisplayMode = enum {
/// Windowed mode.
windowed, windowed,
/// Fullscreen mode, using this option may change the display's video mode.
fullscreen, 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 { pub const CursorMode = enum {

View file

@ -38,6 +38,8 @@ last_size: glfw.Window.Size,
last_pos: glfw.Window.Pos, last_pos: glfw.Window.Pos,
size_limit: SizeLimit, size_limit: SizeLimit,
frame_buffer_resized: bool, frame_buffer_resized: bool,
display_mode: DisplayMode,
border: bool,
current_cursor: CursorShape, current_cursor: CursorShape,
cursors: [@typeInfo(CursorShape).Enum.fields.len]?glfw.Cursor, 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 }, .max = .{ .width = null, .height = null },
}, },
.frame_buffer_resized = false, .frame_buffer_resized = false,
.display_mode = .windowed,
.border = true,
.current_cursor = .arrow, .current_cursor = .arrow,
.cursors = std.mem.zeroes([@typeInfo(CursorShape).Enum.fields.len]?glfw.Cursor), .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); 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) { switch (mode) {
.windowed => { .windowed => {
try self.window.setMonitor( self.window.setAttrib(.decorated, self.border);
self.window.setAttrib(.floating, false);
self.window.setMonitor(
null, null,
@intCast(i32, self.last_pos.x), @intCast(i32, self.last_pos.x),
@intCast(i32, self.last_pos.y), @intCast(i32, self.last_pos.y),
@ -404,41 +410,68 @@ pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor_index: ?usize) !vo
); );
}, },
.fullscreen => { .fullscreen => {
if (try self.displayMode() == .windowed) { if (self.display_mode == .windowed) {
self.last_size = try self.window.getSize(); self.last_size = self.window.getSize();
self.last_pos = try self.window.getPos(); self.last_pos = self.window.getPos();
} }
const monitor = blk: { const monitor = blk: {
if (monitor_index) |i| { 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); defer self.allocator.free(monitor_list);
break :blk monitor_list[i]; break :blk monitor_list[i];
} }
break :blk glfw.Monitor.getPrimary(); 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(); const monitor = blk: {
try self.window.setMonitor(monitor, 0, 0, video_mode.getWidth(), video_mode.getHeight(), null); 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 { pub fn displayMode(self: *Core) DisplayMode {
if (self.window.getMonitor()) |_| { return self.display_mode;
return .fullscreen;
} else {
return .windowed;
}
} }
pub fn setBorder(self: *Core, value: bool) void { 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 { pub fn border(self: *Core) bool {
const decorated = try self.window.getAttrib(.decorated); return self.border;
return decorated == 1;
} }
pub fn setHeadless(self: *Core, value: bool) void { pub fn setHeadless(self: *Core, value: bool) void {

View file

@ -183,6 +183,10 @@ pub fn setTitle(self: *Core, title: [:0]const u8) void {
pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor: ?usize) void { pub fn setDisplayMode(self: *Core, mode: DisplayMode, monitor: ?usize) void {
_ = monitor; _ = monitor;
if (mode == .borderless) {
// borderless fullscreen window has no meaning in web
mode = .fullscreen;
}
js.machCanvasSetDisplayMode(self.id, @enumToInt(mode)); js.machCanvasSetDisplayMode(self.id, @enumToInt(mode));
} }