comparison 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
comparison
equal deleted inserted replaced
1223:55896320c8bf 1224:a3fa15c25026
1 import ../../thirdparty/x11/xlib
2 import ../../thirdparty/x11/xutil
3 import ../../thirdparty/x11/keysym
4 import ../../thirdparty/x11/x as x11
5 import ../../thirdparty/x11/xkblib
6
7 const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_xlib_surface"]
8
9 # got values (keycodes) from xev
10 const KeyTypeMap = {
11 9: Escape, 67: F1, 68: F2, 69: F3, 70: F4, 71: F5, 72: F6, 73: F7, 74: F8,
12 75: F9, 76: F10, 95: F11, 96: F12,
13 49: NumberRowExtra1, 10: `1`, 11: `2`, 12: `3`, 13: `4`, 14: `5`, 15: `6`,
14 16: `7`, 17: `8`, 18: `9`, 19: `0`, 20: NumberRowExtra2, 21: NumberRowExtra3,
15 24: Q, 25: W, 26: E, 27: R, 28: T, 29: Y, 30: U, 31: I, 32: O, 33: P, 38: A,
16 39: S, 40: D, 41: Key.F, 42: G, 43: H, 44: J, 45: K, 46: L, 52: Z, 53: X,
17 54: C, 55: V, 56: B, 57: N, 58: M,
18
19 23: Tab, 66: CapsLock, 50: ShiftL, 62: ShiftR, 37: CtrlL, 105: CtrlR,
20 133: SuperL, 134: SuperR,
21 64: AltL, 108: AltR,
22 65: Space, 36: Enter, 22: Backspace,
23 34: LetterRow1Extra1, 35: LetterRow1Extra2,
24 47: LetterRow2Extra1, 48: LetterRow2Extra2, 51: LetterRow2Extra3,
25 59: LetterRow3Extra1, 60: LetterRow3Extra2, 61: LetterRow3Extra3,
26 111: Up, 116: Down, 113: Left, 114: Right,
27 112: PageUp, 117: PageDown, 110: Home, 115: End, 118: Insert, 119: Delete,
28 107: PrintScreen, 78: ScrollLock, 127: Pause,
29 }.toTable
30
31 const MouseButtonTypeMap = {
32 x11.Button1: MouseButton.Mouse1,
33 x11.Button2: MouseButton.Mouse2,
34 x11.Button3: MouseButton.Mouse3,
35 }.toTable
36
37 var deleteMessage*: Atom
38
39 type
40 NativeWindow* = object
41 display*: ptr xlib.Display
42 window*: x11.Window
43 emptyCursor: Cursor
44
45 template checkXlibResult(call: untyped) =
46 let value = call
47 if value == 0:
48 raise newException(Exception, "Xlib error: " & astToStr(call) &
49 " returned " & $value)
50
51 proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} =
52 logging.error &"Xlib: {event[]}"
53
54 proc CreateWindow*(title: string): NativeWindow =
55 checkXlibResult XInitThreads()
56 let display = XOpenDisplay(nil)
57 if display == nil:
58 quit "Failed to open display"
59 discard XSetErrorHandler(XErrorLogger)
60
61 let rootWindow = display.XDefaultRootWindow()
62 discard display.XkbSetDetectableAutoRepeat(true, nil)
63 var
64 attribs: XWindowAttributes
65 width = cuint(800)
66 height = cuint(600)
67 checkXlibResult display.XGetWindowAttributes(rootWindow, addr(attribs))
68
69 var attrs = XSetWindowAttributes()
70 let window = XCreateWindow(
71 display,
72 rootWindow,
73 (attribs.width - cint(width)) div 2, (attribs.height - cint(height)) div 2,
74 width, height,
75 0,
76 CopyFromParent,
77 InputOutput,
78 cast[PVisual](CopyFromParent),
79 0, # CWOverrideRedirect,
80 addr attrs,
81 # foregroundColor, backgroundColor
82 )
83 checkXlibResult XSetStandardProperties(display, window, title, "window", 0, nil, 0, nil)
84 checkXlibResult XSelectInput(display, window, PointerMotionMask or ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask or ExposureMask)
85 checkXlibResult XMapWindow(display, window)
86
87 deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false))
88 checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1)
89
90 var data = "\0".cstring
91 var pixmap = display.XCreateBitmapFromData(window, data, 1, 1)
92 var color: XColor
93 var empty_cursor = display.XCreatePixmapCursor(pixmap, pixmap, addr(color), addr(color), 0, 0)
94 checkXlibResult display.XFreePixmap(pixmap)
95 return NativeWindow(display: display, window: window, emptyCursor: empty_cursor)
96
97 proc SetTitle*(window: NativeWindow, title: string) =
98 checkXlibResult XSetStandardProperties(window.display, window.window, title, "window", 0, nil, 0, nil)
99
100 proc Fullscreen*(window: var NativeWindow, enable: bool) =
101 var
102 wm_state = window.display.XInternAtom("_NET_WM_STATE", 0)
103 wm_fullscreen = window.display.XInternAtom("_NET_WM_STATE_FULLSCREEN", 0)
104 var
105 xev: XEvent
106 xev.xclient = XClientMessageEvent(
107 message_type: wm_state,
108 format: 32,
109 window: window.window,
110 data: XClientMessageData(
111 l: [
112 int(not enable) xor 1,
113 clong(wm_fullscreen),
114 0,
115 0,
116 0
117 ]
118 )
119 )
120 xev.theType = ClientMessage
121
122 checkXlibResult window.display.XSendEvent(
123 window.display.DefaultRootWindow(),
124 0,
125 SubstructureRedirectMask or SubstructureNotifyMask,
126 addr xev
127 )
128 checkXlibResult window.display.XFlush()
129
130 proc HideSystemCursor*(window: NativeWindow) =
131 checkXlibResult XDefineCursor(window.display, window.window, window.emptyCursor)
132 checkXlibResult window.display.XFlush()
133
134 proc ShowSystemCursor*(window: NativeWindow) =
135 checkXlibResult XUndefineCursor(window.display, window.window)
136 checkXlibResult window.display.XFlush()
137
138 proc Destroy*(window: NativeWindow) =
139 checkXlibResult window.display.XFreeCursor(window.emptyCursor)
140 checkXlibResult window.display.XDestroyWindow(window.window)
141 discard window.display.XCloseDisplay() # always returns 0
142
143 proc Size*(window: NativeWindow): (int, int) =
144 var attribs: XWindowAttributes
145 checkXlibResult XGetWindowAttributes(window.display, window.window, addr(attribs))
146 return (int(attribs.width), int(attribs.height))
147
148 proc PendingEvents*(window: NativeWindow): seq[Event] =
149 var event: XEvent
150 while window.display.XPending() > 0:
151 discard window.display.XNextEvent(addr(event))
152 case event.theType
153 of ClientMessage:
154 if cast[Atom](event.xclient.data.l[0]) == deleteMessage:
155 result.add(Event(eventType: Quit))
156 of KeyPress:
157 let keyevent = cast[PXKeyEvent](addr(event))
158 let xkey = int(keyevent.keycode)
159 result.add Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
160 of KeyRelease:
161 let keyevent = cast[PXKeyEvent](addr(event))
162 let xkey = int(keyevent.keycode)
163 result.add Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN))
164 of ButtonPress:
165 let button = int(cast[PXButtonEvent](addr(event)).button)
166 if button == Button4:
167 result.add Event(eventType: MouseWheel, amount: 1'f32)
168 elif button == Button5:
169 result.add Event(eventType: MouseWheel, amount: -1'f32)
170 else:
171 result.add Event(eventType: MousePressed, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN))
172 of ButtonRelease:
173 let button = int(cast[PXButtonEvent](addr(event)).button)
174 result.add Event(eventType: MouseReleased, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN))
175 of MotionNotify:
176 let motion = cast[PXMotionEvent](addr(event))
177 result.add Event(eventType: MouseMoved, x: motion.x, y: motion.y)
178 of ConfigureNotify, Expose:
179 result.add Event(eventType: ResizedWindow)
180 else:
181 discard
182
183
184 proc GetMousePosition*(window: NativeWindow): Option[Vec2f] =
185 var
186 root: x11.Window
187 win: x11.Window
188 rootX: cint
189 rootY: cint
190 winX: cint
191 winY: cint
192 mask: cuint
193 onscreen = XQueryPointer(
194 window.display,
195 window.window,
196 addr(root),
197 addr(win),
198 addr(rootX),
199 addr(rootY),
200 addr(winX),
201 addr(winY),
202 addr(mask),
203 )
204 if onscreen != 0:
205 result = some(Vec2f([float32(winX), float32(winY)]))
206
207
208 proc CreateNativeSurface(instance: VkInstance, window: NativeWindow): VkSurfaceKHR =
209 var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
210 sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
211 dpy: cast[ptr Display](window.display),
212 window: cast[Window](window.window),
213 )
214 checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result))