Mercurial > games > semicongine
view src/semicongine/platform/linux/window.nim @ 206:7f921d7d0a2b
did: small refactoring of module structure
author | Sam <sam@basx.dev> |
---|---|
date | Tue, 09 May 2023 01:11:51 +0700 |
parents | de9dff24c422 |
children | ef8cea2545f8 |
line wrap: on
line source
import std/options import std/tables import std/strformat import x11/xlib, x11/xutil, x11/keysym import x11/x import ../../core import ../../events import ./symkey_map export keysym var deleteMessage*: Atom type NativeWindow* = object display*: ptr xlib.Display window*: x.Window emptyCursor: Cursor template checkXlibResult*(call: untyped) = let value = call if value == 0: 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 rootWindow = display.XDefaultRootWindow() var attribs: XWindowAttributes width = cuint(800) height = cuint(600) checkXlibResult display.XGetWindowAttributes(rootWindow, addr(attribs)) 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) var data = "\0".cstring var pixmap = display.XCreateBitmapFromData(window, data, 1, 1) var color: XColor 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: var NativeWindow, enable: bool) = var wm_state = window.display.XInternAtom("_NET_WM_STATE", 0) wm_fullscreen = window.display.XInternAtom("_NET_WM_STATE_FULLSCREEN", 0) var xev: XEvent xev.xclient = XClientMessageEvent( message_type: wm_state, format: 32, window: window.window, data: XClientMessageData( l: [ int(enable) xor 1, clong(wm_fullscreen), 0, 0, 0 ] ) ) xev.theType = ClientMessage checkXlibResult window.display.XSendEvent( window.display.DefaultRootWindow(), 0, SubstructureRedirectMask or SubstructureNotifyMask, addr xev ) 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) checkXlibResult window.display.XDestroyWindow(window.window) discard window.display.XCloseDisplay() # always returns 0 proc size*(window: NativeWindow): (int, int) = var attribs: XWindowAttributes checkXlibResult XGetWindowAttributes(window.display, window.window, addr(attribs)) return (int(attribs.width), int(attribs.height)) proc pendingEvents*(window: NativeWindow): seq[Event] = var event: XEvent serials: Table[culong, Table[int, seq[Event]]] while window.display.XPending() > 0: discard window.display.XNextEvent(addr(event)) case event.theType of ClientMessage: if cast[Atom](event.xclient.data.l[0]) == deleteMessage: result.add(Event(eventType: Quit)) of KeyPress: let keyevent = cast[PXKeyEvent](addr(event)) let xkey = int(keyevent.keycode) # ugly, but required to catch auto-repeat keys of X11 if not (keyevent.serial in serials): serials[keyevent.serial] = initTable[int, seq[Event]]() if not (xkey in serials[keyevent.serial]): serials[keyevent.serial][xkey] = newSeq[Event]() serials[keyevent.serial][xkey].add(Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))) of KeyRelease: let keyevent = cast[PXKeyEvent](addr(event)) let xkey = int(keyevent.keycode) # ugly, but required to catch auto-repeat keys of X11 if not (keyevent.serial in serials): serials[keyevent.serial] = initTable[int, seq[Event]]() if not (xkey in serials[keyevent.serial]): serials[keyevent.serial][xkey] = newSeq[Event]() serials[keyevent.serial][xkey].add Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN)) of ButtonPress: let button = int(cast[PXButtonEvent](addr(event)).button) result.add Event(eventType: MousePressed, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN)) of ButtonRelease: let button = int(cast[PXButtonEvent](addr(event)).button) result.add Event(eventType: MouseReleased, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN)) of MotionNotify: let motion = cast[PXMotionEvent](addr(event)) result.add Event(eventType: MouseMoved, x: motion.x, y: motion.y) of ConfigureNotify, Expose: result.add Event(eventType: ResizedWindow) else: discard # little hack to work around X11 auto-repeat keys for (serial, keys) in serials.pairs: for (key, events) in keys.pairs: if events.len == 1: result.add events[0] proc getMousePosition*(window: NativeWindow): Option[Vec2f] = var root: x.Window win: x.Window rootX: cint rootY: cint winX: cint winY: cint mask: cuint onscreen = XQueryPointer( window.display, window.window, addr(root), addr(win), addr(rootX), addr(rootY), addr(winX), addr(winY), addr(mask), ) if onscreen != 0: result = some(Vec2f([float32(winX), float32(winY)]))