changeset 1454:eaad5e0443f8 default tip main

add: support for text-input, windows support still missing
author sam <sam@basx.dev>
date Fri, 21 Mar 2025 18:16:24 +0700
parents 60a709362440
children
files semicongine/core/types.nim semicongine/input.nim semicongine/platform/linux/rendering.nim semicongine/platform/linux/types.nim semicongine/platform/windows/rendering.nim
diffstat 5 files changed, 44 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/core/types.nim	Sun Mar 16 22:33:35 2025 +0700
+++ b/semicongine/core/types.nim	Fri Mar 21 18:16:24 2025 +0700
@@ -300,6 +300,7 @@
     case eventType*: EventType
     of KeyPressed, KeyReleased:
       key*: Key
+      char*: Rune
     of MousePressed, MouseReleased:
       button*: MouseButton
     of MouseWheel:
@@ -466,6 +467,7 @@
     windowIsMinimized*: bool = false
     lockMouse*: bool = false
     hasFocus*: bool = false
+    characterInput*: Rune
 
   ActionMap* = object
     keyActions*: Table[string, set[Key]]
--- a/semicongine/input.nim	Sun Mar 16 22:33:35 2025 +0700
+++ b/semicongine/input.nim	Fri Mar 21 18:16:24 2025 +0700
@@ -1,4 +1,5 @@
 import std/strutils
+import std/unicode
 import std/tables
 
 import ./core
@@ -14,6 +15,7 @@
   engine().input.mouseWheel = 0
   engine().input.mouseMove = vec2i(0, 0)
   engine().input.windowWasResized = false
+  engine().input.characterInput = default(Rune)
 
   let newMousePos = getMousePosition(engine().vulkan.window)
   engine().input.mouseMove = newMousePos - engine().input.mousePosition
@@ -33,6 +35,9 @@
     of KeyPressed:
       engine().input.keyWasPressed.incl event.key
       engine().input.keyIsDown.incl event.key
+      # exclude control characters for text input
+      if not (0x00'i32 <= int32(event.char) and int32(event.char) <= 0x1F'i32):
+        engine().input.characterInput = event.char
     of KeyReleased:
       engine().input.keyWasReleased.incl event.key
       engine().input.keyIsDown.excl event.key
@@ -67,6 +72,9 @@
 proc keyWasReleased*(key: Key): bool =
   key in engine().input.keyWasReleased
 
+proc characterInput*(): Rune =
+  engine().input.characterInput
+
 proc mouseIsDown*(button: MouseButton): bool =
   button in engine().input.mouseIsDown
 
--- a/semicongine/platform/linux/rendering.nim	Sun Mar 16 22:33:35 2025 +0700
+++ b/semicongine/platform/linux/rendering.nim	Fri Mar 21 18:16:24 2025 +0700
@@ -1,4 +1,5 @@
 import std/options
+import std/unicode
 import std/tables
 
 import ../../thirdparty/x11/xlib
@@ -165,7 +166,14 @@
   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)
+
+  # get an input context, to allow encoding of key-events to characters
+  let im = XOpenIM(display, nil, nil, nil)
+  let ic = XCreateIC(
+    im, XNInputStyle, XIMPreeditNothing or XIMStatusNothing, XNClientWindow, window, nil
+  )
+  return
+    NativeWindow(display: display, window: window, emptyCursor: empty_cursor, ic: ic)
 
 proc destroyWindow*(window: NativeWindow) =
   checkXlibResult XDestroyWindow(window.display, window.window)
@@ -214,6 +222,9 @@
   discard XGetWindowAttributes(window.display, window.window, addr(attribs))
   vec2i(attribs.width, attribs.height)
 
+# buffer to save utf8-data from keyboard events
+var unicodeData = newString(16)
+
 proc pendingEvents*(window: NativeWindow): seq[Event] =
   var event: XEvent
   while window.display.XPending() > 0:
@@ -225,9 +236,17 @@
     of KeyPress:
       let keyevent = cast[PXKeyEvent](addr(event))
       let xkey = int(keyevent.keycode)
-      result.add Event(
-        eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN)
+      var e =
+        Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
+      let len = Xutf8LookupString(
+        window.ic, keyevent, unicodeData, unicodeData.len.cint, nil, nil
       )
+      if len > 0:
+        unicodeData.setLen(len)
+        for r in unicodeData.runes():
+          e.char = r
+          break
+      result.add e
     of KeyRelease:
       let keyevent = cast[PXKeyEvent](addr(event))
       let xkey = int(keyevent.keycode)
--- a/semicongine/platform/linux/types.nim	Sun Mar 16 22:33:35 2025 +0700
+++ b/semicongine/platform/linux/types.nim	Fri Mar 21 18:16:24 2025 +0700
@@ -5,6 +5,7 @@
   display*: ptr xlib.Display
   window*: x11.Window
   emptyCursor*: Cursor
+  ic*: XIC
 
 # alsa API
 type
--- a/semicongine/platform/windows/rendering.nim	Sun Mar 16 22:33:35 2025 +0700
+++ b/semicongine/platform/windows/rendering.nim	Fri Mar 21 18:16:24 2025 +0700
@@ -134,6 +134,17 @@
     currentEvents.add(
       Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))
     )
+    #[
+    proc ToUnicodeEx*(
+      wVirtKey: UINT,
+      wScanCode: UINT,
+      lpKeyState: ptr BYTE,
+      pwszBuff: LPWSTR,
+      cchBuff: int32,
+      wFlags: UINT,
+      dwhkl: HKL,
+    ): int32 {.winapi, stdcall, dynlib: "user32", importc.}
+    ]#
   of WM_KEYUP, WM_SYSKEYUP:
     let key = mapLeftRightKeys(INT(wParam), lParam)
     currentEvents.add(