# HG changeset patch # User Sam # Date 1682791353 -25200 # Node ID 34e536f7001f60089f31de8bba8edcd2322fc9b9 # Parent bf2f4a9cd962ce77858f9173c6eceaae42205c8e add: support for showing/hiding cursur, X11 fullscreen (win32 still missing) diff -r bf2f4a9cd962 -r 34e536f7001f examples/E10_pong.nim --- a/examples/E10_pong.nim Sat Apr 29 21:38:52 2023 +0700 +++ b/examples/E10_pong.nim Sun Apr 30 01:02:33 2023 +0700 @@ -67,13 +67,17 @@ width = 1'f currentTime = cpuTime() showSystemCursor = true - while myengine.updateInputs() == Running and not myengine.keyWasPressed(Escape): + fullscreen = false + while myengine.updateInputs() == Running and not myengine.keyIsDown(Escape): if myengine.keyWasPressed(C): if showSystemCursor: myengine.hideSystemCursor() else: myengine.showSystemCursor() showSystemCursor = not showSystemCursor + if myengine.keyWasPressed(F): + fullscreen = not fullscreen + myengine.fullscreen(fullscreen) let dt: float32 = cpuTime() - currentTime currentTime = cpuTime() @@ -97,7 +101,8 @@ # loose if ball.transform.col(3).x - ballSize/2 <= 0: - break + ball.transform = scale3d(ballSize, ballSize, 1'f) * translate3d(30'f, 30'f, 0'f) + ballVelocity = newVec2f(1, 1).normalized * ballSpeed # bar if ball.transform.col(3).x - ballSize/2 <= barWidth: diff -r bf2f4a9cd962 -r 34e536f7001f src/semicongine/engine.nim --- a/src/semicongine/engine.nim Sat Apr 29 21:38:52 2023 +0700 +++ b/src/semicongine/engine.nim Sun Apr 30 01:02:33 2023 +0700 @@ -167,3 +167,4 @@ func windowWasResized*(engine: Engine): auto = engine.input.windowWasResized func showSystemCursor*(engine: Engine) = engine.window.showSystemCursor() func hideSystemCursor*(engine: Engine) = engine.window.hideSystemCursor() +proc fullscreen*(engine: Engine, enable: bool) = engine.window.fullscreen(enable) diff -r bf2f4a9cd962 -r 34e536f7001f src/semicongine/platform/linux/window.nim --- a/src/semicongine/platform/linux/window.nim Sat Apr 29 21:38:52 2023 +0700 +++ b/src/semicongine/platform/linux/window.nim Sun Apr 30 01:02:33 2023 +0700 @@ -1,9 +1,13 @@ import std/options import std/tables +import std/strformat +import std/logging + import x11/xlib, x11/xutil, - x11/keysym + x11/keysym, + x11/xatom import x11/x import ../../events @@ -27,42 +31,113 @@ raise newException(Exception, "Xlib error: " & astToStr(call) & " returned " & $value) + +proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} = + echo &"Xlib: {event[]}" + proc createWindow*(title: string): NativeWindow = checkXlibResult XInitThreads() let display = XOpenDisplay(nil) if display == nil: quit "Failed to open display" + discard XSetErrorHandler(XErrorLogger) - let - screen = XDefaultScreen(display) - rootWindow = XRootWindow(display, screen) - foregroundColor = XBlackPixel(display, screen) - backgroundColor = XWhitePixel(display, screen) + let rootWindow = display.XDefaultRootWindow() + var + attribs: XWindowAttributes + width = cuint(800) + height = cuint(600) + checkXlibResult display.XGetWindowAttributes(rootWindow, addr(attribs)) - let window = XCreateSimpleWindow(display, rootWindow, -1, -1, 800, 600, 0, - foregroundColor, backgroundColor) - checkXlibResult XSetStandardProperties(display, window, title, "window", 0, - nil, 0, nil) - checkXlibResult XSelectInput(display, window, PointerMotionMask or - ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask or ExposureMask) + var attrs = XSetWindowAttributes( + # override_redirect: 1 + ) + let window = XCreateWindow( + display, + rootWindow, + (attribs.width - cint(width)) div 2, (attribs.height - cint(height)) div 2, + width, height, + 0, + CopyFromParent, + InputOutput, + cast[PVisual](CopyFromParent), + 0, # CWOverrideRedirect, + addr attrs, + # foregroundColor, backgroundColor + ) + checkXlibResult XSetStandardProperties(display, window, title, "window", 0, nil, 0, nil) + checkXlibResult XSelectInput(display, window, PointerMotionMask or ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask or ExposureMask) checkXlibResult XMapWindow(display, window) deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false)) checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1) - # quite a lot of work to hide the cursor... var data = "\0".cstring - var pixmap = XCreateBitmapFromData(display, window, data, 1, 1) + var pixmap = display.XCreateBitmapFromData(window, data, 1, 1) var color: XColor - var empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap, addr(color), addr(color), 0, 0) - checkXlibResult XFreePixmap(display, pixmap) + var empty_cursor = display.XCreatePixmapCursor(pixmap, pixmap, addr(color), addr(color), 0, 0) + checkXlibResult display.XFreePixmap(pixmap) return NativeWindow(display: display, window: window, emptyCursor: empty_cursor) +proc fullscreen*(window: NativeWindow, enable: bool) = + var WM_HINTS = XInternAtom(window.display, "_MOTIF_WM_HINTS", 1); + var border = culong(if enable: 0 else: 1) + + type + MWMHints = object + flags: culong + functions: culong + decorations: culong + input_mode: clong + status: culong + + var hints = MWMHints(flags: (culong(1) shl 1), functions: 0, decorations: border, input_mode: 0, status: 0) + + checkXlibResult window.display.XChangeProperty(window.window, WM_HINTS, WM_HINTS, 32, PropModeReplace, cast[cstring](addr hints), sizeof(MWMHints) div sizeof(clong)) + + var + wm_state = window.display.XInternAtom("_NET_WM_STATE", 1) + wm_state_operation = window.display.XInternAtom(if enable: "_NET_WM_STATE_ADD" else: "_NET_WM_STATE_REMOVE", 1) + wm_fullscreen = window.display.XInternAtom("_NET_WM_STATE_FULLSCREEN", 1) + xev = XEvent( + theType: ClientMessage, + xany: XAnyEvent(theType: ClientMessage), + xclient: XClientMessageEvent( + message_type: wm_state, + format: 32, + window: window.window, + data: XClientMessageData( + l: [ + clong(wm_state_operation), + clong(wm_fullscreen), + 0, + 0, + 0 + ] + ) + ) + ) + + discard window.display.XSendEvent( + window.display.XRootWindow(window.display.XDefaultScreen()), + 0, + SubstructureRedirectMask or SubstructureNotifyMask, + addr xev + ) + + if enable: + var attribs: XWindowAttributes + checkXlibResult window.display.XGetWindowAttributes(window.display.XDefaultRootWindow(), addr(attribs)) + checkXlibResult window.display.XMoveResizeWindow(window.window, 0, 0, cuint(attribs.width), cuint(attribs.height)) + checkXlibResult window.display.XFlush() + proc hideSystemCursor*(window: NativeWindow) = checkXlibResult XDefineCursor(window.display, window.window, window.emptyCursor) + checkXlibResult window.display.XFlush() proc showSystemCursor*(window: NativeWindow) = checkXlibResult XUndefineCursor(window.display, window.window) + checkXlibResult window.display.XFlush() proc destroy*(window: NativeWindow) = checkXlibResult window.display.XFreeCursor(window.emptyCursor) diff -r bf2f4a9cd962 -r 34e536f7001f src/semicongine/platform/windows/window.nim --- a/src/semicongine/platform/windows/window.nim Sat Apr 29 21:38:52 2023 +0700 +++ b/src/semicongine/platform/windows/window.nim Sun Apr 30 01:02:33 2023 +0700 @@ -87,6 +87,10 @@ discard ShowWindow(result.hwnd, SW_SHOW) +proc fullscreen*(window: NativeWindow, enable: bool) = + discard + # from the oneandonly: https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353 + proc hideSystemCursor*(window: NativeWindow) = discard ShowCursor(false)