app: added support for fullscreen mode

The option field WindowMode allows changing the window mode of an application in either Windowed or Fullscreen.
Only macOS, Windows and X11 platforms are currently supported.

Updates gio#89.

Signed-off-by: pierre <pierre.curto@gmail.com>
This commit is contained in:
pierre
2021-03-23 15:39:25 +01:00
committed by Elias Naur
parent bc2c3db43e
commit 238dd1aa86
7 changed files with 171 additions and 1 deletions
+69
View File
@@ -54,6 +54,23 @@ type MinMaxInfo struct {
PtMaxTrackSize Point
}
type WindowPlacement struct {
length uint32
flags uint32
showCmd uint32
ptMinPosition Point
ptMaxPosition Point
rcNormalPosition Rect
rcDevice Rect
}
type MonitorInfo struct {
cbSize uint32
Monitor Rect
WorkArea Rect
Flags uint32
}
const (
TRUE = 1
@@ -63,6 +80,9 @@ const (
CW_USEDEFAULT = -2147483648
GWL_STYLE = ^(uint32(16) - 1) // -16
HWND_TOPMOST = ^(uint32(1) - 1) // -1
HTCLIENT = 1
IDC_ARROW = 32512
@@ -87,6 +107,12 @@ const (
SW_SHOWDEFAULT = 10
SWP_FRAMECHANGED = 0x0020
SWP_NOMOVE = 0x0002
SWP_NOOWNERZORDER = 0x0200
SWP_NOSIZE = 0x0001
SWP_NOZORDER = 0x0004
USER_TIMER_MINIMUM = 0x0000000A
VK_CONTROL = 0x11
@@ -237,10 +263,14 @@ var (
_GetKeyState = user32.NewProc("GetKeyState")
_GetMessage = user32.NewProc("GetMessageW")
_GetMessageTime = user32.NewProc("GetMessageTime")
_GetMonitorInfo = user32.NewProc("GetMonitorInfoW")
_GetWindowLong = user32.NewProc("GetWindowLongPtrW")
_GetWindowPlacement = user32.NewProc("GetWindowPlacement")
_KillTimer = user32.NewProc("KillTimer")
_LoadCursor = user32.NewProc("LoadCursorW")
_LoadImage = user32.NewProc("LoadImageW")
_MonitorFromPoint = user32.NewProc("MonitorFromPoint")
_MonitorFromWindow = user32.NewProc("MonitorFromWindow")
_MsgWaitForMultipleObjectsEx = user32.NewProc("MsgWaitForMultipleObjectsEx")
_OpenClipboard = user32.NewProc("OpenClipboard")
_PeekMessage = user32.NewProc("PeekMessageW")
@@ -258,6 +288,9 @@ var (
_SetFocus = user32.NewProc("SetFocus")
_SetProcessDPIAware = user32.NewProc("SetProcessDPIAware")
_SetTimer = user32.NewProc("SetTimer")
_SetWindowLong = user32.NewProc("SetWindowLongPtrW")
_SetWindowPos = user32.NewProc("SetWindowPos")
_SetWindowPlacement = user32.NewProc("SetWindowPlacement")
_TranslateMessage = user32.NewProc("TranslateMessage")
_UnregisterClass = user32.NewProc("UnregisterClassW")
_UpdateWindow = user32.NewProc("UpdateWindow")
@@ -417,6 +450,42 @@ func GetWindowDPI(hwnd syscall.Handle) int {
}
}
func GetWindowPlacement(hwnd syscall.Handle) *WindowPlacement {
var wp WindowPlacement
wp.length = uint32(unsafe.Sizeof(wp))
_GetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&wp)))
return &wp
}
func GetMonitorInfo(hwnd syscall.Handle) MonitorInfo {
var mi MonitorInfo
mi.cbSize = uint32(unsafe.Sizeof(mi))
v, _, _ := _MonitorFromWindow.Call(uintptr(hwnd), MONITOR_DEFAULTTOPRIMARY)
_GetMonitorInfo.Call(v, uintptr(unsafe.Pointer(&mi)))
return mi
}
func GetWindowLong(hwnd syscall.Handle) (style uintptr) {
style, _, _ = _GetWindowLong.Call(uintptr(hwnd), uintptr(GWL_STYLE))
return
}
func SetWindowLong(hwnd syscall.Handle, idx uint32, style uintptr) {
_SetWindowLong.Call(uintptr(hwnd), uintptr(idx), style)
}
func SetWindowPlacement(hwnd syscall.Handle, wp *WindowPlacement) {
_SetWindowPlacement.Call(uintptr(hwnd), uintptr(unsafe.Pointer(wp)))
}
func SetWindowPos(hwnd syscall.Handle, hwndInsertAfter uint32, x, y, dx, dy int32, style uintptr) {
_SetWindowPos.Call(uintptr(hwnd), uintptr(hwndInsertAfter),
uintptr(x), uintptr(y),
uintptr(dx), uintptr(dy),
style,
)
}
func GlobalAlloc(size int) (syscall.Handle, error) {
r, _, err := _GlobalAlloc.Call(GHND, uintptr(size))
if r == 0 {
+13
View File
@@ -41,6 +41,7 @@ __attribute__ ((visibility ("hidden"))) CGFloat gio_getScreenBackingScale(void);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_readClipboard(void);
__attribute__ ((visibility ("hidden"))) void gio_writeClipboard(unichar *chars, NSUInteger length);
__attribute__ ((visibility ("hidden"))) void gio_setNeedsDisplay(CFTypeRef viewRef);
__attribute__ ((visibility ("hidden"))) void gio_toggleFullScreen(CFTypeRef windowRef);
__attribute__ ((visibility ("hidden"))) CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight);
__attribute__ ((visibility ("hidden"))) void gio_makeKeyAndOrderFront(CFTypeRef windowRef);
__attribute__ ((visibility ("hidden"))) NSPoint gio_cascadeTopLeftFromPoint(CFTypeRef windowRef, NSPoint topLeft);
@@ -62,6 +63,7 @@ type window struct {
cursor pointer.CursorName
scale float32
mode WindowMode
}
// viewMap is the mapping from Cocoa NSViews to Go windows.
@@ -124,6 +126,16 @@ func (w *window) WriteClipboard(s string) {
})
}
func (w *window) SetWindowMode(mode WindowMode) {
switch mode {
case w.mode:
return
case Fullscreen:
C.gio_toggleFullScreen(w.window)
}
w.mode = mode
}
func (w *window) SetCursor(name pointer.CursorName) {
w.cursor = windowSetCursor(w.cursor, name)
}
@@ -353,6 +365,7 @@ func NewWindow(win Callbacks, opts *Options) error {
}
nextTopLeft = C.gio_cascadeTopLeftFromPoint(w.window, nextTopLeft)
C.gio_makeKeyAndOrderFront(w.window)
w.SetWindowMode(opts.WindowMode)
})
return <-errch
}
+5
View File
@@ -167,6 +167,11 @@ void gio_makeKeyAndOrderFront(CFTypeRef windowRef) {
[window makeKeyAndOrderFront:nil];
}
void gio_toggleFullScreen(CFTypeRef windowRef) {
NSWindow *window = (__bridge NSWindow *)windowRef;
[window toggleFullScreen:nil];
}
CFTypeRef gio_createWindow(CFTypeRef viewRef, const char *title, CGFloat width, CGFloat height, CGFloat minWidth, CGFloat minHeight, CGFloat maxWidth, CGFloat maxHeight) {
@autoreleasepool {
NSRect rect = NSMakeRect(0, 0, width, height);
+36
View File
@@ -52,6 +52,9 @@ type window struct {
cursorIn bool
cursor syscall.Handle
// placement saves the previous window position when in full screen mode.
placement *windows.WindowPlacement
mu sync.Mutex
animating bool
@@ -119,6 +122,7 @@ func NewWindow(window Callbacks, opts *Options) error {
// Since the window class for the cursor is null,
// set it here to show the cursor.
w.SetCursor(pointer.CursorDefault)
w.SetWindowMode(opts.WindowMode)
if err := w.loop(); err != nil {
panic(err)
}
@@ -524,6 +528,38 @@ func (w *window) readClipboard() error {
return nil
}
func (w *window) SetWindowMode(mode WindowMode) {
// https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
switch mode {
case Windowed:
if w.placement == nil {
return
}
windows.SetWindowPlacement(w.hwnd, w.placement)
w.placement = nil
style := windows.GetWindowLong(w.hwnd)
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style|windows.WS_OVERLAPPEDWINDOW)
windows.SetWindowPos(w.hwnd, windows.HWND_TOPMOST,
0, 0, 0, 0,
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
)
case Fullscreen:
if w.placement != nil {
return
}
w.placement = windows.GetWindowPlacement(w.hwnd)
style := windows.GetWindowLong(w.hwnd)
windows.SetWindowLong(w.hwnd, windows.GWL_STYLE, style&^windows.WS_OVERLAPPEDWINDOW)
mi := windows.GetMonitorInfo(w.hwnd)
windows.SetWindowPos(w.hwnd, 0,
mi.Monitor.Left, mi.Monitor.Top,
mi.Monitor.Right-mi.Monitor.Left,
mi.Monitor.Bottom-mi.Monitor.Top,
windows.SWP_NOOWNERZORDER|windows.SWP_FRAMECHANGED,
)
}
}
func (w *window) WriteClipboard(s string) {
w.writeClipboard(s)
}
+24 -1
View File
@@ -71,6 +71,12 @@ type x11Window struct {
atom C.Atom
// "GTK_TEXT_BUFFER_CONTENTS"
gtk_text_buffer_contents C.Atom
// "_NET_WM_NAME"
wmName C.Atom
// "_NET_WM_STATE"
wmState C.Atom
// _NET_WM_STATE_FULLSCREEN"
wmStateFullscreen C.Atom
}
stage system.Stage
cfg unit.Metric
@@ -141,6 +147,18 @@ func (w *x11Window) SetCursor(name pointer.CursorName) {
C.XDefineCursor(w.x, w.xw, c)
}
func (w *x11Window) SetWindowMode(mode WindowMode) {
switch mode {
case Windowed:
C.XDeleteProperty(w.x, w.xw, w.atoms.wmStateFullscreen)
case Fullscreen:
C.XChangeProperty(w.x, w.xw, w.atoms.wmState, C.XA_ATOM,
32, C.PropModeReplace,
(*C.uchar)(unsafe.Pointer(&w.atoms.wmStateFullscreen)), 1,
)
}
}
func (w *x11Window) ShowTextInput(show bool) {}
// Close the window.
@@ -612,6 +630,9 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
w.atoms.clipboardContent = w.atom("CLIPBOARD_CONTENT", false)
w.atoms.atom = w.atom("ATOM", false)
w.atoms.targets = w.atom("TARGETS", false)
w.atoms.wmName = w.atom("_NET_WM_NAME", false)
w.atoms.wmState = w.atom("_NET_WM_STATE", false)
w.atoms.wmStateFullscreen = w.atom("_NET_WM_STATE_FULLSCREEN", false)
// set the name
ctitle := C.CString(opts.Title)
@@ -625,11 +646,13 @@ func newX11Window(gioWin Callbacks, opts *Options) error {
format: 8,
nitems: C.ulong(len(opts.Title)),
},
w.atom("_NET_WM_NAME", false))
w.atoms.wmName)
// extensions
C.XSetWMProtocols(dpy, win, &w.atoms.evDelWindow, 1)
w.SetWindowMode(opts.WindowMode)
// make the window visible on the screen
C.XMapWindow(dpy, win)
+8
View File
@@ -19,8 +19,16 @@ type Options struct {
MinWidth, MinHeight unit.Value
MaxWidth, MaxHeight unit.Value
Title string
WindowMode WindowMode
}
type WindowMode uint8
const (
Windowed WindowMode = iota
Fullscreen
)
type FrameEvent struct {
system.FrameEvent
+16
View File
@@ -444,6 +444,22 @@ func (q *queue) Events(k event.Tag) []event.Event {
return q.q.Events(k)
}
const (
// Windowed is the normal window mode with OS specific window decorations.
Windowed = wm.Windowed
// Fullscreen is the full screen window mode.
Fullscreen = wm.Fullscreen
)
// WindowMode sets the window mode.
//
// Supported platforms are macOS, X11 and Windows.
func WindowMode(mode wm.WindowMode) Option {
return func(opts *wm.Options) {
opts.WindowMode = mode
}
}
// Title sets the title of the wm.
func Title(t string) Option {
return func(opts *wm.Options) {