changeset 1446:ee9bc0f47f41 default tip main

did: cleanup platform code
author sam <sam@basx.dev>
date Sat, 01 Mar 2025 10:53:47 +0700
parents 8ec9ce8eade0
children
files semicongine/platform/linux/rendering.nim semicongine/platform/windows/rendering.nim semicongine/rendering.nim semicongine/rendering/platform/linux.nim semicongine/rendering/platform/windows.nim
diffstat 5 files changed, 616 insertions(+), 616 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/semicongine/platform/linux/rendering.nim	Sat Mar 01 10:53:47 2025 +0700
@@ -0,0 +1,306 @@
+import std/options
+import std/tables
+
+import ../../thirdparty/x11/xlib
+import ../../thirdparty/x11/xutil
+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: Key.R,
+  28: T,
+  29: Key.Y,
+  30: U,
+  31: I,
+  32: Key.O,
+  33: P,
+  38: A,
+  39: S,
+  40: D,
+  41: Key.F,
+  42: Key.G,
+  43: H,
+  44: J,
+  45: K,
+  46: L,
+  52: Key.Z,
+  53: Key.X,
+  54: C,
+  55: V,
+  56: Key.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: Key.Left,
+  114: Key.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* {.hint[GlobalVar]: off.}: Atom # one internal use, not serious
+
+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,
+    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 destroyWindow*(window: NativeWindow) =
+  checkXlibResult XDestroyWindow(window.display, window.window)
+
+proc setTitle*(window: NativeWindow, title: string) =
+  discard 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): Vec2i =
+  var attribs: XWindowAttributes
+  discard XGetWindowAttributes(window.display, window.window, addr(attribs))
+  vec2i(attribs.width, 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 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): Vec2i =
+  var
+    root: x11.Window
+    win: x11.Window
+    rootX: cint
+    rootY: cint
+    winX: cint
+    winY: cint
+    mask: cuint
+  discard XQueryPointer(
+    window.display,
+    window.window,
+    addr(root),
+    addr(win),
+    addr(rootX),
+    addr(rootY),
+    addr(winX),
+    addr(winY),
+    addr(mask),
+  )
+  vec2i(winX, winY)
+
+proc setMousePosition*(window: NativeWindow, pos: Vec2i) =
+  discard XWarpPointer(
+    window.display,
+    default(x11.Window),
+    window.window,
+    0,
+    0,
+    0,
+    0,
+    pos.x.cint,
+    pos.y.cint,
+  )
+
+proc createNativeSurface(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
+  var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
+    sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+    dpy: cast[ptr core.Display](window.display),
+    window: cast[core.Window](window.window),
+  )
+  checkVkResult vkCreateXlibSurfaceKHR(
+    instance, addr(surfaceCreateInfo), nil, addr(result)
+  )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/semicongine/platform/windows/rendering.nim	Sat Mar 01 10:53:47 2025 +0700
@@ -0,0 +1,308 @@
+import std/tables
+import std/options
+
+import ../../thirdparty/winim/winim/inc/[windef, winuser, wincon, winbase]
+import ../../thirdparty/winim/winim/[winstr, utils]
+
+const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_win32_surface"]
+
+const KeyTypeMap* = {
+  VK_ESCAPE: Key.Escape,
+  VK_F1: F1,
+  VK_F2: F2,
+  VK_F3: F3,
+  VK_F4: F4,
+  VK_F5: F5,
+  VK_F6: F6,
+  VK_F7: F7,
+  VK_F8: F8,
+  VK_F9: F9,
+  VK_F10: F10,
+  VK_F11: F11,
+  VK_F12: F12,
+  VK_OEM_3: NumberRowExtra1,
+  int('0'): `0`,
+  int('1'): `1`,
+  int('2'): `2`,
+  int('3'): `3`,
+  int('4'): `4`,
+  int('5'): `5`,
+  int('6'): `6`,
+  int('7'): `7`,
+  int('8'): `8`,
+  int('9'): `9`,
+  VK_OEM_MINUS: NumberRowExtra2,
+  VK_OEM_PLUS: NumberRowExtra3,
+  int('A'): A,
+  int('B'): Key.B,
+  int('C'): C,
+  int('D'): D,
+  int('E'): E,
+  int('F'): F,
+  int('G'): Key.G,
+  int('H'): H,
+  int('I'): I,
+  int('J'): J,
+  int('K'): K,
+  int('L'): Key.L,
+  int('M'): M,
+  int('N'): N,
+  int('O'): Key.O,
+  int('P'): P,
+  int('Q'): Q,
+  int('R'): Key.R,
+  int('S'): S,
+  int('T'): Key.T,
+  int('U'): U,
+  int('V'): V,
+  int('W'): W,
+  int('X'): Key.X,
+  int('Y'): Key.Y,
+  int('Z'): Key.Z,
+  VK_TAB: Tab,
+  VK_CAPITAL: CapsLock,
+  VK_LSHIFT: ShiftL,
+  VK_SHIFT: ShiftL,
+  VK_RSHIFT: ShiftR,
+  VK_LCONTROL: CtrlL,
+  VK_CONTROL: CtrlL,
+  VK_RCONTROL: CtrlR,
+  VK_LWIN: SuperL,
+  VK_RWIN: SuperR,
+  VK_LMENU: AltL,
+  VK_RMENU: AltR,
+  VK_SPACE: Space,
+  VK_RETURN: Enter,
+  VK_BACK: Backspace,
+  VK_OEM_4: LetterRow1Extra1,
+  VK_OEM_6: LetterRow1Extra2,
+  VK_OEM_5: LetterRow2Extra3,
+  VK_OEM_1: LetterRow2Extra1,
+  VK_OEM_7: LetterRow2Extra2,
+  VK_OEM_COMMA: LetterRow3Extra1,
+  VK_OEM_PERIOD: LetterRow3Extra2,
+  VK_OEM_2: LetterRow3Extra3,
+  VK_UP: Up,
+  VK_DOWN: Down,
+  VK_LEFT: Key.Left,
+  VK_RIGHT: Key.Right,
+  VK_PRIOR: PageUp,
+  VK_NEXT: PageDown,
+  VK_HOME: Home,
+  VK_END: End,
+  VK_INSERT: Insert,
+  VK_DELETE: Key.Delete,
+}.toTable
+
+# sorry, have to use module-global variable to capture windows events
+var currentEvents: seq[Event]
+
+template checkWin32Result*(call: untyped) =
+  let value = call
+  if value == 0:
+    raise
+      newException(Exception, "Win32 error: " & astToStr(call) & " returned " & $value)
+
+let
+  andCursorMask = [0xff]
+  xorCursorMask = [0x00]
+  invisibleCursor = CreateCursor(
+    0, 0, 0, 1, 1, pointer(addr andCursorMask), pointer(addr xorCursorMask)
+  )
+  defaultCursor = LoadCursor(0, IDC_ARROW)
+var currentCursor = defaultCursor
+
+proc mapLeftRightKeys(key: INT, lparam: LPARAM): INT =
+  case key
+  of VK_SHIFT:
+    MapVirtualKey(UINT((lParam and 0x00ff0000) shr 16), MAPVK_VSC_TO_VK_EX)
+  of VK_CONTROL:
+    if (lParam and 0x01000000) == 0: VK_LCONTROL else: VK_RCONTROL
+  of VK_MENU:
+    if (lParam and 0x01000000) == 0: VK_LMENU else: VK_RMENU
+  else:
+    key
+
+proc windowHandler(
+    hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
+): LRESULT {.stdcall.} =
+  case uMsg
+  of WM_DESTROY:
+    currentEvents.add(Event(eventType: Quit))
+  of WM_KEYDOWN, WM_SYSKEYDOWN:
+    let key = mapLeftRightKeys(INT(wParam), lParam)
+    currentEvents.add(
+      Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))
+    )
+  of WM_KEYUP, WM_SYSKEYUP:
+    let key = mapLeftRightKeys(INT(wParam), lParam)
+    currentEvents.add(
+      Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))
+    )
+  of WM_LBUTTONDOWN:
+    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse1))
+  of WM_LBUTTONUP:
+    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse1))
+  of WM_MBUTTONDOWN:
+    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse2))
+  of WM_MBUTTONUP:
+    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse2))
+  of WM_RBUTTONDOWN:
+    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse3))
+  of WM_RBUTTONUP:
+    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse3))
+  of WM_MOUSEWHEEL:
+    currentEvents.add(
+      Event(
+        eventType: MouseWheel,
+        amount: float32(GET_WHEEL_DELTA_WPARAM(wParam)) / WHEEL_DELTA,
+      )
+    )
+  of WM_SIZING:
+    currentEvents.add(Event(eventType: ResizedWindow))
+  of WM_SIZE:
+    if wParam == SIZE_MINIMIZED:
+      currentEvents.add(Event(eventType: MinimizedWindow))
+    elif wParam == SIZE_RESTORED:
+      currentEvents.add(Event(eventType: RestoredWindow))
+  of WM_SETFOCUS:
+    currentEvents.add(Event(eventType: GotFocus))
+  of WM_KILLFOCUS:
+    currentEvents.add(Event(eventType: LostFocus))
+  of WM_SETCURSOR:
+    if LOWORD(lParam) == HTCLIENT:
+      SetCursor(currentCursor)
+      return 1
+    else:
+      return DefWindowProc(hwnd, uMsg, wParam, lParam)
+  else:
+    return DefWindowProc(hwnd, uMsg, wParam, lParam)
+
+proc createWindow*(title: string): NativeWindow =
+  when not defined(release):
+    AllocConsole()
+    discard stdin.reopen("conIN$", fmRead)
+    discard stdout.reopen("conOUT$", fmWrite)
+    discard stderr.reopen("conOUT$", fmWrite)
+
+  result.hInstance = HINSTANCE(GetModuleHandle(nil))
+  var
+    windowClassName = T"EngineWindowClass"
+    windowName = T(title)
+    windowClass = WNDCLASSEX(
+      cbSize: UINT(WNDCLASSEX.sizeof),
+      lpfnWndProc: windowHandler,
+      hInstance: result.hInstance,
+      lpszClassName: windowClassName,
+      hcursor: currentCursor,
+    )
+
+  if (RegisterClassEx(addr(windowClass)) == 0):
+    raise newException(Exception, "Unable to register window class")
+
+  result.hwnd = CreateWindowEx(
+    DWORD(0),
+    windowClassName,
+    windowName,
+    DWORD(WS_OVERLAPPEDWINDOW),
+    CW_USEDEFAULT,
+    CW_USEDEFAULT,
+    CW_USEDEFAULT,
+    CW_USEDEFAULT,
+    HMENU(0),
+    HINSTANCE(0),
+    result.hInstance,
+    nil,
+  )
+
+  result.g_wpPrev.length = UINT(sizeof(WINDOWPLACEMENT))
+  discard result.hwnd.ShowWindow(SW_SHOW)
+  discard result.hwnd.SetForegroundWindow()
+  discard result.hwnd.SetFocus()
+
+proc destroyWindow*(window: NativeWindow) =
+  DestroyWindow(window.hwnd)
+
+proc setTitle*(window: NativeWindow, title: string) =
+  window.hwnd.SetWindowText(T(title))
+
+# inspired by the one and only, Raymond Chen
+# https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
+proc setFullscreen*(window: var NativeWindow, enable: bool) =
+  let dwStyle: DWORD = GetWindowLong(window.hwnd, GWL_STYLE)
+  if enable:
+    var mi = MONITORINFO(cbSize: DWORD(sizeof(MONITORINFO)))
+    if GetWindowPlacement(window.hwnd, addr window.g_wpPrev) and
+        GetMonitorInfo(
+          MonitorFromWindow(window.hwnd, MONITOR_DEFAULTTOPRIMARY), addr mi
+        ):
+      SetWindowLong(window.hwnd, GWL_STYLE, dwStyle and (not WS_OVERLAPPEDWINDOW))
+      SetWindowPos(
+        window.hwnd,
+        HWND_TOP,
+        mi.rcMonitor.left,
+        mi.rcMonitor.top,
+        mi.rcMonitor.right - mi.rcMonitor.left,
+        mi.rcMonitor.bottom - mi.rcMonitor.top,
+        SWP_NOOWNERZORDER or SWP_FRAMECHANGED,
+      )
+  else:
+    SetWindowLong(window.hwnd, GWL_STYLE, dwStyle or WS_OVERLAPPEDWINDOW)
+    SetWindowPlacement(window.hwnd, addr window.g_wpPrev)
+    SetWindowPos(
+      window.hwnd,
+      HWND(0),
+      0,
+      0,
+      0,
+      0,
+      SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_NOOWNERZORDER or SWP_FRAMECHANGED,
+    )
+
+proc showSystemCursor*(window: NativeWindow, value: bool) =
+  if value == true:
+    currentCursor = defaultCursor
+    SetCursor(currentCursor)
+  else:
+    currentCursor = invisibleCursor
+    SetCursor(currentCursor)
+
+proc destroy*(window: NativeWindow) =
+  discard
+
+proc size*(window: NativeWindow): Vec2i =
+  var rect: RECT
+  discard GetClientRect(window.hwnd, addr(rect))
+  vec2i(rect.right - rect.left, rect.bottom - rect.top)
+
+proc pendingEvents*(window: NativeWindow): seq[Event] =
+  # empty queue
+  var msg: MSG
+  # fill queue
+  while PeekMessage(addr(msg), window.hwnd, 0, 0, PM_REMOVE):
+    TranslateMessage(addr(msg))
+    DispatchMessage(addr(msg))
+  result = currentEvents
+  currentEvents.setLen(0)
+
+proc getMousePosition*(window: NativeWindow): Vec2i =
+  var p: POINT
+  discard GetCursorPos(addr(p))
+  discard window.hwnd.ScreenToClient(addr(p))
+  vec2i(p.x, p.y)
+
+proc setMousePosition*(window: NativeWindow, pos: Vec2i) =
+  var p = POINT(x: pos.x, y: pos.y)
+  discard window.hwnd.ClientToScreen(addr(p))
+  discard SetCursorPos(p.x, p.y)
+
+proc createNativeSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
+  assert instance.Valid
+  var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR(
+    sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
+    hinstance: cast[HINSTANCE](window.hinstance),
+    hwnd: cast[HWND](window.hwnd),
+  )
+  checkVkResult vkCreateWin32SurfaceKHR(
+    instance, addr(surfaceCreateInfo), nil, addr(result)
+  )
--- a/semicongine/rendering.nim	Thu Feb 27 20:17:57 2025 +0700
+++ b/semicongine/rendering.nim	Sat Mar 01 10:53:47 2025 +0700
@@ -22,9 +22,9 @@
 # believe me, this makes everything much, much easier
 
 when defined(windows):
-  include ./rendering/platform/windows
+  include ./platform/windows/rendering
 when defined(linux):
-  include ./rendering/platform/linux
+  include ./platform/linux/rendering
 
 import ../semicongine/rendering/memory
 import ../semicongine/rendering/renderer
--- a/semicongine/rendering/platform/linux.nim	Thu Feb 27 20:17:57 2025 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,306 +0,0 @@
-import std/options
-import std/tables
-
-import ../../thirdparty/x11/xlib
-import ../../thirdparty/x11/xutil
-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: Key.R,
-  28: T,
-  29: Key.Y,
-  30: U,
-  31: I,
-  32: Key.O,
-  33: P,
-  38: A,
-  39: S,
-  40: D,
-  41: Key.F,
-  42: Key.G,
-  43: H,
-  44: J,
-  45: K,
-  46: L,
-  52: Key.Z,
-  53: Key.X,
-  54: C,
-  55: V,
-  56: Key.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: Key.Left,
-  114: Key.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* {.hint[GlobalVar]: off.}: Atom # one internal use, not serious
-
-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,
-    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 destroyWindow*(window: NativeWindow) =
-  checkXlibResult XDestroyWindow(window.display, window.window)
-
-proc setTitle*(window: NativeWindow, title: string) =
-  discard 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): Vec2i =
-  var attribs: XWindowAttributes
-  discard XGetWindowAttributes(window.display, window.window, addr(attribs))
-  vec2i(attribs.width, 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 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): Vec2i =
-  var
-    root: x11.Window
-    win: x11.Window
-    rootX: cint
-    rootY: cint
-    winX: cint
-    winY: cint
-    mask: cuint
-  discard XQueryPointer(
-    window.display,
-    window.window,
-    addr(root),
-    addr(win),
-    addr(rootX),
-    addr(rootY),
-    addr(winX),
-    addr(winY),
-    addr(mask),
-  )
-  vec2i(winX, winY)
-
-proc setMousePosition*(window: NativeWindow, pos: Vec2i) =
-  discard XWarpPointer(
-    window.display,
-    default(x11.Window),
-    window.window,
-    0,
-    0,
-    0,
-    0,
-    pos.x.cint,
-    pos.y.cint,
-  )
-
-proc createNativeSurface(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
-  var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
-    sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
-    dpy: cast[ptr core.Display](window.display),
-    window: cast[core.Window](window.window),
-  )
-  checkVkResult vkCreateXlibSurfaceKHR(
-    instance, addr(surfaceCreateInfo), nil, addr(result)
-  )
--- a/semicongine/rendering/platform/windows.nim	Thu Feb 27 20:17:57 2025 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-import std/tables
-import std/options
-
-import ../../thirdparty/winim/winim/inc/[windef, winuser, wincon, winbase]
-import ../../thirdparty/winim/winim/[winstr, utils]
-
-const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_win32_surface"]
-
-const KeyTypeMap* = {
-  VK_ESCAPE: Key.Escape,
-  VK_F1: F1,
-  VK_F2: F2,
-  VK_F3: F3,
-  VK_F4: F4,
-  VK_F5: F5,
-  VK_F6: F6,
-  VK_F7: F7,
-  VK_F8: F8,
-  VK_F9: F9,
-  VK_F10: F10,
-  VK_F11: F11,
-  VK_F12: F12,
-  VK_OEM_3: NumberRowExtra1,
-  int('0'): `0`,
-  int('1'): `1`,
-  int('2'): `2`,
-  int('3'): `3`,
-  int('4'): `4`,
-  int('5'): `5`,
-  int('6'): `6`,
-  int('7'): `7`,
-  int('8'): `8`,
-  int('9'): `9`,
-  VK_OEM_MINUS: NumberRowExtra2,
-  VK_OEM_PLUS: NumberRowExtra3,
-  int('A'): A,
-  int('B'): Key.B,
-  int('C'): C,
-  int('D'): D,
-  int('E'): E,
-  int('F'): F,
-  int('G'): Key.G,
-  int('H'): H,
-  int('I'): I,
-  int('J'): J,
-  int('K'): K,
-  int('L'): Key.L,
-  int('M'): M,
-  int('N'): N,
-  int('O'): Key.O,
-  int('P'): P,
-  int('Q'): Q,
-  int('R'): Key.R,
-  int('S'): S,
-  int('T'): Key.T,
-  int('U'): U,
-  int('V'): V,
-  int('W'): W,
-  int('X'): Key.X,
-  int('Y'): Key.Y,
-  int('Z'): Key.Z,
-  VK_TAB: Tab,
-  VK_CAPITAL: CapsLock,
-  VK_LSHIFT: ShiftL,
-  VK_SHIFT: ShiftL,
-  VK_RSHIFT: ShiftR,
-  VK_LCONTROL: CtrlL,
-  VK_CONTROL: CtrlL,
-  VK_RCONTROL: CtrlR,
-  VK_LWIN: SuperL,
-  VK_RWIN: SuperR,
-  VK_LMENU: AltL,
-  VK_RMENU: AltR,
-  VK_SPACE: Space,
-  VK_RETURN: Enter,
-  VK_BACK: Backspace,
-  VK_OEM_4: LetterRow1Extra1,
-  VK_OEM_6: LetterRow1Extra2,
-  VK_OEM_5: LetterRow2Extra3,
-  VK_OEM_1: LetterRow2Extra1,
-  VK_OEM_7: LetterRow2Extra2,
-  VK_OEM_COMMA: LetterRow3Extra1,
-  VK_OEM_PERIOD: LetterRow3Extra2,
-  VK_OEM_2: LetterRow3Extra3,
-  VK_UP: Up,
-  VK_DOWN: Down,
-  VK_LEFT: Key.Left,
-  VK_RIGHT: Key.Right,
-  VK_PRIOR: PageUp,
-  VK_NEXT: PageDown,
-  VK_HOME: Home,
-  VK_END: End,
-  VK_INSERT: Insert,
-  VK_DELETE: Key.Delete,
-}.toTable
-
-# sorry, have to use module-global variable to capture windows events
-var currentEvents: seq[Event]
-
-template checkWin32Result*(call: untyped) =
-  let value = call
-  if value == 0:
-    raise
-      newException(Exception, "Win32 error: " & astToStr(call) & " returned " & $value)
-
-let
-  andCursorMask = [0xff]
-  xorCursorMask = [0x00]
-  invisibleCursor = CreateCursor(
-    0, 0, 0, 1, 1, pointer(addr andCursorMask), pointer(addr xorCursorMask)
-  )
-  defaultCursor = LoadCursor(0, IDC_ARROW)
-var currentCursor = defaultCursor
-
-proc mapLeftRightKeys(key: INT, lparam: LPARAM): INT =
-  case key
-  of VK_SHIFT:
-    MapVirtualKey(UINT((lParam and 0x00ff0000) shr 16), MAPVK_VSC_TO_VK_EX)
-  of VK_CONTROL:
-    if (lParam and 0x01000000) == 0: VK_LCONTROL else: VK_RCONTROL
-  of VK_MENU:
-    if (lParam and 0x01000000) == 0: VK_LMENU else: VK_RMENU
-  else:
-    key
-
-proc windowHandler(
-    hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
-): LRESULT {.stdcall.} =
-  case uMsg
-  of WM_DESTROY:
-    currentEvents.add(Event(eventType: Quit))
-  of WM_KEYDOWN, WM_SYSKEYDOWN:
-    let key = mapLeftRightKeys(INT(wParam), lParam)
-    currentEvents.add(
-      Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))
-    )
-  of WM_KEYUP, WM_SYSKEYUP:
-    let key = mapLeftRightKeys(INT(wParam), lParam)
-    currentEvents.add(
-      Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))
-    )
-  of WM_LBUTTONDOWN:
-    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse1))
-  of WM_LBUTTONUP:
-    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse1))
-  of WM_MBUTTONDOWN:
-    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse2))
-  of WM_MBUTTONUP:
-    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse2))
-  of WM_RBUTTONDOWN:
-    currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse3))
-  of WM_RBUTTONUP:
-    currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse3))
-  of WM_MOUSEWHEEL:
-    currentEvents.add(
-      Event(
-        eventType: MouseWheel,
-        amount: float32(GET_WHEEL_DELTA_WPARAM(wParam)) / WHEEL_DELTA,
-      )
-    )
-  of WM_SIZING:
-    currentEvents.add(Event(eventType: ResizedWindow))
-  of WM_SIZE:
-    if wParam == SIZE_MINIMIZED:
-      currentEvents.add(Event(eventType: MinimizedWindow))
-    elif wParam == SIZE_RESTORED:
-      currentEvents.add(Event(eventType: RestoredWindow))
-  of WM_SETFOCUS:
-    currentEvents.add(Event(eventType: GotFocus))
-  of WM_KILLFOCUS:
-    currentEvents.add(Event(eventType: LostFocus))
-  of WM_SETCURSOR:
-    if LOWORD(lParam) == HTCLIENT:
-      SetCursor(currentCursor)
-      return 1
-    else:
-      return DefWindowProc(hwnd, uMsg, wParam, lParam)
-  else:
-    return DefWindowProc(hwnd, uMsg, wParam, lParam)
-
-proc createWindow*(title: string): NativeWindow =
-  when not defined(release):
-    AllocConsole()
-    discard stdin.reopen("conIN$", fmRead)
-    discard stdout.reopen("conOUT$", fmWrite)
-    discard stderr.reopen("conOUT$", fmWrite)
-
-  result.hInstance = HINSTANCE(GetModuleHandle(nil))
-  var
-    windowClassName = T"EngineWindowClass"
-    windowName = T(title)
-    windowClass = WNDCLASSEX(
-      cbSize: UINT(WNDCLASSEX.sizeof),
-      lpfnWndProc: windowHandler,
-      hInstance: result.hInstance,
-      lpszClassName: windowClassName,
-      hcursor: currentCursor,
-    )
-
-  if (RegisterClassEx(addr(windowClass)) == 0):
-    raise newException(Exception, "Unable to register window class")
-
-  result.hwnd = CreateWindowEx(
-    DWORD(0),
-    windowClassName,
-    windowName,
-    DWORD(WS_OVERLAPPEDWINDOW),
-    CW_USEDEFAULT,
-    CW_USEDEFAULT,
-    CW_USEDEFAULT,
-    CW_USEDEFAULT,
-    HMENU(0),
-    HINSTANCE(0),
-    result.hInstance,
-    nil,
-  )
-
-  result.g_wpPrev.length = UINT(sizeof(WINDOWPLACEMENT))
-  discard result.hwnd.ShowWindow(SW_SHOW)
-  discard result.hwnd.SetForegroundWindow()
-  discard result.hwnd.SetFocus()
-
-proc destroyWindow*(window: NativeWindow) =
-  DestroyWindow(window.hwnd)
-
-proc setTitle*(window: NativeWindow, title: string) =
-  window.hwnd.SetWindowText(T(title))
-
-# inspired by the one and only, Raymond Chen
-# https://devblogs.microsoft.com/oldnewthing/20100412-00/?p=14353
-proc setFullscreen*(window: var NativeWindow, enable: bool) =
-  let dwStyle: DWORD = GetWindowLong(window.hwnd, GWL_STYLE)
-  if enable:
-    var mi = MONITORINFO(cbSize: DWORD(sizeof(MONITORINFO)))
-    if GetWindowPlacement(window.hwnd, addr window.g_wpPrev) and
-        GetMonitorInfo(
-          MonitorFromWindow(window.hwnd, MONITOR_DEFAULTTOPRIMARY), addr mi
-        ):
-      SetWindowLong(window.hwnd, GWL_STYLE, dwStyle and (not WS_OVERLAPPEDWINDOW))
-      SetWindowPos(
-        window.hwnd,
-        HWND_TOP,
-        mi.rcMonitor.left,
-        mi.rcMonitor.top,
-        mi.rcMonitor.right - mi.rcMonitor.left,
-        mi.rcMonitor.bottom - mi.rcMonitor.top,
-        SWP_NOOWNERZORDER or SWP_FRAMECHANGED,
-      )
-  else:
-    SetWindowLong(window.hwnd, GWL_STYLE, dwStyle or WS_OVERLAPPEDWINDOW)
-    SetWindowPlacement(window.hwnd, addr window.g_wpPrev)
-    SetWindowPos(
-      window.hwnd,
-      HWND(0),
-      0,
-      0,
-      0,
-      0,
-      SWP_NOMOVE or SWP_NOSIZE or SWP_NOZORDER or SWP_NOOWNERZORDER or SWP_FRAMECHANGED,
-    )
-
-proc showSystemCursor*(window: NativeWindow, value: bool) =
-  if value == true:
-    currentCursor = defaultCursor
-    SetCursor(currentCursor)
-  else:
-    currentCursor = invisibleCursor
-    SetCursor(currentCursor)
-
-proc destroy*(window: NativeWindow) =
-  discard
-
-proc size*(window: NativeWindow): Vec2i =
-  var rect: RECT
-  discard GetClientRect(window.hwnd, addr(rect))
-  vec2i(rect.right - rect.left, rect.bottom - rect.top)
-
-proc pendingEvents*(window: NativeWindow): seq[Event] =
-  # empty queue
-  var msg: MSG
-  # fill queue
-  while PeekMessage(addr(msg), window.hwnd, 0, 0, PM_REMOVE):
-    TranslateMessage(addr(msg))
-    DispatchMessage(addr(msg))
-  result = currentEvents
-  currentEvents.setLen(0)
-
-proc getMousePosition*(window: NativeWindow): Vec2i =
-  var p: POINT
-  discard GetCursorPos(addr(p))
-  discard window.hwnd.ScreenToClient(addr(p))
-  vec2i(p.x, p.y)
-
-proc setMousePosition*(window: NativeWindow, pos: Vec2i) =
-  var p = POINT(x: pos.x, y: pos.y)
-  discard window.hwnd.ClientToScreen(addr(p))
-  discard SetCursorPos(p.x, p.y)
-
-proc createNativeSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
-  assert instance.Valid
-  var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR(
-    sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
-    hinstance: cast[HINSTANCE](window.hinstance),
-    hwnd: cast[HWND](window.hwnd),
-  )
-  checkVkResult vkCreateWin32SurfaceKHR(
-    instance, addr(surfaceCreateInfo), nil, addr(result)
-  )