Mercurial > games > semicongine
diff semiconginev2/rendering/platform/linux.nim @ 1224:a3fa15c25026 compiletime-tests
did: cleanup, add audio, change platform-dependent structure
author | sam <sam@basx.dev> |
---|---|
date | Wed, 17 Jul 2024 22:02:11 +0700 |
parents | |
children | b0f4c8ccd49a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/semiconginev2/rendering/platform/linux.nim Wed Jul 17 22:02:11 2024 +0700 @@ -0,0 +1,214 @@ +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) + 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 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(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 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 + 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 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 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))