mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user