Mercurial > games > semicongine
view semiconginev2/rendering/platform/linux.nim @ 1256:bfb75c934f4e
add: window focus handling, improve window api a bit
author | sam <sam@basx.dev> |
---|---|
date | Sun, 28 Jul 2024 17:33:41 +0700 |
parents | b0f4c8ccd49a |
children |
line wrap: on
line source
import ../../thirdparty/x11/xlib import ../../thirdparty/x11/xutil import ../../thirdparty/x11/keysym import ../../thirdparty/x11/x as x11 import ../../thirdparty/x11/xkblib const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_xlib_surface"] # got values (keycodes) from xev const KeyTypeMap = { 9: Escape, 67: F1, 68: F2, 69: F3, 70: F4, 71: F5, 72: F6, 73: F7, 74: F8, 75: F9, 76: F10, 95: F11, 96: F12, 49: NumberRowExtra1, 10: `1`, 11: `2`, 12: `3`, 13: `4`, 14: `5`, 15: `6`, 16: `7`, 17: `8`, 18: `9`, 19: `0`, 20: NumberRowExtra2, 21: NumberRowExtra3, 24: Q, 25: W, 26: E, 27: R, 28: T, 29: Y, 30: U, 31: I, 32: O, 33: P, 38: A, 39: S, 40: D, 41: Key.F, 42: G, 43: H, 44: J, 45: K, 46: L, 52: Z, 53: X, 54: C, 55: V, 56: B, 57: N, 58: M, 23: Tab, 66: CapsLock, 50: ShiftL, 62: ShiftR, 37: CtrlL, 105: CtrlR, 133: SuperL, 134: SuperR, 64: AltL, 108: AltR, 65: Space, 36: Enter, 22: Backspace, 34: LetterRow1Extra1, 35: LetterRow1Extra2, 47: LetterRow2Extra1, 48: LetterRow2Extra2, 51: LetterRow2Extra3, 59: LetterRow3Extra1, 60: LetterRow3Extra2, 61: LetterRow3Extra3, 111: Up, 116: Down, 113: Left, 114: Right, 112: PageUp, 117: PageDown, 110: Home, 115: End, 118: Insert, 119: Delete, 107: PrintScreen, 78: ScrollLock, 127: Pause, }.toTable const MouseButtonTypeMap = { x11.Button1: MouseButton.Mouse1, x11.Button2: MouseButton.Mouse2, x11.Button3: MouseButton.Mouse3, }.toTable var deleteMessage*: Atom type NativeWindow* = object display*: ptr xlib.Display window*: x11.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.} = logging.error &"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() discard display.XkbSetDetectableAutoRepeat(true, nil) var attribs: XWindowAttributes width = cuint(800) height = cuint(600) checkXlibResult display.XGetWindowAttributes(rootWindow, addr(attribs)) var attrs = XSetWindowAttributes() 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 or FocusChangeMask) 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 SetTitle*(window: NativeWindow, title: string) = checkXlibResult XSetStandardProperties(window.display, window.window, title, "window", 0, nil, 0, nil) proc SetFullscreen*(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(not 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 ShowSystemCursor*(window: NativeWindow, value: bool) = if value == true: checkXlibResult XUndefineCursor(window.display, window.window) checkXlibResult window.display.XFlush() else: checkXlibResult XDefineCursor(window.display, window.window, window.emptyCursor) 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 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) result.add Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN)) of KeyRelease: let keyevent = cast[PXKeyEvent](addr(event)) let xkey = int(keyevent.keycode) result.add Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN)) of ButtonPress: let button = int(cast[PXButtonEvent](addr(event)).button) if button == Button4: result.add Event(eventType: MouseWheel, amount: 1'f32) elif button == Button5: result.add Event(eventType: MouseWheel, amount: -1'f32) else: 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 FocusIn: result.add Event(eventType: GotFocus) of FocusOut: result.add Event(eventType: LostFocus) of ConfigureNotify, Expose: result.add Event(eventType: ResizedWindow) else: discard proc GetMousePosition*(window: NativeWindow): Option[Vec2f] = var root: x11.Window win: x11.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)])) proc SetMousePosition*(window: NativeWindow, x, y: int) = checkXlibResult XWarpPointer( window.display, default(x11.Window), window.window, 0, 0, 0, 0, x.cint, y.cint, ) checkXlibResult XSync(window.display, false.XBool) proc CreateNativeSurface(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, dpy: cast[ptr Display](window.display), window: cast[Window](window.window), ) checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))