v4 / vlib / sokol / sapp / sapp_x11_linux.v
2036 lines · 1939 sloc · 62.24 KB · badd3466fd7ddc8a543badb4ecda4d999770ff91
Raw
1module sapp
2
3// X11 backend implementation for sokol_app.
4// Ports the C _sapp_x11_* functions from sokol_app.h to V.
5
6const x11_max_keycodes = 256
7
8// Keysym-to-unicode lookup table (from GLFW's xkb_unicode.c)
9struct X11Codepair {
10 keysym u16
11 ucs u16
12}
13
14const x11_keysymtab = [
15 X11Codepair{0x01a1, 0x0104},
16 X11Codepair{0x01a2, 0x02d8},
17 X11Codepair{0x01a3, 0x0141},
18 X11Codepair{0x01a5, 0x013d},
19 X11Codepair{0x01a6, 0x015a},
20 X11Codepair{0x01a9, 0x0160},
21 X11Codepair{0x01aa, 0x015e},
22 X11Codepair{0x01ab, 0x0164},
23 X11Codepair{0x01ac, 0x0179},
24 X11Codepair{0x01ae, 0x017d},
25 X11Codepair{0x01af, 0x017b},
26 X11Codepair{0x01b1, 0x0105},
27 X11Codepair{0x01b2, 0x02db},
28 X11Codepair{0x01b3, 0x0142},
29 X11Codepair{0x01b5, 0x013e},
30 X11Codepair{0x01b6, 0x015b},
31 X11Codepair{0x01b7, 0x02c7},
32 X11Codepair{0x01b9, 0x0161},
33 X11Codepair{0x01ba, 0x015f},
34 X11Codepair{0x01bb, 0x0165},
35 X11Codepair{0x01bc, 0x017a},
36 X11Codepair{0x01bd, 0x02dd},
37 X11Codepair{0x01be, 0x017e},
38 X11Codepair{0x01bf, 0x017c},
39 X11Codepair{0x01c0, 0x0154},
40 X11Codepair{0x01c3, 0x0102},
41 X11Codepair{0x01c5, 0x0139},
42 X11Codepair{0x01c6, 0x0106},
43 X11Codepair{0x01c8, 0x010c},
44 X11Codepair{0x01ca, 0x0118},
45 X11Codepair{0x01cc, 0x011a},
46 X11Codepair{0x01cf, 0x010e},
47 X11Codepair{0x01d0, 0x0110},
48 X11Codepair{0x01d1, 0x0143},
49 X11Codepair{0x01d2, 0x0147},
50 X11Codepair{0x01d5, 0x0150},
51 X11Codepair{0x01d8, 0x0158},
52 X11Codepair{0x01d9, 0x016e},
53 X11Codepair{0x01db, 0x0170},
54 X11Codepair{0x01de, 0x0162},
55 X11Codepair{0x01e0, 0x0155},
56 X11Codepair{0x01e3, 0x0103},
57 X11Codepair{0x01e5, 0x013a},
58 X11Codepair{0x01e6, 0x0107},
59 X11Codepair{0x01e8, 0x010d},
60 X11Codepair{0x01ea, 0x0119},
61 X11Codepair{0x01ec, 0x011b},
62 X11Codepair{0x01ef, 0x010f},
63 X11Codepair{0x01f0, 0x0111},
64 X11Codepair{0x01f1, 0x0144},
65 X11Codepair{0x01f2, 0x0148},
66 X11Codepair{0x01f5, 0x0151},
67 X11Codepair{0x01f8, 0x0159},
68 X11Codepair{0x01f9, 0x016f},
69 X11Codepair{0x01fb, 0x0171},
70 X11Codepair{0x01fe, 0x0163},
71 X11Codepair{0x01ff, 0x02d9},
72 X11Codepair{0x02a1, 0x0126},
73 X11Codepair{0x02a6, 0x0124},
74 X11Codepair{0x02a9, 0x0130},
75 X11Codepair{0x02ab, 0x011e},
76 X11Codepair{0x02ac, 0x0134},
77 X11Codepair{0x02b1, 0x0127},
78 X11Codepair{0x02b6, 0x0125},
79 X11Codepair{0x02b9, 0x0131},
80 X11Codepair{0x02bb, 0x011f},
81 X11Codepair{0x02bc, 0x0135},
82 X11Codepair{0x02c5, 0x010a},
83 X11Codepair{0x02c6, 0x0108},
84 X11Codepair{0x02d5, 0x0120},
85 X11Codepair{0x02d8, 0x011c},
86 X11Codepair{0x02dd, 0x016c},
87 X11Codepair{0x02de, 0x015c},
88 X11Codepair{0x02e5, 0x010b},
89 X11Codepair{0x02e6, 0x0109},
90 X11Codepair{0x02f5, 0x0121},
91 X11Codepair{0x02f8, 0x011d},
92 X11Codepair{0x02fd, 0x016d},
93 X11Codepair{0x02fe, 0x015d},
94 X11Codepair{0x03a2, 0x0138},
95 X11Codepair{0x03a3, 0x0156},
96 X11Codepair{0x03a5, 0x0128},
97 X11Codepair{0x03a6, 0x013b},
98 X11Codepair{0x03aa, 0x0112},
99 X11Codepair{0x03ab, 0x0122},
100 X11Codepair{0x03ac, 0x0166},
101 X11Codepair{0x03b3, 0x0157},
102 X11Codepair{0x03b5, 0x0129},
103 X11Codepair{0x03b6, 0x013c},
104 X11Codepair{0x03ba, 0x0113},
105 X11Codepair{0x03bb, 0x0123},
106 X11Codepair{0x03bc, 0x0167},
107 X11Codepair{0x03bd, 0x014a},
108 X11Codepair{0x03bf, 0x014b},
109 X11Codepair{0x03c0, 0x0100},
110 X11Codepair{0x03c7, 0x012e},
111 X11Codepair{0x03cc, 0x0116},
112 X11Codepair{0x03cf, 0x012a},
113 X11Codepair{0x03d1, 0x0145},
114 X11Codepair{0x03d2, 0x014c},
115 X11Codepair{0x03d3, 0x0136},
116 X11Codepair{0x03d9, 0x0172},
117 X11Codepair{0x03dd, 0x0168},
118 X11Codepair{0x03de, 0x016a},
119 X11Codepair{0x03e0, 0x0101},
120 X11Codepair{0x03e7, 0x012f},
121 X11Codepair{0x03ec, 0x0117},
122 X11Codepair{0x03ef, 0x012b},
123 X11Codepair{0x03f1, 0x0146},
124 X11Codepair{0x03f2, 0x014d},
125 X11Codepair{0x03f3, 0x0137},
126 X11Codepair{0x03f9, 0x0173},
127 X11Codepair{0x03fd, 0x0169},
128 X11Codepair{0x03fe, 0x016b},
129 X11Codepair{0x047e, 0x203e},
130 X11Codepair{0x04a1, 0x3002},
131 X11Codepair{0x04a2, 0x300c},
132 X11Codepair{0x04a3, 0x300d},
133 X11Codepair{0x04a4, 0x3001},
134 X11Codepair{0x04a5, 0x30fb},
135 X11Codepair{0x04a6, 0x30f2},
136 X11Codepair{0x04a7, 0x30a1},
137 X11Codepair{0x04a8, 0x30a3},
138 X11Codepair{0x04a9, 0x30a5},
139 X11Codepair{0x04aa, 0x30a7},
140 X11Codepair{0x04ab, 0x30a9},
141 X11Codepair{0x04ac, 0x30e3},
142 X11Codepair{0x04ad, 0x30e5},
143 X11Codepair{0x04ae, 0x30e7},
144 X11Codepair{0x04af, 0x30c3},
145 X11Codepair{0x04b0, 0x30fc},
146 X11Codepair{0x04b1, 0x30a2},
147 X11Codepair{0x04b2, 0x30a4},
148 X11Codepair{0x04b3, 0x30a6},
149 X11Codepair{0x04b4, 0x30a8},
150 X11Codepair{0x04b5, 0x30aa},
151 X11Codepair{0x04b6, 0x30ab},
152 X11Codepair{0x04b7, 0x30ad},
153 X11Codepair{0x04b8, 0x30af},
154 X11Codepair{0x04b9, 0x30b1},
155 X11Codepair{0x04ba, 0x30b3},
156 X11Codepair{0x04bb, 0x30b5},
157 X11Codepair{0x04bc, 0x30b7},
158 X11Codepair{0x04bd, 0x30b9},
159 X11Codepair{0x04be, 0x30bb},
160 X11Codepair{0x04bf, 0x30bd},
161 X11Codepair{0x04c0, 0x30bf},
162 X11Codepair{0x04c1, 0x30c1},
163 X11Codepair{0x04c2, 0x30c4},
164 X11Codepair{0x04c3, 0x30c6},
165 X11Codepair{0x04c4, 0x30c8},
166 X11Codepair{0x04c5, 0x30ca},
167 X11Codepair{0x04c6, 0x30cb},
168 X11Codepair{0x04c7, 0x30cc},
169 X11Codepair{0x04c8, 0x30cd},
170 X11Codepair{0x04c9, 0x30ce},
171 X11Codepair{0x04ca, 0x30cf},
172 X11Codepair{0x04cb, 0x30d2},
173 X11Codepair{0x04cc, 0x30d5},
174 X11Codepair{0x04cd, 0x30d8},
175 X11Codepair{0x04ce, 0x30db},
176 X11Codepair{0x04cf, 0x30de},
177 X11Codepair{0x04d0, 0x30df},
178 X11Codepair{0x04d1, 0x30e0},
179 X11Codepair{0x04d2, 0x30e1},
180 X11Codepair{0x04d3, 0x30e2},
181 X11Codepair{0x04d4, 0x30e4},
182 X11Codepair{0x04d5, 0x30e6},
183 X11Codepair{0x04d6, 0x30e8},
184 X11Codepair{0x04d7, 0x30e9},
185 X11Codepair{0x04d8, 0x30ea},
186 X11Codepair{0x04d9, 0x30eb},
187 X11Codepair{0x04da, 0x30ec},
188 X11Codepair{0x04db, 0x30ed},
189 X11Codepair{0x04dc, 0x30ef},
190 X11Codepair{0x04dd, 0x30f3},
191 X11Codepair{0x04de, 0x309b},
192 X11Codepair{0x04df, 0x309c},
193 X11Codepair{0x05ac, 0x060c},
194 X11Codepair{0x05bb, 0x061b},
195 X11Codepair{0x05bf, 0x061f},
196 X11Codepair{0x05c1, 0x0621},
197 X11Codepair{0x05c2, 0x0622},
198 X11Codepair{0x05c3, 0x0623},
199 X11Codepair{0x05c4, 0x0624},
200 X11Codepair{0x05c5, 0x0625},
201 X11Codepair{0x05c6, 0x0626},
202 X11Codepair{0x05c7, 0x0627},
203 X11Codepair{0x05c8, 0x0628},
204 X11Codepair{0x05c9, 0x0629},
205 X11Codepair{0x05ca, 0x062a},
206 X11Codepair{0x05cb, 0x062b},
207 X11Codepair{0x05cc, 0x062c},
208 X11Codepair{0x05cd, 0x062d},
209 X11Codepair{0x05ce, 0x062e},
210 X11Codepair{0x05cf, 0x062f},
211 X11Codepair{0x05d0, 0x0630},
212 X11Codepair{0x05d1, 0x0631},
213 X11Codepair{0x05d2, 0x0632},
214 X11Codepair{0x05d3, 0x0633},
215 X11Codepair{0x05d4, 0x0634},
216 X11Codepair{0x05d5, 0x0635},
217 X11Codepair{0x05d6, 0x0636},
218 X11Codepair{0x05d7, 0x0637},
219 X11Codepair{0x05d8, 0x0638},
220 X11Codepair{0x05d9, 0x0639},
221 X11Codepair{0x05da, 0x063a},
222 X11Codepair{0x05e0, 0x0640},
223 X11Codepair{0x05e1, 0x0641},
224 X11Codepair{0x05e2, 0x0642},
225 X11Codepair{0x05e3, 0x0643},
226 X11Codepair{0x05e4, 0x0644},
227 X11Codepair{0x05e5, 0x0645},
228 X11Codepair{0x05e6, 0x0646},
229 X11Codepair{0x05e7, 0x0647},
230 X11Codepair{0x05e8, 0x0648},
231 X11Codepair{0x05e9, 0x0649},
232 X11Codepair{0x05ea, 0x064a},
233 X11Codepair{0x05eb, 0x064b},
234 X11Codepair{0x05ec, 0x064c},
235 X11Codepair{0x05ed, 0x064d},
236 X11Codepair{0x05ee, 0x064e},
237 X11Codepair{0x05ef, 0x064f},
238 X11Codepair{0x05f0, 0x0650},
239 X11Codepair{0x05f1, 0x0651},
240 X11Codepair{0x05f2, 0x0652},
241 X11Codepair{0x06a1, 0x0452},
242 X11Codepair{0x06a2, 0x0453},
243 X11Codepair{0x06a3, 0x0451},
244 X11Codepair{0x06a4, 0x0454},
245 X11Codepair{0x06a5, 0x0455},
246 X11Codepair{0x06a6, 0x0456},
247 X11Codepair{0x06a7, 0x0457},
248 X11Codepair{0x06a8, 0x0458},
249 X11Codepair{0x06a9, 0x0459},
250 X11Codepair{0x06aa, 0x045a},
251 X11Codepair{0x06ab, 0x045b},
252 X11Codepair{0x06ac, 0x045c},
253 X11Codepair{0x06ae, 0x045e},
254 X11Codepair{0x06af, 0x045f},
255 X11Codepair{0x06b0, 0x2116},
256 X11Codepair{0x06b1, 0x0402},
257 X11Codepair{0x06b2, 0x0403},
258 X11Codepair{0x06b3, 0x0401},
259 X11Codepair{0x06b4, 0x0404},
260 X11Codepair{0x06b5, 0x0405},
261 X11Codepair{0x06b6, 0x0406},
262 X11Codepair{0x06b7, 0x0407},
263 X11Codepair{0x06b8, 0x0408},
264 X11Codepair{0x06b9, 0x0409},
265 X11Codepair{0x06ba, 0x040a},
266 X11Codepair{0x06bb, 0x040b},
267 X11Codepair{0x06bc, 0x040c},
268 X11Codepair{0x06be, 0x040e},
269 X11Codepair{0x06bf, 0x040f},
270 X11Codepair{0x06c0, 0x044e},
271 X11Codepair{0x06c1, 0x0430},
272 X11Codepair{0x06c2, 0x0431},
273 X11Codepair{0x06c3, 0x0446},
274 X11Codepair{0x06c4, 0x0434},
275 X11Codepair{0x06c5, 0x0435},
276 X11Codepair{0x06c6, 0x0444},
277 X11Codepair{0x06c7, 0x0433},
278 X11Codepair{0x06c8, 0x0445},
279 X11Codepair{0x06c9, 0x0438},
280 X11Codepair{0x06ca, 0x0439},
281 X11Codepair{0x06cb, 0x043a},
282 X11Codepair{0x06cc, 0x043b},
283 X11Codepair{0x06cd, 0x043c},
284 X11Codepair{0x06ce, 0x043d},
285 X11Codepair{0x06cf, 0x043e},
286 X11Codepair{0x06d0, 0x043f},
287 X11Codepair{0x06d1, 0x044f},
288 X11Codepair{0x06d2, 0x0440},
289 X11Codepair{0x06d3, 0x0441},
290 X11Codepair{0x06d4, 0x0442},
291 X11Codepair{0x06d5, 0x0443},
292 X11Codepair{0x06d6, 0x0436},
293 X11Codepair{0x06d7, 0x0432},
294 X11Codepair{0x06d8, 0x044c},
295 X11Codepair{0x06d9, 0x044b},
296 X11Codepair{0x06da, 0x0437},
297 X11Codepair{0x06db, 0x0448},
298 X11Codepair{0x06dc, 0x044d},
299 X11Codepair{0x06dd, 0x0449},
300 X11Codepair{0x06de, 0x0447},
301 X11Codepair{0x06df, 0x044a},
302 X11Codepair{0x06e0, 0x042e},
303 X11Codepair{0x06e1, 0x0410},
304 X11Codepair{0x06e2, 0x0411},
305 X11Codepair{0x06e3, 0x0426},
306 X11Codepair{0x06e4, 0x0414},
307 X11Codepair{0x06e5, 0x0415},
308 X11Codepair{0x06e6, 0x0424},
309 X11Codepair{0x06e7, 0x0413},
310 X11Codepair{0x06e8, 0x0425},
311 X11Codepair{0x06e9, 0x0418},
312 X11Codepair{0x06ea, 0x0419},
313 X11Codepair{0x06eb, 0x041a},
314 X11Codepair{0x06ec, 0x041b},
315 X11Codepair{0x06ed, 0x041c},
316 X11Codepair{0x06ee, 0x041d},
317 X11Codepair{0x06ef, 0x041e},
318 X11Codepair{0x06f0, 0x041f},
319 X11Codepair{0x06f1, 0x042f},
320 X11Codepair{0x06f2, 0x0420},
321 X11Codepair{0x06f3, 0x0421},
322 X11Codepair{0x06f4, 0x0422},
323 X11Codepair{0x06f5, 0x0423},
324 X11Codepair{0x06f6, 0x0416},
325 X11Codepair{0x06f7, 0x0412},
326 X11Codepair{0x06f8, 0x042c},
327 X11Codepair{0x06f9, 0x042b},
328 X11Codepair{0x06fa, 0x0417},
329 X11Codepair{0x06fb, 0x0428},
330 X11Codepair{0x06fc, 0x042d},
331 X11Codepair{0x06fd, 0x0429},
332 X11Codepair{0x06fe, 0x0427},
333 X11Codepair{0x06ff, 0x042a},
334 X11Codepair{0x07a1, 0x0386},
335 X11Codepair{0x07a2, 0x0388},
336 X11Codepair{0x07a3, 0x0389},
337 X11Codepair{0x07a4, 0x038a},
338 X11Codepair{0x07a5, 0x03aa},
339 X11Codepair{0x07a7, 0x038c},
340 X11Codepair{0x07a8, 0x038e},
341 X11Codepair{0x07a9, 0x03ab},
342 X11Codepair{0x07ab, 0x038f},
343 X11Codepair{0x07ae, 0x0385},
344 X11Codepair{0x07af, 0x2015},
345 X11Codepair{0x07b1, 0x03ac},
346 X11Codepair{0x07b2, 0x03ad},
347 X11Codepair{0x07b3, 0x03ae},
348 X11Codepair{0x07b4, 0x03af},
349 X11Codepair{0x07b5, 0x03ca},
350 X11Codepair{0x07b6, 0x0390},
351 X11Codepair{0x07b7, 0x03cc},
352 X11Codepair{0x07b8, 0x03cd},
353 X11Codepair{0x07b9, 0x03cb},
354 X11Codepair{0x07ba, 0x03b0},
355 X11Codepair{0x07bb, 0x03ce},
356 X11Codepair{0x07c1, 0x0391},
357 X11Codepair{0x07c2, 0x0392},
358 X11Codepair{0x07c3, 0x0393},
359 X11Codepair{0x07c4, 0x0394},
360 X11Codepair{0x07c5, 0x0395},
361 X11Codepair{0x07c6, 0x0396},
362 X11Codepair{0x07c7, 0x0397},
363 X11Codepair{0x07c8, 0x0398},
364 X11Codepair{0x07c9, 0x0399},
365 X11Codepair{0x07ca, 0x039a},
366 X11Codepair{0x07cb, 0x039b},
367 X11Codepair{0x07cc, 0x039c},
368 X11Codepair{0x07cd, 0x039d},
369 X11Codepair{0x07ce, 0x039e},
370 X11Codepair{0x07cf, 0x039f},
371 X11Codepair{0x07d0, 0x03a0},
372 X11Codepair{0x07d1, 0x03a1},
373 X11Codepair{0x07d2, 0x03a3},
374 X11Codepair{0x07d4, 0x03a4},
375 X11Codepair{0x07d5, 0x03a5},
376 X11Codepair{0x07d6, 0x03a6},
377 X11Codepair{0x07d7, 0x03a7},
378 X11Codepair{0x07d8, 0x03a8},
379 X11Codepair{0x07d9, 0x03a9},
380 X11Codepair{0x07e1, 0x03b1},
381 X11Codepair{0x07e2, 0x03b2},
382 X11Codepair{0x07e3, 0x03b3},
383 X11Codepair{0x07e4, 0x03b4},
384 X11Codepair{0x07e5, 0x03b5},
385 X11Codepair{0x07e6, 0x03b6},
386 X11Codepair{0x07e7, 0x03b7},
387 X11Codepair{0x07e8, 0x03b8},
388 X11Codepair{0x07e9, 0x03b9},
389 X11Codepair{0x07ea, 0x03ba},
390 X11Codepair{0x07eb, 0x03bb},
391 X11Codepair{0x07ec, 0x03bc},
392 X11Codepair{0x07ed, 0x03bd},
393 X11Codepair{0x07ee, 0x03be},
394 X11Codepair{0x07ef, 0x03bf},
395 X11Codepair{0x07f0, 0x03c0},
396 X11Codepair{0x07f1, 0x03c1},
397 X11Codepair{0x07f2, 0x03c3},
398 X11Codepair{0x07f3, 0x03c2},
399 X11Codepair{0x07f4, 0x03c4},
400 X11Codepair{0x07f5, 0x03c5},
401 X11Codepair{0x07f6, 0x03c6},
402 X11Codepair{0x07f7, 0x03c7},
403 X11Codepair{0x07f8, 0x03c8},
404 X11Codepair{0x07f9, 0x03c9},
405 X11Codepair{0x0cdf, 0x2017},
406 X11Codepair{0x0ce0, 0x05d0},
407 X11Codepair{0x0ce1, 0x05d1},
408 X11Codepair{0x0ce2, 0x05d2},
409 X11Codepair{0x0ce3, 0x05d3},
410 X11Codepair{0x0ce4, 0x05d4},
411 X11Codepair{0x0ce5, 0x05d5},
412 X11Codepair{0x0ce6, 0x05d6},
413 X11Codepair{0x0ce7, 0x05d7},
414 X11Codepair{0x0ce8, 0x05d8},
415 X11Codepair{0x0ce9, 0x05d9},
416 X11Codepair{0x0cea, 0x05da},
417 X11Codepair{0x0ceb, 0x05db},
418 X11Codepair{0x0cec, 0x05dc},
419 X11Codepair{0x0ced, 0x05dd},
420 X11Codepair{0x0cee, 0x05de},
421 X11Codepair{0x0cef, 0x05df},
422 X11Codepair{0x0cf0, 0x05e0},
423 X11Codepair{0x0cf1, 0x05e1},
424 X11Codepair{0x0cf2, 0x05e2},
425 X11Codepair{0x0cf3, 0x05e3},
426 X11Codepair{0x0cf4, 0x05e4},
427 X11Codepair{0x0cf5, 0x05e5},
428 X11Codepair{0x0cf6, 0x05e6},
429 X11Codepair{0x0cf7, 0x05e7},
430 X11Codepair{0x0cf8, 0x05e8},
431 X11Codepair{0x0cf9, 0x05e9},
432 X11Codepair{0x0cfa, 0x05ea},
433 X11Codepair{0x13a4, 0x20ac},
434 X11Codepair{0x13bc, 0x0152},
435 X11Codepair{0x13bd, 0x0153},
436 X11Codepair{0x13be, 0x0178},
437 X11Codepair{0x20ac, 0x20ac},
438 X11Codepair{0xfe50, 0x0060},
439 X11Codepair{0xfe51, 0x00b4},
440 X11Codepair{0xfe52, 0x005e},
441 X11Codepair{0xfe53, 0x007e},
442 X11Codepair{0xfe54, 0x00af},
443 X11Codepair{0xfe55, 0x02d8},
444 X11Codepair{0xfe56, 0x02d9},
445 X11Codepair{0xfe57, 0x00a8},
446 X11Codepair{0xfe58, 0x02da},
447 X11Codepair{0xfe59, 0x02dd},
448 X11Codepair{0xfe5a, 0x02c7},
449 X11Codepair{0xfe5b, 0x00b8},
450 X11Codepair{0xfe5c, 0x02db},
451 X11Codepair{0xfe5d, 0x037a},
452 X11Codepair{0xfe5e, 0x309b},
453 X11Codepair{0xfe5f, 0x309c},
454 X11Codepair{0xfe63, 0x002f},
455 X11Codepair{0xfe64, 0x02bc},
456 X11Codepair{0xfe65, 0x02bd},
457 X11Codepair{0xfe66, 0x02f5},
458 X11Codepair{0xfe67, 0x02f3},
459 X11Codepair{0xfe68, 0x02cd},
460 X11Codepair{0xfe69, 0xa788},
461 X11Codepair{0xfe6a, 0x02f7},
462 X11Codepair{0xfe6e, 0x002c},
463 X11Codepair{0xfe6f, 0x00a4},
464 X11Codepair{0xfe80, 0x0061},
465 X11Codepair{0xfe81, 0x0041},
466 X11Codepair{0xfe82, 0x0065},
467 X11Codepair{0xfe83, 0x0045},
468 X11Codepair{0xfe84, 0x0069},
469 X11Codepair{0xfe85, 0x0049},
470 X11Codepair{0xfe86, 0x006f},
471 X11Codepair{0xfe87, 0x004f},
472 X11Codepair{0xfe88, 0x0075},
473 X11Codepair{0xfe89, 0x0055},
474 X11Codepair{0xfe8a, 0x0259},
475 X11Codepair{0xfe8b, 0x018f},
476 X11Codepair{0xfe8c, 0x00b5},
477 X11Codepair{0xfe90, 0x005f},
478 X11Codepair{0xfe91, 0x02c8},
479 X11Codepair{0xfe92, 0x02cc},
480 X11Codepair{0xff80, 0x0020},
481 X11Codepair{0xff95, 0x0037},
482 X11Codepair{0xff96, 0x0034},
483 X11Codepair{0xff97, 0x0038},
484 X11Codepair{0xff98, 0x0036},
485 X11Codepair{0xff99, 0x0032},
486 X11Codepair{0xff9a, 0x0039},
487 X11Codepair{0xff9b, 0x0033},
488 X11Codepair{0xff9c, 0x0031},
489 X11Codepair{0xff9d, 0x0035},
490 X11Codepair{0xff9e, 0x0030},
491 X11Codepair{0xffaa, 0x002a},
492 X11Codepair{0xffab, 0x002b},
493 X11Codepair{0xffac, 0x002c},
494 X11Codepair{0xffad, 0x002d},
495 X11Codepair{0xffae, 0x002e},
496 X11Codepair{0xffaf, 0x002f},
497 X11Codepair{0xffb0, 0x0030},
498 X11Codepair{0xffb1, 0x0031},
499 X11Codepair{0xffb2, 0x0032},
500 X11Codepair{0xffb3, 0x0033},
501 X11Codepair{0xffb4, 0x0034},
502 X11Codepair{0xffb5, 0x0035},
503 X11Codepair{0xffb6, 0x0036},
504 X11Codepair{0xffb7, 0x0037},
505 X11Codepair{0xffb8, 0x0038},
506 X11Codepair{0xffb9, 0x0039},
507 X11Codepair{0xffbd, 0x003d},
508]!
509
510// === Error handling ===
511
512fn x11_grab_error_handler() {
513 g_sapp_state.x11.error_code = 0 // Success
514 C.XSetErrorHandler(voidptr(x11_error_handler))
515}
516
517fn x11_release_error_handler() {
518 C.XSync(g_sapp_state.x11.display, x_false)
519 C.XSetErrorHandler(unsafe { nil })
520}
521
522fn x11_error_handler(_display &C.Display, _event voidptr) int {
523 // XErrorEvent.error_code is at a known offset
524 // For simplicity, just set a non-zero error code
525 g_sapp_state.x11.error_code = 1
526 return 0
527}
528
529// === Atom initialization ===
530
531fn x11_init_extensions() {
532 d := g_sapp_state.x11.display
533 g_sapp_state.x11.utf8_string = C.XInternAtom(d, c'UTF8_STRING', x_false)
534 g_sapp_state.x11.wm_protocols = C.XInternAtom(d, c'WM_PROTOCOLS', x_false)
535 g_sapp_state.x11.wm_delete_window = C.XInternAtom(d, c'WM_DELETE_WINDOW', x_false)
536 g_sapp_state.x11.wm_state = C.XInternAtom(d, c'WM_STATE', x_false)
537 g_sapp_state.x11.net_wm_name = C.XInternAtom(d, c'_NET_WM_NAME', x_false)
538 g_sapp_state.x11.net_wm_icon_name = C.XInternAtom(d, c'_NET_WM_ICON_NAME', x_false)
539 g_sapp_state.x11.net_wm_icon = C.XInternAtom(d, c'_NET_WM_ICON', x_false)
540 g_sapp_state.x11.net_wm_state = C.XInternAtom(d, c'_NET_WM_STATE', x_false)
541 g_sapp_state.x11.net_wm_state_fullscreen = C.XInternAtom(d, c'_NET_WM_STATE_FULLSCREEN',
542 x_false)
543 g_sapp_state.x11.clipboard_atom = C.XInternAtom(d, c'CLIPBOARD', x_false)
544 g_sapp_state.x11.targets = C.XInternAtom(d, c'TARGETS', x_false)
545 if g_sapp_state.drop.enabled {
546 g_sapp_state.x11.xdnd.xdnd_aware = C.XInternAtom(d, c'XdndAware', x_false)
547 g_sapp_state.x11.xdnd.xdnd_enter = C.XInternAtom(d, c'XdndEnter', x_false)
548 g_sapp_state.x11.xdnd.xdnd_position = C.XInternAtom(d, c'XdndPosition', x_false)
549 g_sapp_state.x11.xdnd.xdnd_status = C.XInternAtom(d, c'XdndStatus', x_false)
550 g_sapp_state.x11.xdnd.xdnd_action_copy = C.XInternAtom(d, c'XdndActionCopy', x_false)
551 g_sapp_state.x11.xdnd.xdnd_drop = C.XInternAtom(d, c'XdndDrop', x_false)
552 g_sapp_state.x11.xdnd.xdnd_finished = C.XInternAtom(d, c'XdndFinished', x_false)
553 g_sapp_state.x11.xdnd.xdnd_selection = C.XInternAtom(d, c'XdndSelection', x_false)
554 g_sapp_state.x11.xdnd.xdnd_type_list = C.XInternAtom(d, c'XdndTypeList', x_false)
555 g_sapp_state.x11.xdnd.text_uri_list = C.XInternAtom(d, c'text/uri-list', x_false)
556 }
557 // Check Xi extension for raw mouse input
558 if C.XQueryExtension(d, c'XInputExtension', &g_sapp_state.x11.xi.major_opcode,
559 &g_sapp_state.x11.xi.event_base, &g_sapp_state.x11.xi.error_base) != 0 {
560 g_sapp_state.x11.xi.major = 2
561 g_sapp_state.x11.xi.minor = 0
562 if C.XIQueryVersion(d, &g_sapp_state.x11.xi.major, &g_sapp_state.x11.xi.minor) == 0 {
563 g_sapp_state.x11.xi.available = true
564 }
565 }
566}
567
568// === Key translation ===
569
570fn x11_translate_keysyms(keysyms &KeySym, width int) KeyCode {
571 if width > 1 {
572 unsafe {
573 match keysyms[1] {
574 xk_kp_0 { return .kp_0 }
575 xk_kp_1 { return .kp_1 }
576 xk_kp_2 { return .kp_2 }
577 xk_kp_3 { return .kp_3 }
578 xk_kp_4 { return .kp_4 }
579 xk_kp_5 { return .kp_5 }
580 xk_kp_6 { return .kp_6 }
581 xk_kp_7 { return .kp_7 }
582 xk_kp_8 { return .kp_8 }
583 xk_kp_9 { return .kp_9 }
584 xk_kp_separator, xk_kp_decimal { return .kp_decimal }
585 xk_kp_equal { return .kp_equal }
586 xk_kp_enter { return .kp_enter }
587 else {}
588 }
589 }
590 }
591 unsafe {
592 return match keysyms[0] {
593 xk_escape { KeyCode.escape }
594 xk_tab { KeyCode.tab }
595 xk_shift_l { KeyCode.left_shift }
596 xk_shift_r { KeyCode.right_shift }
597 xk_control_l { KeyCode.left_control }
598 xk_control_r { KeyCode.right_control }
599 xk_meta_l, xk_alt_l { KeyCode.left_alt }
600 xk_mode_switch, xk_iso_level3_shift, xk_meta_r, xk_alt_r { KeyCode.right_alt }
601 xk_super_l { KeyCode.left_super }
602 xk_super_r { KeyCode.right_super }
603 xk_menu { KeyCode.menu }
604 xk_num_lock { KeyCode.num_lock }
605 xk_caps_lock { KeyCode.caps_lock }
606 xk_print { KeyCode.print_screen }
607 xk_scroll_lock { KeyCode.scroll_lock }
608 xk_pause { KeyCode.pause }
609 xk_delete { KeyCode.delete }
610 xk_backspace { KeyCode.backspace }
611 xk_return { KeyCode.enter }
612 xk_home { KeyCode.home }
613 xk_end { KeyCode.end }
614 xk_page_up { KeyCode.page_up }
615 xk_page_down { KeyCode.page_down }
616 xk_insert { KeyCode.insert }
617 xk_left { KeyCode.left }
618 xk_right { KeyCode.right }
619 xk_down { KeyCode.down }
620 xk_up { KeyCode.up }
621 xk_f1 { KeyCode.f1 }
622 xk_f2 { KeyCode.f2 }
623 xk_f3 { KeyCode.f3 }
624 xk_f4 { KeyCode.f4 }
625 xk_f5 { KeyCode.f5 }
626 xk_f6 { KeyCode.f6 }
627 xk_f7 { KeyCode.f7 }
628 xk_f8 { KeyCode.f8 }
629 xk_f9 { KeyCode.f9 }
630 xk_f10 { KeyCode.f10 }
631 xk_f11 { KeyCode.f11 }
632 xk_f12 { KeyCode.f12 }
633 xk_f13 { KeyCode.f13 }
634 xk_f14 { KeyCode.f14 }
635 xk_f15 { KeyCode.f15 }
636 xk_f16 { KeyCode.f16 }
637 xk_f17 { KeyCode.f17 }
638 xk_f18 { KeyCode.f18 }
639 xk_f19 { KeyCode.f19 }
640 xk_f20 { KeyCode.f20 }
641 xk_f21 { KeyCode.f21 }
642 xk_f22 { KeyCode.f22 }
643 xk_f23 { KeyCode.f23 }
644 xk_f24 { KeyCode.f24 }
645 xk_f25 { KeyCode.f25 }
646 xk_kp_divide { KeyCode.kp_divide }
647 xk_kp_multiply { KeyCode.kp_multiply }
648 xk_kp_subtract { KeyCode.kp_subtract }
649 xk_kp_add { KeyCode.kp_add }
650 xk_kp_insert { KeyCode.kp_0 }
651 xk_kp_end { KeyCode.kp_1 }
652 xk_kp_down { KeyCode.kp_2 }
653 xk_kp_page_down { KeyCode.kp_3 }
654 xk_kp_left { KeyCode.kp_4 }
655 xk_kp_right { KeyCode.kp_6 }
656 xk_kp_home { KeyCode.kp_7 }
657 xk_kp_up { KeyCode.kp_8 }
658 xk_kp_page_up { KeyCode.kp_9 }
659 xk_kp_delete { KeyCode.kp_decimal }
660 xk_kp_equal { KeyCode.kp_equal }
661 xk_kp_enter { KeyCode.kp_enter }
662 xk_a { KeyCode.a }
663 xk_b { KeyCode.b }
664 xk_c { KeyCode.c }
665 xk_d { KeyCode.d }
666 xk_e { KeyCode.e }
667 xk_f { KeyCode.f }
668 xk_g { KeyCode.g }
669 xk_h { KeyCode.h }
670 xk_i { KeyCode.i }
671 xk_j { KeyCode.j }
672 xk_k { KeyCode.k }
673 xk_l { KeyCode.l }
674 xk_m { KeyCode.m }
675 xk_n { KeyCode.n }
676 xk_o { KeyCode.o }
677 xk_p { KeyCode.p }
678 xk_q { KeyCode.q }
679 xk_r { KeyCode.r }
680 xk_s { KeyCode.s }
681 xk_t { KeyCode.t }
682 xk_u { KeyCode.u }
683 xk_v { KeyCode.v }
684 xk_w { KeyCode.w }
685 xk_x { KeyCode.x }
686 xk_y { KeyCode.y }
687 xk_z { KeyCode.z }
688 xk_1 { KeyCode._1 }
689 xk_2 { KeyCode._2 }
690 xk_3 { KeyCode._3 }
691 xk_4 { KeyCode._4 }
692 xk_5 { KeyCode._5 }
693 xk_6 { KeyCode._6 }
694 xk_7 { KeyCode._7 }
695 xk_8 { KeyCode._8 }
696 xk_9 { KeyCode._9 }
697 xk_0 { KeyCode._0 }
698 xk_space { KeyCode.space }
699 xk_minus { KeyCode.minus }
700 xk_equal { KeyCode.equal }
701 xk_bracketleft { KeyCode.left_bracket }
702 xk_bracketright { KeyCode.right_bracket }
703 xk_backslash { KeyCode.backslash }
704 xk_semicolon { KeyCode.semicolon }
705 xk_apostrophe { KeyCode.apostrophe }
706 xk_grave { KeyCode.grave_accent }
707 xk_comma { KeyCode.comma }
708 xk_period { KeyCode.period }
709 xk_slash { KeyCode.slash }
710 xk_less { KeyCode.world_1 }
711 else { KeyCode.invalid }
712 }
713 }
714}
715
716fn x11_lookup_keysym(event &C.XEvent) KeySym {
717 mut keysym := KeySym(0)
718 unsafe {
719 C.XLookupString(&event.xkey, nil, 0, &keysym, nil)
720 }
721 return keysym
722}
723
724fn x11_translate_event_key(scancode int, keysym KeySym) KeyCode {
725 key := linux_translate_navigation_or_keypad_keysym(u32(keysym))
726 if key != .invalid {
727 return key
728 }
729 return x11_translate_key(scancode)
730}
731
732// XKB key name to keycode mapping entry
733struct X11KeymapEntry {
734mut:
735 key KeyCode
736 name [4]u8
737}
738
739fn make_keymap_entry(key KeyCode, name string) X11KeymapEntry {
740 mut e := X11KeymapEntry{
741 key: key
742 }
743 for i := 0; i < 4 && i < name.len; i++ {
744 unsafe {
745 e.name[i] = name[i]
746 }
747 }
748 return e
749}
750
751fn x11_init_keytable() {
752 for i in 0 .. max_keycodes {
753 g_sapp_state.keycodes[i] = .invalid
754 }
755 // Use XKB to determine physical key locations
756 desc := C.XkbGetMap(g_sapp_state.x11.display, 0, xkb_use_core_kbd)
757 if desc == unsafe { nil } {
758 return
759 }
760 C.XkbGetNames(g_sapp_state.x11.display, xkb_key_names_mask | xkb_key_aliases_mask, desc)
761
762 scancode_min := int(desc.min_key_code)
763 scancode_max := int(desc.max_key_code)
764
765 keymap := [
766 make_keymap_entry(.grave_accent, 'TLDE'),
767 make_keymap_entry(._1, 'AE01'),
768 make_keymap_entry(._2, 'AE02'),
769 make_keymap_entry(._3, 'AE03'),
770 make_keymap_entry(._4, 'AE04'),
771 make_keymap_entry(._5, 'AE05'),
772 make_keymap_entry(._6, 'AE06'),
773 make_keymap_entry(._7, 'AE07'),
774 make_keymap_entry(._8, 'AE08'),
775 make_keymap_entry(._9, 'AE09'),
776 make_keymap_entry(._0, 'AE10'),
777 make_keymap_entry(.minus, 'AE11'),
778 make_keymap_entry(.equal, 'AE12'),
779 make_keymap_entry(.q, 'AD01'),
780 make_keymap_entry(.w, 'AD02'),
781 make_keymap_entry(.e, 'AD03'),
782 make_keymap_entry(.r, 'AD04'),
783 make_keymap_entry(.t, 'AD05'),
784 make_keymap_entry(.y, 'AD06'),
785 make_keymap_entry(.u, 'AD07'),
786 make_keymap_entry(.i, 'AD08'),
787 make_keymap_entry(.o, 'AD09'),
788 make_keymap_entry(.p, 'AD10'),
789 make_keymap_entry(.left_bracket, 'AD11'),
790 make_keymap_entry(.right_bracket, 'AD12'),
791 make_keymap_entry(.a, 'AC01'),
792 make_keymap_entry(.s, 'AC02'),
793 make_keymap_entry(.d, 'AC03'),
794 make_keymap_entry(.f, 'AC04'),
795 make_keymap_entry(.g, 'AC05'),
796 make_keymap_entry(.h, 'AC06'),
797 make_keymap_entry(.j, 'AC07'),
798 make_keymap_entry(.k, 'AC08'),
799 make_keymap_entry(.l, 'AC09'),
800 make_keymap_entry(.semicolon, 'AC10'),
801 make_keymap_entry(.apostrophe, 'AC11'),
802 make_keymap_entry(.z, 'AB01'),
803 make_keymap_entry(.x, 'AB02'),
804 make_keymap_entry(.c, 'AB03'),
805 make_keymap_entry(.v, 'AB04'),
806 make_keymap_entry(.b, 'AB05'),
807 make_keymap_entry(.n, 'AB06'),
808 make_keymap_entry(.m, 'AB07'),
809 make_keymap_entry(.comma, 'AB08'),
810 make_keymap_entry(.period, 'AB09'),
811 make_keymap_entry(.slash, 'AB10'),
812 make_keymap_entry(.backslash, 'BKSL'),
813 make_keymap_entry(.world_1, 'LSGT'),
814 make_keymap_entry(.space, 'SPCE'),
815 make_keymap_entry(.escape, 'ESC\x00'),
816 make_keymap_entry(.enter, 'RTRN'),
817 make_keymap_entry(.tab, 'TAB\x00'),
818 make_keymap_entry(.backspace, 'BKSP'),
819 make_keymap_entry(.insert, 'INS\x00'),
820 make_keymap_entry(.delete, 'DELE'),
821 make_keymap_entry(.right, 'RGHT'),
822 make_keymap_entry(.left, 'LEFT'),
823 make_keymap_entry(.down, 'DOWN'),
824 make_keymap_entry(.up, 'UP\x00\x00'),
825 make_keymap_entry(.page_up, 'PGUP'),
826 make_keymap_entry(.page_down, 'PGDN'),
827 make_keymap_entry(.home, 'HOME'),
828 make_keymap_entry(.end, 'END\x00'),
829 make_keymap_entry(.caps_lock, 'CAPS'),
830 make_keymap_entry(.scroll_lock, 'SCLK'),
831 make_keymap_entry(.num_lock, 'NMLK'),
832 make_keymap_entry(.print_screen, 'PRSC'),
833 make_keymap_entry(.pause, 'PAUS'),
834 make_keymap_entry(.f1, 'FK01'),
835 make_keymap_entry(.f2, 'FK02'),
836 make_keymap_entry(.f3, 'FK03'),
837 make_keymap_entry(.f4, 'FK04'),
838 make_keymap_entry(.f5, 'FK05'),
839 make_keymap_entry(.f6, 'FK06'),
840 make_keymap_entry(.f7, 'FK07'),
841 make_keymap_entry(.f8, 'FK08'),
842 make_keymap_entry(.f9, 'FK09'),
843 make_keymap_entry(.f10, 'FK10'),
844 make_keymap_entry(.f11, 'FK11'),
845 make_keymap_entry(.f12, 'FK12'),
846 make_keymap_entry(.f13, 'FK13'),
847 make_keymap_entry(.f14, 'FK14'),
848 make_keymap_entry(.f15, 'FK15'),
849 make_keymap_entry(.f16, 'FK16'),
850 make_keymap_entry(.f17, 'FK17'),
851 make_keymap_entry(.f18, 'FK18'),
852 make_keymap_entry(.f19, 'FK19'),
853 make_keymap_entry(.f20, 'FK20'),
854 make_keymap_entry(.f21, 'FK21'),
855 make_keymap_entry(.f22, 'FK22'),
856 make_keymap_entry(.f23, 'FK23'),
857 make_keymap_entry(.f24, 'FK24'),
858 make_keymap_entry(.f25, 'FK25'),
859 make_keymap_entry(.kp_0, 'KP0\x00'),
860 make_keymap_entry(.kp_1, 'KP1\x00'),
861 make_keymap_entry(.kp_2, 'KP2\x00'),
862 make_keymap_entry(.kp_3, 'KP3\x00'),
863 make_keymap_entry(.kp_4, 'KP4\x00'),
864 make_keymap_entry(.kp_5, 'KP5\x00'),
865 make_keymap_entry(.kp_6, 'KP6\x00'),
866 make_keymap_entry(.kp_7, 'KP7\x00'),
867 make_keymap_entry(.kp_8, 'KP8\x00'),
868 make_keymap_entry(.kp_9, 'KP9\x00'),
869 make_keymap_entry(.kp_decimal, 'KPDL'),
870 make_keymap_entry(.kp_divide, 'KPDV'),
871 make_keymap_entry(.kp_multiply, 'KPMU'),
872 make_keymap_entry(.kp_subtract, 'KPSU'),
873 make_keymap_entry(.kp_add, 'KPAD'),
874 make_keymap_entry(.kp_enter, 'KPEN'),
875 make_keymap_entry(.kp_equal, 'KPEQ'),
876 make_keymap_entry(.left_shift, 'LFSH'),
877 make_keymap_entry(.left_control, 'LCTL'),
878 make_keymap_entry(.left_alt, 'LALT'),
879 make_keymap_entry(.left_super, 'LWIN'),
880 make_keymap_entry(.right_shift, 'RTSH'),
881 make_keymap_entry(.right_control, 'RCTL'),
882 make_keymap_entry(.right_alt, 'RALT'),
883 make_keymap_entry(.right_alt, 'LVL3'),
884 make_keymap_entry(.right_alt, 'MDSW'),
885 make_keymap_entry(.right_super, 'RWIN'),
886 make_keymap_entry(.menu, 'MENU'),
887 ]
888
889 // Find X11 keycode to sokol-app key code mapping
890 for scancode in scancode_min .. scancode_max + 1 {
891 mut key := KeyCode.invalid
892 for km in keymap {
893 if unsafe {
894 C.strncmp(&char(&desc.names.keys[scancode].name[0]), &char(&km.name[0]),
895 usize(xkb_key_name_length))
896 } == 0 {
897 key = km.key
898 break
899 }
900 }
901 // Fall back to key aliases
902 if key == .invalid && desc.names.key_aliases != unsafe { nil } {
903 for i in 0 .. int(desc.names.num_key_aliases) {
904 if unsafe {
905 C.strncmp(&char(&desc.names.key_aliases[i].real[0]),
906 &char(&desc.names.keys[scancode].name[0]), usize(xkb_key_name_length))
907 } != 0 {
908 continue
909 }
910 for km in keymap {
911 if unsafe {
912 C.strncmp(&char(&desc.names.key_aliases[i].alias[0]), &char(&km.name[0]),
913 usize(xkb_key_name_length))
914 } == 0 {
915 key = km.key
916 break
917 }
918 }
919 if key != .invalid {
920 break
921 }
922 }
923 }
924 g_sapp_state.keycodes[scancode] = key
925 }
926 C.XkbFreeNames(desc, xkb_key_names_mask, x_true)
927 C.XkbFreeKeyboard(desc, 0, x_true)
928
929 // Fall back using traditional keysym lookup
930 mut syms_per_code := 0
931 keysyms := C.XGetKeyboardMapping(g_sapp_state.x11.display, u8(scancode_min), scancode_max -
932 scancode_min + 1, &syms_per_code)
933 for scancode in scancode_min .. scancode_max + 1 {
934 if g_sapp_state.keycodes[scancode] == .invalid {
935 base := usize((scancode - scancode_min) * syms_per_code)
936 g_sapp_state.keycodes[scancode] = x11_translate_keysyms(unsafe { keysyms + base },
937 syms_per_code)
938 }
939 }
940 C.XFree(keysyms)
941}
942
943// === DPI ===
944
945fn x11_query_system_dpi() {
946 mut dpi_ok := false
947 rms := C.XResourceManagerString(g_sapp_state.x11.display)
948 if rms != unsafe { nil } {
949 db := C.XrmGetStringDatabase(rms)
950 if db != unsafe { nil } {
951 mut value := C.XrmValue{}
952 mut @type := &char(unsafe { nil })
953 if C.XrmGetResource(db, c'Xft.dpi', c'Xft.Dpi', &@type, &value) != 0 {
954 if @type != unsafe { nil } && C.strcmp(@type, c'String') == 0 {
955 g_sapp_state.x11.dpi = f32(C.atof(value.addr))
956 dpi_ok = true
957 }
958 }
959 C.XrmDestroyDatabase(db)
960 }
961 }
962 if !dpi_ok {
963 g_sapp_state.x11.dpi = 96.0
964 }
965}
966
967// === Keysym to unicode ===
968
969fn x11_keysym_to_unicode(keysym KeySym) i32 {
970 keysym_value := u64(keysym)
971 // Latin-1 characters (1:1 mapping)
972 if (keysym_value >= 0x0020 && keysym_value <= 0x007e)
973 || (keysym_value >= 0x00a0 && keysym_value <= 0x00ff) {
974 return i32(keysym_value)
975 }
976 // Directly encoded 24-bit UCS characters
977 if (keysym_value & 0xff000000) == 0x01000000 {
978 return i32(keysym_value & 0x00ffffff)
979 }
980 // Binary search in table
981 mut min := 0
982 mut max := x11_keysymtab.len - 1
983 for max >= min {
984 mid := (min + max) / 2
985 if u64(x11_keysymtab[mid].keysym) < keysym_value {
986 min = mid + 1
987 } else if u64(x11_keysymtab[mid].keysym) > keysym_value {
988 max = mid - 1
989 } else {
990 return i32(x11_keysymtab[mid].ucs)
991 }
992 }
993 return -1
994}
995
996// === Window management ===
997
998fn x11_send_event(msg_type Atom, a i64, b i64, c_ i64, d i64, e i64) {
999 mut event := C.XEvent{}
1000 unsafe {
1001 event.@type = x_client_message
1002 event.xclient.window = g_sapp_state.x11.window
1003 event.xclient.format = 32
1004 event.xclient.message_type = msg_type
1005 event.xclient.data.l[0] = a
1006 event.xclient.data.l[1] = b
1007 event.xclient.data.l[2] = c_
1008 event.xclient.data.l[3] = d
1009 event.xclient.data.l[4] = e
1010 }
1011 C.XSendEvent(g_sapp_state.x11.display, g_sapp_state.x11.root, x_false,
1012 substructure_notify_mask | substructure_redirect_mask, &event)
1013}
1014
1015fn x11_wait_for_event(event_type int, timeout_sec f64, out_event &C.XEvent) bool {
1016 // Simple polling loop
1017 for {
1018 if C.XCheckTypedWindowEvent(g_sapp_state.x11.display, g_sapp_state.x11.window, event_type,
1019 out_event) != 0 {
1020 return true
1021 }
1022 mut fd := C.pollfd{
1023 fd: C.ConnectionNumber(g_sapp_state.x11.display)
1024 events: pollin
1025 }
1026 C.poll(&fd, 1, int(timeout_sec * 1000))
1027 // Simplified timeout - just try once
1028 return C.XCheckTypedWindowEvent(g_sapp_state.x11.display, g_sapp_state.x11.window,
1029 event_type, out_event) != 0
1030 }
1031 return false
1032}
1033
1034fn x11_app_event(event_type EventType) {
1035 if g_sapp_state.init_called && !g_sapp_state.cleanup_called {
1036 init_sapp_event(event_type)
1037 call_sapp_event(&g_sapp_state.event)
1038 }
1039}
1040
1041fn x11_roundf_gzero(v f32) int {
1042 if v >= 0.5 {
1043 return int(v + 0.5)
1044 } else {
1045 return int(v)
1046 }
1047}
1048
1049fn x11_update_dimensions(x11_window_width int, x11_window_height int) {
1050 window_scale := g_sapp_state.x11.dpi / 96.0
1051 g_sapp_state.window_width = x11_roundf_gzero(f32(x11_window_width) / window_scale)
1052 g_sapp_state.window_height = x11_roundf_gzero(f32(x11_window_height) / window_scale)
1053 cur_fb_width := g_sapp_state.framebuffer_width
1054 cur_fb_height := g_sapp_state.framebuffer_height
1055 g_sapp_state.framebuffer_width =
1056 x11_roundf_gzero(f32(g_sapp_state.window_width) * g_sapp_state.dpi_scale)
1057 g_sapp_state.framebuffer_height =
1058 x11_roundf_gzero(f32(g_sapp_state.window_height) * g_sapp_state.dpi_scale)
1059 dim_changed := g_sapp_state.framebuffer_width != cur_fb_width
1060 || g_sapp_state.framebuffer_height != cur_fb_height
1061 if dim_changed && !g_sapp_state.first_frame {
1062 x11_app_event(.resized)
1063 }
1064}
1065
1066fn x11_update_dimensions_from_window_size() {
1067 mut attribs := C.XWindowAttributes{}
1068 C.XGetWindowAttributes(g_sapp_state.x11.display, g_sapp_state.x11.window, &attribs)
1069 x11_update_dimensions(attribs.width, attribs.height)
1070}
1071
1072fn x11_set_fullscreen(enable bool) {
1073 if g_sapp_state.x11.net_wm_state != x_none && g_sapp_state.x11.net_wm_state_fullscreen != x_none {
1074 if enable {
1075 x11_send_event(g_sapp_state.x11.net_wm_state, 1, // _NET_WM_STATE_ADD
1076 i64(g_sapp_state.x11.net_wm_state_fullscreen), 0, 1, 0)
1077 } else {
1078 x11_send_event(g_sapp_state.x11.net_wm_state, 0, // _NET_WM_STATE_REMOVE
1079 i64(g_sapp_state.x11.net_wm_state_fullscreen), 0, 1, 0)
1080 }
1081 }
1082 C.XFlush(g_sapp_state.x11.display)
1083}
1084
1085// set_resizable toggles whether the current Linux window can be resized by the window manager.
1086pub fn set_resizable(resizable bool) {
1087 $if sokol_wayland ? {
1088 wl_set_resizable(resizable)
1089 } $else {
1090 x11_set_resizable(resizable)
1091 }
1092}
1093
1094fn x11_set_resizable(resizable bool) {
1095 if g_sapp_state.x11.display == unsafe { nil } || g_sapp_state.x11.window == x_window_none {
1096 return
1097 }
1098 mut hints := C.XAllocSizeHints()
1099 if hints == unsafe { nil } {
1100 return
1101 }
1102 hints.flags = pw_gravity
1103 hints.win_gravity = center_gravity
1104 if !resizable {
1105 mut attribs := C.XWindowAttributes{}
1106 C.XGetWindowAttributes(g_sapp_state.x11.display, g_sapp_state.x11.window, &attribs)
1107 hints.flags |= i64(1 << 4) | i64(1 << 5) // PMinSize | PMaxSize
1108 hints.min_width = attribs.width
1109 hints.min_height = attribs.height
1110 hints.max_width = attribs.width
1111 hints.max_height = attribs.height
1112 }
1113 C.XSetWMNormalHints(g_sapp_state.x11.display, g_sapp_state.x11.window, hints)
1114 C.XFree(hints)
1115 C.XFlush(g_sapp_state.x11.display)
1116}
1117
1118fn x11_create_window(visual_ &C.Visual, depth int) {
1119 mut vis := unsafe { visual_ }
1120 mut dep := depth
1121 if vis == unsafe { nil } {
1122 vis = C.XDefaultVisual(g_sapp_state.x11.display, g_sapp_state.x11.screen)
1123 dep = C.XDefaultDepth(g_sapp_state.x11.display, g_sapp_state.x11.screen)
1124 }
1125 g_sapp_state.x11.colormap = C.XCreateColormap(g_sapp_state.x11.display, g_sapp_state.x11.root,
1126 vis, alloc_none)
1127 mut wa := C.XSetWindowAttributes{}
1128 wa.colormap = g_sapp_state.x11.colormap
1129 wa.border_pixel = 0
1130 wa.event_mask = u64(structure_notify_mask | key_press_mask | key_release_mask | pointer_motion_mask | button_press_mask | button_release_mask | exposure_mask | focus_change_mask | visibility_change_mask | enter_window_mask | leave_window_mask | property_change_mask)
1131
1132 display_width := C.XDisplayWidth(g_sapp_state.x11.display, g_sapp_state.x11.screen)
1133 display_height := C.XDisplayHeight(g_sapp_state.x11.display, g_sapp_state.x11.screen)
1134 window_scale := g_sapp_state.x11.dpi / 96.0
1135 mut x11_window_width := x11_roundf_gzero(f32(g_sapp_state.window_width) * window_scale)
1136 mut x11_window_height := x11_roundf_gzero(f32(g_sapp_state.window_height) * window_scale)
1137 if g_sapp_state.window_width == 0 {
1138 x11_window_width = (display_width * 4) / 5
1139 }
1140 if g_sapp_state.window_height == 0 {
1141 x11_window_height = (display_height * 4) / 5
1142 }
1143 // wamask = CWBorderPixel | CWColormap | CWEventMask
1144 wamask := u64(0x800 | 0x2000 | 0x800)
1145 x11_grab_error_handler()
1146 g_sapp_state.x11.window = C.XCreateWindow(g_sapp_state.x11.display, g_sapp_state.x11.root, 0,
1147 0, u32(x11_window_width), u32(x11_window_height), 0, dep, input_output, vis, wamask, &wa)
1148 x11_release_error_handler()
1149 if g_sapp_state.x11.window == x_window_none {
1150 eprintln('sokol_app: X11: failed to create window')
1151 return
1152 }
1153 mut protocols := [1]Atom{}
1154 protocols[0] = g_sapp_state.x11.wm_delete_window
1155 C.XSetWMProtocols(g_sapp_state.x11.display, g_sapp_state.x11.window, &protocols[0], 1)
1156 mut hints := C.XAllocSizeHints()
1157 hints.flags = pw_gravity
1158 hints.win_gravity = center_gravity
1159 C.XSetWMNormalHints(g_sapp_state.x11.display, g_sapp_state.x11.window, hints)
1160 C.XFree(hints)
1161 // Announce support for drag'n'drop
1162 if g_sapp_state.drop.enabled {
1163 version := Atom(x11_xdnd_version)
1164 C.XChangeProperty(g_sapp_state.x11.display, g_sapp_state.x11.window,
1165 g_sapp_state.x11.xdnd.xdnd_aware, xa_atom, 32, prop_mode_replace,
1166 unsafe { &u8(&version) }, 1)
1167 }
1168 x11_update_window_title()
1169 x11_update_dimensions_from_window_size()
1170}
1171
1172fn x11_destroy_window() {
1173 if g_sapp_state.x11.window != x_window_none {
1174 C.XUnmapWindow(g_sapp_state.x11.display, g_sapp_state.x11.window)
1175 C.XDestroyWindow(g_sapp_state.x11.display, g_sapp_state.x11.window)
1176 g_sapp_state.x11.window = x_window_none
1177 }
1178 if g_sapp_state.x11.colormap != x_colormap_none {
1179 C.XFreeColormap(g_sapp_state.x11.display, g_sapp_state.x11.colormap)
1180 g_sapp_state.x11.colormap = x_colormap_none
1181 }
1182 C.XFlush(g_sapp_state.x11.display)
1183}
1184
1185fn x11_window_visible() bool {
1186 mut wa := C.XWindowAttributes{}
1187 C.XGetWindowAttributes(g_sapp_state.x11.display, g_sapp_state.x11.window, &wa)
1188 return wa.map_state == is_viewable
1189}
1190
1191fn x11_show_window() {
1192 if !x11_window_visible() {
1193 C.XMapWindow(g_sapp_state.x11.display, g_sapp_state.x11.window)
1194 mut dummy := C.XEvent{}
1195 x11_wait_for_event(visibility_notify, 0.1, &dummy)
1196 C.XRaiseWindow(g_sapp_state.x11.display, g_sapp_state.x11.window)
1197 C.XFlush(g_sapp_state.x11.display)
1198 }
1199}
1200
1201fn x11_update_window_title() {
1202 title := &char(&g_sapp_state.window_title[0])
1203 C.Xutf8SetWMProperties(g_sapp_state.x11.display, g_sapp_state.x11.window, title, title,
1204 unsafe { nil }, 0, unsafe { nil }, unsafe { nil }, unsafe { nil })
1205 C.XChangeProperty(g_sapp_state.x11.display, g_sapp_state.x11.window,
1206 g_sapp_state.x11.net_wm_name, g_sapp_state.x11.utf8_string, 8, prop_mode_replace,
1207 &g_sapp_state.window_title[0], int(C.strlen(title)))
1208 C.XChangeProperty(g_sapp_state.x11.display, g_sapp_state.x11.window,
1209 g_sapp_state.x11.net_wm_icon_name, g_sapp_state.x11.utf8_string, 8, prop_mode_replace,
1210 &g_sapp_state.window_title[0], int(C.strlen(title)))
1211 C.XFlush(g_sapp_state.x11.display)
1212}
1213
1214// === Cursors ===
1215
1216fn x11_create_hidden_cursor() {
1217 mut img := C.XcursorImageCreate(16, 16)
1218 if img == unsafe { nil } {
1219 return
1220 }
1221 img.xhot = 0
1222 img.yhot = 0
1223 unsafe { C.memset(img.pixels, 0, usize(16 * 16 * 4)) }
1224 g_sapp_state.x11.hidden_cursor = C.XcursorImageLoadCursor(g_sapp_state.x11.display, img)
1225 C.XcursorImageDestroy(img)
1226}
1227
1228fn x11_create_standard_cursor(cursor MouseCursor, name &char, theme &char, size int, fallback_native u32) {
1229 idx := int(cursor)
1230 if theme != unsafe { nil } {
1231 img := C.XcursorLibraryLoadImage(name, theme, size)
1232 if img != unsafe { nil } {
1233 g_sapp_state.x11.standard_cursors[idx] =
1234 C.XcursorImageLoadCursor(g_sapp_state.x11.display, img)
1235 C.XcursorImageDestroy(img)
1236 }
1237 }
1238 if g_sapp_state.x11.standard_cursors[idx] == x_cursor_none && fallback_native != 0 {
1239 g_sapp_state.x11.standard_cursors[idx] = C.XCreateFontCursor(g_sapp_state.x11.display,
1240 fallback_native)
1241 }
1242}
1243
1244fn x11_create_standard_cursors() {
1245 cursor_theme := C.XcursorGetTheme(g_sapp_state.x11.display)
1246 size := C.XcursorGetDefaultSize(g_sapp_state.x11.display)
1247 x11_create_standard_cursor(.arrow, c'default', cursor_theme, size, xc_left_ptr)
1248 x11_create_standard_cursor(.ibeam, c'text', cursor_theme, size, xc_xterm)
1249 x11_create_standard_cursor(.crosshair, c'crosshair', cursor_theme, size, xc_crosshair)
1250 x11_create_standard_cursor(.pointing_hand, c'pointer', cursor_theme, size, xc_hand2)
1251 x11_create_standard_cursor(.resize_ew, c'ew-resize', cursor_theme, size, xc_sb_h_double_arrow)
1252 x11_create_standard_cursor(.resize_ns, c'ns-resize', cursor_theme, size, xc_sb_v_double_arrow)
1253 x11_create_standard_cursor(.resize_nwse, c'nwse-resize', cursor_theme, size, 0)
1254 x11_create_standard_cursor(.resize_nesw, c'nesw-resize', cursor_theme, size, 0)
1255 x11_create_standard_cursor(.resize_all, c'all-scroll', cursor_theme, size, xc_fleur)
1256 x11_create_standard_cursor(.not_allowed, c'no-allowed', cursor_theme, size, 0)
1257 x11_create_hidden_cursor()
1258}
1259
1260fn x11_destroy_standard_cursors() {
1261 if g_sapp_state.x11.hidden_cursor != x_cursor_none {
1262 C.XFreeCursor(g_sapp_state.x11.display, g_sapp_state.x11.hidden_cursor)
1263 g_sapp_state.x11.hidden_cursor = x_cursor_none
1264 }
1265 for i in 0 .. mousecursor_num {
1266 if g_sapp_state.x11.standard_cursors[i] != x_cursor_none {
1267 C.XFreeCursor(g_sapp_state.x11.display, g_sapp_state.x11.standard_cursors[i])
1268 g_sapp_state.x11.standard_cursors[i] = x_cursor_none
1269 }
1270 }
1271}
1272
1273fn x11_toggle_fullscreen() {
1274 g_sapp_state.fullscreen = !g_sapp_state.fullscreen
1275 x11_set_fullscreen(g_sapp_state.fullscreen)
1276 x11_update_dimensions_from_window_size()
1277}
1278
1279fn x11_update_cursor(cursor MouseCursor, shown bool) {
1280 idx := int(cursor)
1281 if shown {
1282 if g_sapp_state.custom_cursor_bound[idx] {
1283 xcursor := g_sapp_state.x11.custom_cursors[idx]
1284 if xcursor != x_cursor_none {
1285 C.XDefineCursor(g_sapp_state.x11.display, g_sapp_state.x11.window, xcursor)
1286 }
1287 } else if g_sapp_state.x11.standard_cursors[idx] != x_cursor_none {
1288 C.XDefineCursor(g_sapp_state.x11.display, g_sapp_state.x11.window,
1289 g_sapp_state.x11.standard_cursors[idx])
1290 } else {
1291 C.XUndefineCursor(g_sapp_state.x11.display, g_sapp_state.x11.window)
1292 }
1293 } else {
1294 C.XDefineCursor(g_sapp_state.x11.display, g_sapp_state.x11.window,
1295 g_sapp_state.x11.hidden_cursor)
1296 }
1297 C.XFlush(g_sapp_state.x11.display)
1298}
1299
1300// === Mouse lock ===
1301
1302fn x11_lock_mouse(do_lock bool) {
1303 if do_lock == g_sapp_state.mouse.locked {
1304 return
1305 }
1306 g_sapp_state.mouse.dx = 0.0
1307 g_sapp_state.mouse.dy = 0.0
1308 g_sapp_state.mouse.locked = do_lock
1309 if g_sapp_state.mouse.locked {
1310 if g_sapp_state.x11.xi.available {
1311 mut em := C.XIEventMask{}
1312 mut mask := [4]u8{}
1313 em.deviceid = xi_all_master_devices
1314 em.mask_len = int(sizeof(mask))
1315 em.mask = unsafe { &mask[0] }
1316 xi_set_mask(unsafe { &mask[0] }, xi_raw_motion)
1317 C.XISelectEvents(g_sapp_state.x11.display, g_sapp_state.x11.root, &em, 1)
1318 }
1319 C.XGrabPointer(g_sapp_state.x11.display, g_sapp_state.x11.window, x_true,
1320 u32(button_press_mask | button_release_mask | pointer_motion_mask), grab_mode_async,
1321 grab_mode_async, g_sapp_state.x11.window, g_sapp_state.x11.hidden_cursor, current_time)
1322 } else {
1323 if g_sapp_state.x11.xi.available {
1324 mut em := C.XIEventMask{}
1325 mut mask := [1]u8{}
1326 em.deviceid = xi_all_master_devices
1327 em.mask_len = int(sizeof(mask))
1328 em.mask = unsafe { &mask[0] }
1329 C.XISelectEvents(g_sapp_state.x11.display, g_sapp_state.x11.root, &em, 1)
1330 }
1331 C.XWarpPointer(g_sapp_state.x11.display, Window(0), g_sapp_state.x11.window, 0, 0, 0, 0,
1332 int(g_sapp_state.mouse.x), int(g_sapp_state.mouse.y))
1333 C.XUngrabPointer(g_sapp_state.x11.display, current_time)
1334 }
1335 C.XFlush(g_sapp_state.x11.display)
1336}
1337
1338// === Clipboard ===
1339
1340fn x11_set_clipboard_string(_str &char) {
1341 if !g_sapp_state.clipboard.enabled || g_sapp_state.clipboard.buffer == unsafe { nil } {
1342 return
1343 }
1344 C.XSetSelectionOwner(g_sapp_state.x11.display, g_sapp_state.x11.clipboard_atom,
1345 g_sapp_state.x11.window, current_time)
1346}
1347
1348fn x11_get_clipboard_string() &char {
1349 if !g_sapp_state.clipboard.enabled || g_sapp_state.clipboard.buffer == unsafe { nil } {
1350 return unsafe { nil }
1351 }
1352 // If we own the selection, just return our buffer
1353 if C.XGetSelectionOwner(g_sapp_state.x11.display, g_sapp_state.x11.clipboard_atom) == g_sapp_state.x11.window {
1354 return g_sapp_state.clipboard.buffer
1355 }
1356 sapp_selection := C.XInternAtom(g_sapp_state.x11.display, c'SAPP_SELECTION', x_false)
1357 C.XConvertSelection(g_sapp_state.x11.display, g_sapp_state.x11.clipboard_atom,
1358 g_sapp_state.x11.utf8_string, sapp_selection, g_sapp_state.x11.window, current_time)
1359 mut event := C.XEvent{}
1360 if !x11_wait_for_event(x_selection_notify, 0.1, &event) {
1361 return unsafe { nil }
1362 }
1363 unsafe {
1364 if event.xselection.property == x_none {
1365 return nil
1366 }
1367 mut data := &u8(nil)
1368 mut actual_type := Atom(0)
1369 mut actual_format := 0
1370 mut item_count := XlibULong(0)
1371 mut bytes_after := XlibULong(0)
1372 C.XGetWindowProperty(g_sapp_state.x11.display, event.xselection.requestor,
1373 event.xselection.property, 0, 0x7fffffff, x_true, g_sapp_state.x11.utf8_string,
1374 &actual_type, &actual_format, &item_count, &bytes_after, &&u8(&data))
1375 if data == nil {
1376 return nil
1377 }
1378 item_size := usize(item_count)
1379 if item_size >= usize(g_sapp_state.clipboard.buf_size) {
1380 C.XFree(data)
1381 return nil
1382 }
1383 C.memcpy(g_sapp_state.clipboard.buffer, data, item_size)
1384 g_sapp_state.clipboard.buffer[item_size] = 0
1385 C.XFree(data)
1386 return g_sapp_state.clipboard.buffer
1387 }
1388}
1389
1390// === Modifier helpers ===
1391
1392fn x11_key_modifier_bit(key KeyCode) u32 {
1393 return match key {
1394 .left_shift, .right_shift { u32(Modifier.shift) }
1395 .left_control, .right_control { u32(Modifier.ctrl) }
1396 .left_alt, .right_alt { u32(Modifier.alt) }
1397 .left_super, .right_super { u32(Modifier.super) }
1398 else { u32(0) }
1399 }
1400}
1401
1402fn x11_button_modifier_bit(btn MouseButton) u32 {
1403 return match btn {
1404 .left { u32(Modifier.lmb) }
1405 .right { u32(Modifier.rmb) }
1406 .middle { u32(Modifier.mmb) }
1407 else { u32(0) }
1408 }
1409}
1410
1411fn x11_mods(x11_mods_ u32) u32 {
1412 mut mods := u32(0)
1413 if x11_mods_ & shift_mask != 0 {
1414 mods |= u32(Modifier.shift)
1415 }
1416 if x11_mods_ & control_mask != 0 {
1417 mods |= u32(Modifier.ctrl)
1418 }
1419 if x11_mods_ & mod1_mask != 0 {
1420 mods |= u32(Modifier.alt)
1421 }
1422 if x11_mods_ & mod4_mask != 0 {
1423 mods |= u32(Modifier.super)
1424 }
1425 if x11_mods_ & button1_mask != 0 {
1426 mods |= u32(Modifier.lmb)
1427 }
1428 if x11_mods_ & button2_mask != 0 {
1429 mods |= u32(Modifier.mmb)
1430 }
1431 if x11_mods_ & button3_mask != 0 {
1432 mods |= u32(Modifier.rmb)
1433 }
1434 return mods
1435}
1436
1437fn x11_translate_button(event &C.XEvent) MouseButton {
1438 return match unsafe { event.xbutton.button } {
1439 x_button1 { MouseButton.left }
1440 x_button2 { MouseButton.middle }
1441 x_button3 { MouseButton.right }
1442 else { MouseButton.invalid }
1443 }
1444}
1445
1446// === Mouse/key event helpers ===
1447
1448fn x11_mouse_update(x int, y int, clear_dxdy bool) {
1449 if !g_sapp_state.mouse.locked {
1450 new_x := f32(x)
1451 new_y := f32(y)
1452 if clear_dxdy {
1453 g_sapp_state.mouse.dx = 0.0
1454 g_sapp_state.mouse.dy = 0.0
1455 } else if g_sapp_state.mouse.pos_valid {
1456 g_sapp_state.mouse.dx = new_x - g_sapp_state.mouse.x
1457 g_sapp_state.mouse.dy = new_y - g_sapp_state.mouse.y
1458 }
1459 g_sapp_state.mouse.x = new_x
1460 g_sapp_state.mouse.y = new_y
1461 g_sapp_state.mouse.pos_valid = true
1462 }
1463}
1464
1465fn x11_mouse_event(event_type EventType, btn MouseButton, mods u32) {
1466 if g_sapp_state.init_called && !g_sapp_state.cleanup_called {
1467 init_sapp_event(event_type)
1468 g_sapp_state.event.mouse_button = btn
1469 g_sapp_state.event.modifiers = mods
1470 call_sapp_event(&g_sapp_state.event)
1471 }
1472}
1473
1474fn x11_scroll_event(x f32, y f32, mods u32) {
1475 if g_sapp_state.init_called && !g_sapp_state.cleanup_called {
1476 init_sapp_event(.mouse_scroll)
1477 g_sapp_state.event.modifiers = mods
1478 g_sapp_state.event.scroll_x = x
1479 g_sapp_state.event.scroll_y = y
1480 call_sapp_event(&g_sapp_state.event)
1481 }
1482}
1483
1484fn x11_key_event(event_type EventType, key KeyCode, repeat bool, mods u32) {
1485 if g_sapp_state.init_called && !g_sapp_state.cleanup_called {
1486 init_sapp_event(event_type)
1487 g_sapp_state.event.key_code = key
1488 g_sapp_state.event.key_repeat = repeat
1489 g_sapp_state.event.modifiers = mods
1490 call_sapp_event(&g_sapp_state.event)
1491 // Check if a CLIPBOARD_PASTED event must be sent too
1492 if g_sapp_state.clipboard.enabled && event_type == .key_down
1493 && g_sapp_state.event.modifiers == u32(Modifier.ctrl)
1494 && g_sapp_state.event.key_code == .v {
1495 init_sapp_event(.clipboard_pasted)
1496 call_sapp_event(&g_sapp_state.event)
1497 }
1498 }
1499}
1500
1501fn x11_char_event(chr u32, repeat bool, mods u32) {
1502 if g_sapp_state.init_called && !g_sapp_state.cleanup_called {
1503 init_sapp_event(.char)
1504 g_sapp_state.event.char_code = chr
1505 g_sapp_state.event.key_repeat = repeat
1506 g_sapp_state.event.modifiers = mods
1507 call_sapp_event(&g_sapp_state.event)
1508 }
1509}
1510
1511fn x11_translate_key(scancode int) KeyCode {
1512 if scancode >= 0 && scancode < x11_max_keycodes {
1513 return g_sapp_state.keycodes[scancode]
1514 }
1515 return .invalid
1516}
1517
1518fn x11_keypress_repeat(keycode int) bool {
1519 mut repeat := false
1520 if keycode >= 0 && keycode < x11_max_keycodes {
1521 repeat = g_sapp_state.x11.key_repeat[keycode]
1522 g_sapp_state.x11.key_repeat[keycode] = true
1523 }
1524 return repeat
1525}
1526
1527fn x11_keyrelease_repeat(keycode int) {
1528 if keycode >= 0 && keycode < x11_max_keycodes {
1529 g_sapp_state.x11.key_repeat[keycode] = false
1530 }
1531}
1532
1533// === Window property helpers ===
1534
1535fn x11_get_window_property(window Window, property Atom, req_type Atom, value &&u8) usize {
1536 mut actual_type := Atom(0)
1537 mut actual_format := 0
1538 mut item_count := XlibULong(0)
1539 mut bytes_after := XlibULong(0)
1540 C.XGetWindowProperty(g_sapp_state.x11.display, window, property, 0, 0x7fffffff, x_false,
1541 req_type, &actual_type, &actual_format, &item_count, &bytes_after, value)
1542 return usize(item_count)
1543}
1544
1545fn x11_get_window_state() int {
1546 mut result := withdrawn_state
1547 mut state := &u8(unsafe { nil })
1548 if x11_get_window_property(g_sapp_state.x11.window, g_sapp_state.x11.wm_state,
1549 g_sapp_state.x11.wm_state, &&u8(&state)) >= 2 {
1550 unsafe {
1551 result = int(*(&u32(state)))
1552 }
1553 }
1554 if state != unsafe { nil } {
1555 C.XFree(state)
1556 }
1557 return result
1558}
1559
1560// === Dropped files ===
1561
1562fn x11_parse_dropped_files_list(src &char) bool {
1563 if src == unsafe { nil } || g_sapp_state.drop.buffer == unsafe { nil } {
1564 return false
1565 }
1566 // Clear drop buffer
1567 unsafe { C.memset(g_sapp_state.drop.buffer, 0, usize(g_sapp_state.drop.buf_size)) }
1568 g_sapp_state.drop.num_files = 0
1569
1570 mut err := false
1571 mut src_count := 0
1572 mut dst_ptr := g_sapp_state.drop.buffer
1573 dst_end_ptr := unsafe { g_sapp_state.drop.buffer + (g_sapp_state.drop.max_path_length - 1) }
1574 mut p := unsafe { src }
1575 for {
1576 src_chr := unsafe { *p }
1577 if src_chr == 0 {
1578 break
1579 }
1580 unsafe { p++ }
1581 src_count++
1582 mut dst_chr := u8(0)
1583 // Check leading 'file://'
1584 if src_count <= 7 {
1585 valid := (src_count == 1 && src_chr == `f`)
1586 || (src_count == 2 && src_chr == `i`)
1587 || (src_count == 3 && src_chr == `l`)
1588 || (src_count == 4 && src_chr == `e`)
1589 || (src_count == 5 && src_chr == `:`)
1590 || (src_count == 6 && src_chr == `/`)
1591 || (src_count == 7 && src_chr == `/`)
1592 if !valid {
1593 err = true
1594 break
1595 }
1596 } else if src_chr == `\r` {
1597 // skip
1598 } else if src_chr == `\n` {
1599 src_count = 0
1600 g_sapp_state.drop.num_files++
1601 if g_sapp_state.drop.num_files >= g_sapp_state.drop.max_files {
1602 break
1603 }
1604 dst_ptr = unsafe {
1605 g_sapp_state.drop.buffer +
1606 g_sapp_state.drop.num_files * g_sapp_state.drop.max_path_length
1607 }
1608 } else if src_chr == `%` {
1609 unsafe {
1610 if *p != 0 && *(p + 1) != 0 {
1611 mut digits := [3]u8{}
1612 digits[0] = u8(*p)
1613 digits[1] = u8(*(p + 1))
1614 digits[2] = 0
1615 p = &char(usize(p) + 2)
1616 dst_chr = u8(C.strtol(&char(&digits[0]), nil, 16))
1617 }
1618 }
1619 } else {
1620 dst_chr = u8(src_chr)
1621 }
1622 if dst_chr != 0 {
1623 if usize(dst_ptr) < usize(dst_end_ptr) {
1624 unsafe {
1625 *dst_ptr = char(dst_chr)
1626 dst_ptr++
1627 }
1628 } else {
1629 err = true
1630 break
1631 }
1632 }
1633 }
1634 if err {
1635 unsafe { C.memset(g_sapp_state.drop.buffer, 0, usize(g_sapp_state.drop.buf_size)) }
1636 g_sapp_state.drop.num_files = 0
1637 return false
1638 }
1639 return true
1640}
1641
1642// === Event handlers ===
1643
1644fn x11_on_genericevent(event &C.XEvent) {
1645 if g_sapp_state.mouse.locked && g_sapp_state.x11.xi.available {
1646 unsafe {
1647 if event.xcookie.extension == g_sapp_state.x11.xi.major_opcode {
1648 if C.XGetEventData(g_sapp_state.x11.display, &event.xcookie) != 0 {
1649 if event.xcookie.evtype == xi_raw_motion {
1650 re := &C.XIRawEvent(event.xcookie.data)
1651 if re.valuators.mask_len > 0 {
1652 mut values := re.raw_values
1653 if xi_mask_is_set(re.valuators.mask, 0) {
1654 g_sapp_state.mouse.dx = f32(*values)
1655 values = values + 1
1656 }
1657 if xi_mask_is_set(re.valuators.mask, 1) {
1658 g_sapp_state.mouse.dy = f32(*values)
1659 }
1660 x11_mouse_event(.mouse_move, .invalid, x11_mods(event.xmotion.state))
1661 }
1662 }
1663 C.XFreeEventData(g_sapp_state.x11.display, &event.xcookie)
1664 }
1665 }
1666 }
1667 }
1668}
1669
1670fn x11_on_focusin(event &C.XEvent) {
1671 unsafe {
1672 if event.xfocus.mode != notify_grab && event.xfocus.mode != notify_ungrab {
1673 x11_app_event(.focused)
1674 }
1675 }
1676}
1677
1678fn x11_on_focusout(event &C.XEvent) {
1679 if g_sapp_state.mouse.locked {
1680 x11_lock_mouse(false)
1681 }
1682 unsafe {
1683 if event.xfocus.mode != notify_grab && event.xfocus.mode != notify_ungrab {
1684 x11_app_event(.unfocused)
1685 }
1686 }
1687}
1688
1689fn x11_on_keypress(event &C.XEvent) {
1690 unsafe {
1691 keycode := int(event.xkey.keycode)
1692 keysym := x11_lookup_keysym(event)
1693 key := x11_translate_event_key(keycode, keysym)
1694 repeat := x11_keypress_repeat(keycode)
1695 mut mods := x11_mods(event.xkey.state)
1696 // X11 doesn't set modifier bit on key down, so emulate that
1697 mods |= x11_key_modifier_bit(key)
1698 if key != .invalid {
1699 x11_key_event(.key_down, key, repeat, mods)
1700 }
1701 chr := x11_keysym_to_unicode(keysym)
1702 if chr > 0 {
1703 x11_char_event(u32(chr), repeat, mods)
1704 }
1705 }
1706}
1707
1708fn x11_on_keyrelease(event &C.XEvent) {
1709 unsafe {
1710 keycode := int(event.xkey.keycode)
1711 keysym := x11_lookup_keysym(event)
1712 key := x11_translate_event_key(keycode, keysym)
1713 x11_keyrelease_repeat(keycode)
1714 if key != .invalid {
1715 mut mods := x11_mods(event.xkey.state)
1716 // X11 doesn't clear modifier bit on key up, so emulate that
1717 mods &= ~x11_key_modifier_bit(key)
1718 x11_key_event(.key_up, key, false, mods)
1719 }
1720 }
1721}
1722
1723fn x11_on_buttonpress(event &C.XEvent) {
1724 unsafe {
1725 x11_mouse_update(event.xbutton.x, event.xbutton.y, false)
1726 btn := x11_translate_button(event)
1727 mut mods := x11_mods(event.xbutton.state)
1728 mods |= x11_button_modifier_bit(btn)
1729 if btn != .invalid {
1730 x11_mouse_event(.mouse_down, btn, mods)
1731 g_sapp_state.x11.mouse_buttons |= u8(1 << int(btn))
1732 } else {
1733 // Might be a scroll event
1734 match event.xbutton.button {
1735 4 { x11_scroll_event(0.0, 1.0, mods) }
1736 5 { x11_scroll_event(0.0, -1.0, mods) }
1737 6 { x11_scroll_event(1.0, 0.0, mods) }
1738 7 { x11_scroll_event(-1.0, 0.0, mods) }
1739 else {}
1740 }
1741 }
1742 }
1743}
1744
1745fn x11_on_buttonrelease(event &C.XEvent) {
1746 unsafe {
1747 x11_mouse_update(event.xbutton.x, event.xbutton.y, false)
1748 btn := x11_translate_button(event)
1749 if btn != .invalid {
1750 mut mods := x11_mods(event.xbutton.state)
1751 mods &= ~x11_button_modifier_bit(btn)
1752 x11_mouse_event(.mouse_up, btn, mods)
1753 g_sapp_state.x11.mouse_buttons &= ~u8(1 << int(btn))
1754 }
1755 }
1756}
1757
1758fn x11_on_enternotify(event &C.XEvent) {
1759 if g_sapp_state.x11.mouse_buttons == 0 {
1760 unsafe {
1761 x11_mouse_update(event.xcrossing.x, event.xcrossing.y, true)
1762 x11_mouse_event(.mouse_enter, .invalid, x11_mods(event.xcrossing.state))
1763 }
1764 }
1765}
1766
1767fn x11_on_leavenotify(event &C.XEvent) {
1768 if g_sapp_state.x11.mouse_buttons == 0 {
1769 unsafe {
1770 x11_mouse_update(event.xcrossing.x, event.xcrossing.y, true)
1771 x11_mouse_event(.mouse_leave, .invalid, x11_mods(event.xcrossing.state))
1772 }
1773 }
1774}
1775
1776fn x11_on_motionnotify(event &C.XEvent) {
1777 if !g_sapp_state.mouse.locked {
1778 unsafe {
1779 x11_mouse_update(event.xmotion.x, event.xmotion.y, false)
1780 x11_mouse_event(.mouse_move, .invalid, x11_mods(event.xmotion.state))
1781 }
1782 }
1783}
1784
1785fn x11_on_propertynotify(event &C.XEvent) {
1786 unsafe {
1787 if event.xproperty.state == property_new_value {
1788 if event.xproperty.atom == g_sapp_state.x11.wm_state {
1789 state := x11_get_window_state()
1790 if state != g_sapp_state.x11.window_state {
1791 g_sapp_state.x11.window_state = state
1792 if state == iconic_state {
1793 x11_app_event(.iconified)
1794 } else if state == normal_state {
1795 x11_app_event(.restored)
1796 }
1797 }
1798 }
1799 }
1800 }
1801}
1802
1803fn x11_on_selectionnotify(event &C.XEvent) {
1804 unsafe {
1805 if event.xselection.property == g_sapp_state.x11.xdnd.xdnd_selection {
1806 mut data := &u8(nil)
1807 result := x11_get_window_property(event.xselection.requestor,
1808 event.xselection.property, event.xselection.target, &&u8(&data))
1809 if g_sapp_state.drop.enabled && result > 0 {
1810 if x11_parse_dropped_files_list(&char(data)) {
1811 g_sapp_state.mouse.dx = 0.0
1812 g_sapp_state.mouse.dy = 0.0
1813 if g_sapp_state.init_called && !g_sapp_state.cleanup_called {
1814 init_sapp_event(.files_dropped)
1815 call_sapp_event(&g_sapp_state.event)
1816 }
1817 }
1818 }
1819 if g_sapp_state.x11.xdnd.version >= 2 {
1820 mut reply := C.XEvent{}
1821 reply.@type = x_client_message
1822 reply.xclient.window = g_sapp_state.x11.xdnd.source
1823 reply.xclient.message_type = g_sapp_state.x11.xdnd.xdnd_finished
1824 reply.xclient.format = 32
1825 reply.xclient.data.l[0] = i64(g_sapp_state.x11.window)
1826 reply.xclient.data.l[1] = i64(result)
1827 reply.xclient.data.l[2] = i64(g_sapp_state.x11.xdnd.xdnd_action_copy)
1828 C.XSendEvent(g_sapp_state.x11.display, g_sapp_state.x11.xdnd.source, x_false,
1829 no_event_mask, &reply)
1830 C.XFlush(g_sapp_state.x11.display)
1831 }
1832 if data != nil {
1833 C.XFree(data)
1834 }
1835 }
1836 }
1837}
1838
1839fn x11_on_selectionrequest(event &C.XEvent) {
1840 unsafe {
1841 req := &event.xselectionrequest
1842 if req.selection != g_sapp_state.x11.clipboard_atom {
1843 return
1844 }
1845 if !g_sapp_state.clipboard.enabled {
1846 return
1847 }
1848 mut reply := C.XEvent{}
1849 reply.@type = x_selection_notify
1850 reply.xselection.requestor = req.requestor
1851 reply.xselection.property = req.property
1852 reply.xselection.target = req.target
1853
1854 if req.target == g_sapp_state.x11.utf8_string {
1855 C.XChangeProperty(g_sapp_state.x11.display, req.requestor, req.property,
1856 g_sapp_state.x11.utf8_string, 8, prop_mode_replace,
1857 &u8(g_sapp_state.clipboard.buffer), int(C.strlen(g_sapp_state.clipboard.buffer)))
1858 } else if req.target == g_sapp_state.x11.targets {
1859 C.XChangeProperty(g_sapp_state.x11.display, req.requestor, req.property, xa_atom, 32,
1860 prop_mode_replace, &u8(&g_sapp_state.x11.utf8_string), 1)
1861 } else {
1862 reply.xselection.property = x_none
1863 }
1864 C.XSendEvent(g_sapp_state.x11.display, req.requestor, x_false, 0, &reply)
1865 }
1866}
1867
1868fn x11_on_clientmessage(event &C.XEvent) {
1869 if C.XFilterEvent(event, Window(0)) != 0 {
1870 return
1871 }
1872 unsafe {
1873 if event.xclient.message_type == g_sapp_state.x11.wm_protocols {
1874 protocol := Atom(u64(event.xclient.data.l[0]))
1875 if protocol == g_sapp_state.x11.wm_delete_window {
1876 g_sapp_state.quit_requested = true
1877 }
1878 } else if event.xclient.message_type == g_sapp_state.x11.xdnd.xdnd_enter {
1879 is_list := (event.xclient.data.l[1] & 1) != 0
1880 g_sapp_state.x11.xdnd.source = Window(u64(event.xclient.data.l[0]))
1881 g_sapp_state.x11.xdnd.version = event.xclient.data.l[1] >> 24
1882 g_sapp_state.x11.xdnd.format = x_none
1883 if g_sapp_state.x11.xdnd.version > x11_xdnd_version {
1884 return
1885 }
1886 mut count := usize(0)
1887 mut formats := &Atom(nil)
1888 if is_list {
1889 count = x11_get_window_property(g_sapp_state.x11.xdnd.source,
1890 g_sapp_state.x11.xdnd.xdnd_type_list, xa_atom, &&u8(&formats))
1891 } else {
1892 count = 3
1893 formats = &Atom(voidptr(&event.xclient.data.l[2]))
1894 }
1895 for i in 0 .. count {
1896 if formats[i] == g_sapp_state.x11.xdnd.text_uri_list {
1897 g_sapp_state.x11.xdnd.format = g_sapp_state.x11.xdnd.text_uri_list
1898 break
1899 }
1900 }
1901 if is_list && formats != nil {
1902 C.XFree(formats)
1903 }
1904 } else if event.xclient.message_type == g_sapp_state.x11.xdnd.xdnd_drop {
1905 if g_sapp_state.x11.xdnd.version > x11_xdnd_version {
1906 return
1907 }
1908 if g_sapp_state.x11.xdnd.format != x_none {
1909 mut time := current_time
1910 if g_sapp_state.x11.xdnd.version >= 1 {
1911 time = Time(u64(event.xclient.data.l[2]))
1912 }
1913 C.XConvertSelection(g_sapp_state.x11.display, g_sapp_state.x11.xdnd.xdnd_selection,
1914 g_sapp_state.x11.xdnd.format, g_sapp_state.x11.xdnd.xdnd_selection,
1915 g_sapp_state.x11.window, time)
1916 } else if g_sapp_state.x11.xdnd.version >= 2 {
1917 mut reply := C.XEvent{}
1918 reply.@type = x_client_message
1919 reply.xclient.window = g_sapp_state.x11.xdnd.source
1920 reply.xclient.message_type = g_sapp_state.x11.xdnd.xdnd_finished
1921 reply.xclient.format = 32
1922 reply.xclient.data.l[0] = i64(g_sapp_state.x11.window)
1923 reply.xclient.data.l[1] = 0
1924 reply.xclient.data.l[2] = i64(x_none)
1925 C.XSendEvent(g_sapp_state.x11.display, g_sapp_state.x11.xdnd.source, x_false,
1926 no_event_mask, &reply)
1927 C.XFlush(g_sapp_state.x11.display)
1928 }
1929 } else if event.xclient.message_type == g_sapp_state.x11.xdnd.xdnd_position {
1930 if g_sapp_state.x11.xdnd.version > x11_xdnd_version {
1931 return
1932 }
1933 mut reply := C.XEvent{}
1934 reply.@type = x_client_message
1935 reply.xclient.window = g_sapp_state.x11.xdnd.source
1936 reply.xclient.message_type = g_sapp_state.x11.xdnd.xdnd_status
1937 reply.xclient.format = 32
1938 reply.xclient.data.l[0] = i64(g_sapp_state.x11.window)
1939 if g_sapp_state.x11.xdnd.format != x_none {
1940 reply.xclient.data.l[1] = 1
1941 if g_sapp_state.x11.xdnd.version >= 2 {
1942 reply.xclient.data.l[4] = i64(g_sapp_state.x11.xdnd.xdnd_action_copy)
1943 }
1944 }
1945 C.XSendEvent(g_sapp_state.x11.display, g_sapp_state.x11.xdnd.source, x_false,
1946 no_event_mask, &reply)
1947 C.XFlush(g_sapp_state.x11.display)
1948 }
1949 }
1950}
1951
1952// === Main event dispatcher ===
1953
1954fn x11_process_event(event &C.XEvent) {
1955 unsafe {
1956 match event.@type {
1957 x_generic_event { x11_on_genericevent(event) }
1958 x_focus_in { x11_on_focusin(event) }
1959 x_focus_out { x11_on_focusout(event) }
1960 x_key_press { x11_on_keypress(event) }
1961 x_key_release { x11_on_keyrelease(event) }
1962 x_button_press { x11_on_buttonpress(event) }
1963 x_button_release { x11_on_buttonrelease(event) }
1964 x_enter_notify { x11_on_enternotify(event) }
1965 x_leave_notify { x11_on_leavenotify(event) }
1966 x_motion_notify { x11_on_motionnotify(event) }
1967 x_property_notify { x11_on_propertynotify(event) }
1968 x_selection_notify { x11_on_selectionnotify(event) }
1969 x_selection_request { x11_on_selectionrequest(event) }
1970 x_destroy_notify {}
1971 x_client_message { x11_on_clientmessage(event) }
1972 else {}
1973 }
1974 }
1975}
1976
1977// === Frame and run functions ===
1978
1979fn x11_linux_frame() {
1980 x11_update_dimensions_from_window_size()
1981 sapp_do_frame()
1982 C.eglSwapBuffers(g_sapp_state.egl.display, g_sapp_state.egl.surface)
1983}
1984
1985fn x11_run() {
1986 C.XInitThreads()
1987 C.XrmInitialize()
1988 g_sapp_state.x11.display = C.XOpenDisplay(unsafe { nil })
1989 if g_sapp_state.x11.display == unsafe { nil } {
1990 eprintln('sokol_app: X11: failed to open display')
1991 return
1992 }
1993 g_sapp_state.x11.screen = C.XDefaultScreen(g_sapp_state.x11.display)
1994 g_sapp_state.x11.root = C.XDefaultRootWindow(g_sapp_state.x11.display)
1995 g_sapp_state.x11.window_state = normal_state
1996 x11_query_system_dpi()
1997 g_sapp_state.dpi_scale = g_sapp_state.x11.dpi / 96.0
1998 x11_init_extensions()
1999 x11_create_standard_cursors()
2000 C.XkbSetDetectableAutoRepeat(g_sapp_state.x11.display, x_true, unsafe { nil })
2001 x11_init_keytable()
2002
2003 // EGL init (creates window internally)
2004 sapp_egl_init_x11()
2005
2006 g_sapp_state.valid = true
2007 x11_show_window()
2008 if g_sapp_state.fullscreen {
2009 x11_set_fullscreen(true)
2010 }
2011 C.XFlush(g_sapp_state.x11.display)
2012
2013 for !g_sapp_state.quit_ordered {
2014 count := C.XPending(g_sapp_state.x11.display)
2015 for _ in 0 .. count {
2016 mut event := C.XEvent{}
2017 C.XNextEvent(g_sapp_state.x11.display, &event)
2018 x11_process_event(&event)
2019 }
2020 x11_linux_frame()
2021 C.XFlush(g_sapp_state.x11.display)
2022 // Handle quit-requested
2023 if g_sapp_state.quit_requested && !g_sapp_state.quit_ordered {
2024 x11_app_event(.quit_requested)
2025 if g_sapp_state.quit_requested {
2026 g_sapp_state.quit_ordered = true
2027 }
2028 }
2029 }
2030 sapp_call_cleanup()
2031 sapp_egl_destroy()
2032 x11_destroy_window()
2033 x11_destroy_standard_cursors()
2034 C.XCloseDisplay(g_sapp_state.x11.display)
2035 sapp_discard_state()
2036}
2037