v2 / vlib / sokol / sapp / sapp_state_linux.v
430 lines · 405 sloc · 12.65 KB · cf8fbc4d9d1c551f8be2d66d343693da9acdee27
Raw
1@[has_globals]
2module sapp
3
4// Linux-specific state structs for the V sokol_app backend.
5// Shared state (SappTiming, SappMouse, etc.) is in sapp_state.v.
6
7// X11 and XKB share keypad/navigation keysyms. Keep the stateful keypad mapping in one place so
8// Num Lock can flip keypad arrows between navigation keys and keypad digits consistently.
9fn linux_translate_navigation_or_keypad_keysym(keysym u32) KeyCode {
10 return match keysym {
11 0xff63, 0xff9e { .insert }
12 0xffff, 0xff9f { .delete }
13 0xff50, 0xff95 { .home }
14 0xff57, 0xff9c { .end }
15 0xff55, 0xff9a { .page_up }
16 0xff56, 0xff9b { .page_down }
17 0xff51, 0xff96 { .left }
18 0xff53, 0xff98 { .right }
19 0xff52, 0xff97 { .up }
20 0xff54, 0xff99 { .down }
21 0xffb0 { .kp_0 }
22 0xffb1 { .kp_1 }
23 0xffb2 { .kp_2 }
24 0xffb3 { .kp_3 }
25 0xff9d, 0xffb5 { .kp_5 }
26 0xffb4 { .kp_4 }
27 0xffb6 { .kp_6 }
28 0xffb7 { .kp_7 }
29 0xffb8 { .kp_8 }
30 0xffb9 { .kp_9 }
31 0xffac, 0xffae { .kp_decimal }
32 0xffaf { .kp_divide }
33 0xffaa { .kp_multiply }
34 0xffad { .kp_subtract }
35 0xffab { .kp_add }
36 0xff8d { .kp_enter }
37 0xffbd { .kp_equal }
38 else { .invalid }
39 }
40}
41
42// Wayland-specific state (only compiled when -d sokol_wayland is used)
43$if sokol_wayland ? {
44 struct SappWayland {
45 mut:
46 // Core Wayland objects
47 display &C.wl_display = unsafe { nil }
48 registry &C.wl_registry = unsafe { nil }
49 compositor &C.wl_compositor = unsafe { nil }
50 surface &C.wl_surface = unsafe { nil }
51 egl_window &C.wl_egl_window = unsafe { nil }
52 shm &C.wl_shm = unsafe { nil }
53 // XDG shell
54 xdg_wm_base &C.xdg_wm_base = unsafe { nil }
55 xdg_surface &C.xdg_surface = unsafe { nil }
56 xdg_toplevel &C.xdg_toplevel = unsafe { nil }
57 // Input
58 seat &C.wl_seat = unsafe { nil }
59 pointer &C.wl_pointer = unsafe { nil }
60 keyboard &C.wl_keyboard = unsafe { nil }
61 touch &C.wl_touch = unsafe { nil }
62 seat_version u32
63 // Pointer constraints
64 pointer_constraints &C.zwp_pointer_constraints_v1 = unsafe { nil }
65 locked_pointer &C.zwp_locked_pointer_v1 = unsafe { nil }
66 relative_pointer_mgr &C.zwp_relative_pointer_manager_v1 = unsafe { nil }
67 relative_pointer &C.zwp_relative_pointer_v1 = unsafe { nil }
68 // XKB keyboard handling
69 xkb_context &C.xkb_context = unsafe { nil }
70 xkb_keymap &C.xkb_keymap = unsafe { nil }
71 xkb_state &C.xkb_state = unsafe { nil }
72 xkb_compose_table &C.xkb_compose_table = unsafe { nil }
73 xkb_compose_state &C.xkb_compose_state = unsafe { nil }
74 // Cursor
75 cursor_shape_manager &C.wp_cursor_shape_manager_v1 = unsafe { nil }
76 cursor_shape_device &C.wp_cursor_shape_device_v1 = unsafe { nil }
77 cursor_theme &C.wl_cursor_theme = unsafe { nil }
78 cursor_default &C.wl_cursor = unsafe { nil }
79 cursor_surface &C.wl_surface = unsafe { nil }
80 cursor_size int
81 // Fractional scale
82 fractional_scale_mgr &C.wp_fractional_scale_manager_v1 = unsafe { nil }
83 fractional_scale &C.wp_fractional_scale_v1 = unsafe { nil }
84 scale_numerator u32
85 // Viewporter
86 viewporter &C.wp_viewporter = unsafe { nil }
87 viewport &C.wp_viewport = unsafe { nil }
88 // Decorations
89 decoration_manager &C.zxdg_decoration_manager_v1 = unsafe { nil }
90 toplevel_decoration &C.zxdg_toplevel_decoration_v1 = unsafe { nil }
91 // Clipboard / data device
92 data_device_manager &C.wl_data_device_manager = unsafe { nil }
93 data_device &C.wl_data_device = unsafe { nil }
94 data_source &C.wl_data_source = unsafe { nil }
95 data_offer &C.wl_data_offer = unsafe { nil }
96 clipboard_string &char = unsafe { nil }
97 clipboard_size int
98 // Window state
99 width int
100 height int
101 fb_width int
102 fb_height int
103 scale f32 = 1.0
104 configured bool
105 closed bool
106 fullscreen bool
107 maximized bool
108 focused bool
109 // Pointer state
110 pointer_x f32
111 pointer_y f32
112 pointer_enter_serial u32
113 // Key repeat
114 key_repeat_rate int = 25
115 key_repeat_delay int = 600
116 key_repeat_timer_fd int = -1
117 key_repeat_keycode u32
118 }
119} $else {
120 // Dummy struct for non-Wayland builds
121 struct SappWayland {}
122}
123
124// X11 XInput extension state
125struct SappXi {
126mut:
127 available bool
128 major_opcode int
129 event_base int
130 error_base int
131 major int
132 minor int
133}
134
135// X11 XDnD state
136struct SappXdnd {
137mut:
138 version i64
139 source Window
140 format Atom
141 xdnd_aware Atom
142 xdnd_enter Atom
143 xdnd_position Atom
144 xdnd_status Atom
145 xdnd_action_copy Atom
146 xdnd_drop Atom
147 xdnd_finished Atom
148 xdnd_selection Atom
149 xdnd_type_list Atom
150 text_uri_list Atom
151}
152
153// X11-specific state
154struct SappX11 {
155mut:
156 mouse_buttons u8
157 display &C.Display = unsafe { nil }
158 screen int
159 root Window
160 colormap Colormap
161 window Window
162 hidden_cursor Cursor
163 standard_cursors [mousecursor_num]Cursor
164 custom_cursors [mousecursor_num]Cursor
165 window_state int
166 dpi f32
167 error_code u8
168 // X atoms
169 utf8_string Atom
170 clipboard_atom Atom
171 targets Atom
172 wm_protocols Atom
173 wm_delete_window Atom
174 wm_state Atom
175 net_wm_name Atom
176 net_wm_icon_name Atom
177 net_wm_icon Atom
178 net_wm_state Atom
179 net_wm_state_fullscreen Atom
180 // Extensions
181 xi SappXi
182 xdnd SappXdnd
183 // Key repeat tracking (keycodes 0..255)
184 key_repeat [256]bool
185}
186
187// EGL state
188struct SappEgl {
189mut:
190 display EGLDisplay
191 context EGLContext
192 surface EGLSurface
193 config EGLConfig
194}
195
196// Main application state - Linux version with Wayland/X11/EGL backends
197struct SappState {
198mut:
199 desc Desc
200 valid bool
201 fullscreen bool
202 first_frame bool
203 init_called bool
204 cleanup_called bool
205 quit_requested bool
206 quit_ordered bool
207 event_consumed bool
208 html5_ask_leave_site bool
209 onscreen_keyboard_shown bool
210 window_width int
211 window_height int
212 framebuffer_width int
213 framebuffer_height int
214 sample_count int
215 swap_interval int
216 dpi_scale f32 = 1.0
217 frame_count u64
218 timing SappTiming
219 event Event
220 mouse SappMouse
221 clipboard SappClipboard
222 drop SappDrop
223 // Platform-specific state
224 wl SappWayland
225 x11 SappX11
226 egl SappEgl
227 gl SappGl
228 // Keycode translation table
229 keycodes [max_keycodes]KeyCode
230 // Window title
231 window_title [max_title_length]u8
232 // V patch
233 v_native_render bool
234 // Custom cursors
235 custom_cursor_bound [mousecursor_num]bool
236}
237
238// The global state instance
239__global g_sapp_state = SappState{}
240
241// === Shared helper functions ===
242
243// init_sapp_event initializes an event struct with common fields.
244fn init_sapp_event(event_type EventType) {
245 unsafe { C.memset(&g_sapp_state.event, 0, int(sizeof(g_sapp_state.event))) }
246 g_sapp_state.event.@type = event_type
247 g_sapp_state.event.frame_count = g_sapp_state.frame_count
248 g_sapp_state.event.mouse_button = .invalid
249 g_sapp_state.event.window_width = g_sapp_state.window_width
250 g_sapp_state.event.window_height = g_sapp_state.window_height
251 g_sapp_state.event.framebuffer_width = g_sapp_state.framebuffer_width
252 g_sapp_state.event.framebuffer_height = g_sapp_state.framebuffer_height
253 g_sapp_state.event.mouse_x = g_sapp_state.mouse.x
254 g_sapp_state.event.mouse_y = g_sapp_state.mouse.y
255 g_sapp_state.event.mouse_dx = g_sapp_state.mouse.dx
256 g_sapp_state.event.mouse_dy = g_sapp_state.mouse.dy
257}
258
259// call_sapp_event dispatches the event to the user's callback.
260fn call_sapp_event(e &Event) bool {
261 if !g_sapp_state.cleanup_called {
262 if g_sapp_state.desc.event_cb != unsafe { nil } {
263 g_sapp_state.desc.event_cb(e)
264 } else if g_sapp_state.desc.event_userdata_cb != unsafe { nil } {
265 g_sapp_state.desc.event_userdata_cb(e, g_sapp_state.desc.user_data)
266 }
267 }
268 if g_sapp_state.event_consumed {
269 g_sapp_state.event_consumed = false
270 return true
271 }
272 return false
273}
274
275// sapp_call_init calls the user's init callback.
276fn sapp_call_init() {
277 if g_sapp_state.desc.init_cb != unsafe { nil } {
278 g_sapp_state.desc.init_cb()
279 } else if g_sapp_state.desc.init_userdata_cb != unsafe { nil } {
280 g_sapp_state.desc.init_userdata_cb(g_sapp_state.desc.user_data)
281 }
282 g_sapp_state.init_called = true
283}
284
285// sapp_call_frame calls the user's frame callback.
286fn sapp_call_frame() {
287 if g_sapp_state.desc.frame_cb != unsafe { nil } {
288 g_sapp_state.desc.frame_cb()
289 } else if g_sapp_state.desc.frame_userdata_cb != unsafe { nil } {
290 g_sapp_state.desc.frame_userdata_cb(g_sapp_state.desc.user_data)
291 }
292}
293
294// sapp_call_cleanup calls the user's cleanup callback.
295fn sapp_call_cleanup() {
296 if !g_sapp_state.cleanup_called {
297 if g_sapp_state.desc.cleanup_cb != unsafe { nil } {
298 g_sapp_state.desc.cleanup_cb()
299 } else if g_sapp_state.desc.cleanup_userdata_cb != unsafe { nil } {
300 g_sapp_state.desc.cleanup_userdata_cb(g_sapp_state.desc.user_data)
301 }
302 g_sapp_state.cleanup_called = true
303 }
304}
305
306// sapp_do_frame handles the per-frame logic.
307fn sapp_do_frame() {
308 if g_sapp_state.first_frame {
309 g_sapp_state.first_frame = false
310 sapp_call_init()
311 }
312 sapp_call_frame()
313 g_sapp_state.frame_count++
314}
315
316// === Timing helpers ===
317
318fn sapp_timing_reset(t &SappTiming) {
319 unsafe {
320 mut mt := t
321 mt.last = 0.0
322 mt.accum = 0.0
323 mt.avg = 0.0
324 mt.spike_count = 0
325 mt.num = 0
326 mt.ring_head = 0
327 mt.ring_tail = 0
328 }
329}
330
331fn sapp_timing_get_avg(t &SappTiming) f64 {
332 return t.avg
333}
334
335// === Drop file helpers ===
336
337fn sapp_dropped_file_path_ptr(index int) &char {
338 assert g_sapp_state.drop.buffer != unsafe { nil }
339 assert index >= 0 && index <= g_sapp_state.drop.max_files
340 offset := index * g_sapp_state.drop.max_path_length
341 assert offset < g_sapp_state.drop.buf_size
342 return unsafe { &char(g_sapp_state.drop.buffer + offset) }
343}
344
345// === State initialization ===
346
347fn sapp_desc_defaults(desc &Desc) Desc {
348 mut res := *desc
349 if res.sample_count == 0 {
350 res.sample_count = 1
351 }
352 if res.swap_interval == 0 {
353 res.swap_interval = 1
354 }
355 if res.gl.major_version == 0 {
356 res.gl.major_version = 4
357 res.gl.minor_version = 1
358 }
359 if res.clipboard_size == 0 {
360 res.clipboard_size = 8192
361 }
362 if res.max_dropped_files == 0 {
363 res.max_dropped_files = 1
364 }
365 if res.max_dropped_file_path_length == 0 {
366 res.max_dropped_file_path_length = 2048
367 }
368 return res
369}
370
371fn sapp_init_state(desc &Desc) {
372 unsafe { C.memset(&g_sapp_state, 0, int(sizeof(g_sapp_state))) }
373 g_sapp_state.desc = sapp_desc_defaults(desc)
374 g_sapp_state.first_frame = true
375 g_sapp_state.window_width = g_sapp_state.desc.width
376 g_sapp_state.window_height = g_sapp_state.desc.height
377 g_sapp_state.framebuffer_width = g_sapp_state.window_width
378 g_sapp_state.framebuffer_height = g_sapp_state.window_height
379 g_sapp_state.sample_count = g_sapp_state.desc.sample_count
380 g_sapp_state.swap_interval = g_sapp_state.desc.swap_interval
381 g_sapp_state.clipboard.enabled = g_sapp_state.desc.enable_clipboard
382 if g_sapp_state.clipboard.enabled {
383 g_sapp_state.clipboard.buf_size = g_sapp_state.desc.clipboard_size
384 g_sapp_state.clipboard.buffer = unsafe {
385 &char(C.calloc(1, usize(g_sapp_state.clipboard.buf_size)))
386 }
387 }
388 g_sapp_state.drop.enabled = g_sapp_state.desc.enable_dragndrop
389 if g_sapp_state.drop.enabled {
390 g_sapp_state.drop.max_files = g_sapp_state.desc.max_dropped_files
391 g_sapp_state.drop.max_path_length = g_sapp_state.desc.max_dropped_file_path_length
392 g_sapp_state.drop.buf_size = g_sapp_state.drop.max_files * g_sapp_state.drop.max_path_length
393 g_sapp_state.drop.buffer = unsafe { &char(C.calloc(1, usize(g_sapp_state.drop.buf_size))) }
394 }
395 // Copy window title
396 if g_sapp_state.desc.window_title != unsafe { nil }
397 && unsafe { C.strlen(g_sapp_state.desc.window_title) } > 0 {
398 title := g_sapp_state.desc.window_title
399 mut i := 0
400 for i < max_title_length - 1 {
401 ch := unsafe { title[i] }
402 if ch == 0 {
403 break
404 }
405 g_sapp_state.window_title[i] = u8(ch)
406 i++
407 }
408 g_sapp_state.window_title[i] = 0
409 } else {
410 // Default title
411 g_sapp_state.window_title[0] = `s`
412 g_sapp_state.window_title[1] = `o`
413 g_sapp_state.window_title[2] = `k`
414 g_sapp_state.window_title[3] = `o`
415 g_sapp_state.window_title[4] = `l`
416 g_sapp_state.window_title[5] = 0
417 }
418 g_sapp_state.dpi_scale = 1.0
419 g_sapp_state.fullscreen = g_sapp_state.desc.fullscreen
420 g_sapp_state.mouse.shown = true
421}
422
423fn sapp_discard_state() {
424 if g_sapp_state.clipboard.enabled && g_sapp_state.clipboard.buffer != unsafe { nil } {
425 unsafe { C.free(g_sapp_state.clipboard.buffer) }
426 }
427 if g_sapp_state.drop.enabled && g_sapp_state.drop.buffer != unsafe { nil } {
428 unsafe { C.free(g_sapp_state.drop.buffer) }
429 }
430}
431