Mercurial > games > semicongine
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)) |