@matjash/pixi-native-linux-x64 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,10 @@
1
+ use std::ffi::{CStr, CString};
2
+ use std::sync::atomic::{AtomicUsize, Ordering};
3
+
1
4
  use napi::bindgen_prelude::FunctionRef;
2
5
  use napi::bindgen_prelude::*;
3
6
  use napi_derive::napi;
7
+ use sdl3_sys::everything::*;
4
8
 
5
9
  #[cfg(windows)]
6
10
  use windows_sys::Win32::Foundation::FreeLibrary;
@@ -21,19 +25,651 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
21
25
  WM_EXITSIZEMOVE, WM_TIMER, WNDPROC,
22
26
  };
23
27
 
28
+ static WINDOW_COUNT: AtomicUsize = AtomicUsize::new(0);
29
+ const DEFAULT_COMPOSITOR_WAIT_MS: u32 = 1_000;
30
+
31
+ #[napi(object)]
32
+ pub struct NativeWindowOptions {
33
+ pub title: String,
34
+ pub width: i32,
35
+ pub height: i32,
36
+ pub resizable: bool,
37
+ pub borderless: bool,
38
+ pub transparent: bool,
39
+ pub x: Option<i32>,
40
+ pub y: Option<i32>,
41
+ pub graphics: String,
42
+ }
43
+
44
+ #[napi(object)]
45
+ pub struct NativeSurfaceDescriptor {
46
+ pub version: u32,
47
+ pub api: String,
48
+ pub window: Buffer,
49
+ pub display: Option<Buffer>,
50
+ pub instance: Option<Buffer>,
51
+ pub egl_window: Option<Buffer>,
52
+ }
53
+
54
+ #[napi(object)]
55
+ pub struct NativeWindowEvent {
56
+ pub kind: String,
57
+ pub window_id: u32,
58
+ pub x: Option<f64>,
59
+ pub y: Option<f64>,
60
+ pub dx: Option<f64>,
61
+ pub dy: Option<f64>,
62
+ pub button: Option<u32>,
63
+ pub key: Option<String>,
64
+ pub scancode: Option<u32>,
65
+ pub repeat: Option<bool>,
66
+ pub shift: Option<bool>,
67
+ pub ctrl: Option<bool>,
68
+ pub alt: Option<bool>,
69
+ pub super_key: Option<bool>,
70
+ pub flipped: Option<bool>,
71
+ }
72
+
73
+ fn sdl_error(context: &str) -> Error {
74
+ let message = unsafe {
75
+ let error = SDL_GetError();
76
+ if error.is_null() {
77
+ "unknown SDL error".to_owned()
78
+ } else {
79
+ CStr::from_ptr(error).to_string_lossy().into_owned()
80
+ }
81
+ };
82
+ Error::from_reason(format!("{context}: {message}"))
83
+ }
84
+
85
+ fn pointer_buffer(pointer: usize) -> Buffer {
86
+ Buffer::from(pointer.to_ne_bytes().to_vec())
87
+ }
88
+
24
89
  #[cfg(windows)]
25
90
  fn hwnd_from_native_data(native_data: &[u8]) -> Result<HWND> {
26
91
  if native_data.len() < std::mem::size_of::<usize>() {
27
92
  return Err(Error::from_reason("native window data is invalid"));
28
93
  }
29
- let hwnd = unsafe { *(native_data.as_ptr() as *const usize) as HWND };
94
+ let mut bytes = [0_u8; std::mem::size_of::<usize>()];
95
+ bytes.copy_from_slice(&native_data[..std::mem::size_of::<usize>()]);
96
+ let hwnd = usize::from_ne_bytes(bytes) as HWND;
30
97
  if hwnd.is_null() {
31
98
  return Err(Error::from_reason("native window handle is null"));
32
99
  }
33
100
  Ok(hwnd)
34
101
  }
35
102
 
36
- const DEFAULT_COMPOSITOR_WAIT_MS: u32 = 1_000;
103
+ fn current_video_driver() -> String {
104
+ unsafe {
105
+ let driver = SDL_GetCurrentVideoDriver();
106
+ if driver.is_null() {
107
+ "unknown".to_owned()
108
+ } else {
109
+ CStr::from_ptr(driver).to_string_lossy().into_owned()
110
+ }
111
+ }
112
+ }
113
+
114
+ fn set_bool_property(properties: SDL_PropertiesID, name: *const i8, value: bool) -> Result<()> {
115
+ if unsafe { SDL_SetBooleanProperty(properties, name, value) } {
116
+ Ok(())
117
+ } else {
118
+ Err(sdl_error("could not set SDL window property"))
119
+ }
120
+ }
121
+
122
+ fn set_number_property(properties: SDL_PropertiesID, name: *const i8, value: i64) -> Result<()> {
123
+ if unsafe { SDL_SetNumberProperty(properties, name, value) } {
124
+ Ok(())
125
+ } else {
126
+ Err(sdl_error("could not set SDL window property"))
127
+ }
128
+ }
129
+
130
+ #[napi]
131
+ pub struct NativeSdlWindow {
132
+ window: usize,
133
+ gl_context: usize,
134
+ id: u32,
135
+ transparent: bool,
136
+ }
137
+
138
+ #[napi]
139
+ impl NativeSdlWindow {
140
+ #[napi(constructor)]
141
+ pub fn new(options: NativeWindowOptions) -> Result<Self> {
142
+ if options.width <= 0 || options.height <= 0 {
143
+ return Err(Error::from_reason(
144
+ "window width and height must be positive",
145
+ ));
146
+ }
147
+ if options.graphics != "webgpu" && options.graphics != "webgl" {
148
+ return Err(Error::from_reason("graphics must be webgpu or webgl"));
149
+ }
150
+ let use_webgl = options.graphics == "webgl";
151
+ if use_webgl {
152
+ // Keep SDL's context on the same GLES/EGL implementation as the
153
+ // generated native-gles call table (ANGLE on Windows).
154
+ unsafe {
155
+ SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, c"1".as_ptr());
156
+ SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, c"1".as_ptr());
157
+ }
158
+ }
159
+ if WINDOW_COUNT.load(Ordering::SeqCst) == 0 && !unsafe { SDL_Init(SDL_INIT_VIDEO) } {
160
+ return Err(sdl_error("could not initialize SDL3 video"));
161
+ }
162
+ // SDL's X11 backend only asks EGL for a transparent ARGB visual when
163
+ // the window has the OpenGL flag. WebGPU still owns the external
164
+ // Vulkan context; this flag is used only for SDL's visual selection.
165
+ let use_x11_transparent_visual = cfg!(target_os = "linux")
166
+ && !use_webgl
167
+ && options.transparent
168
+ && current_video_driver() == "x11";
169
+ if use_x11_transparent_visual {
170
+ unsafe {
171
+ SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, c"1".as_ptr());
172
+ SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, c"1".as_ptr());
173
+ }
174
+ }
175
+ if use_webgl || use_x11_transparent_visual {
176
+ for (attribute, value) in [
177
+ (SDL_GL_CONTEXT_MAJOR_VERSION, 3),
178
+ (SDL_GL_CONTEXT_MINOR_VERSION, 0),
179
+ (
180
+ SDL_GL_CONTEXT_PROFILE_MASK,
181
+ SDL_GL_CONTEXT_PROFILE_ES.0 as i32,
182
+ ),
183
+ (SDL_GL_ALPHA_SIZE, 8),
184
+ (SDL_GL_DEPTH_SIZE, 24),
185
+ (SDL_GL_STENCIL_SIZE, 8),
186
+ (SDL_GL_DOUBLEBUFFER, 1),
187
+ (SDL_GL_EGL_PLATFORM, 1),
188
+ ] {
189
+ if !unsafe { SDL_GL_SetAttribute(attribute, value) } {
190
+ return Err(sdl_error("could not configure the SDL3 OpenGL ES context"));
191
+ }
192
+ }
193
+ }
194
+
195
+ let properties = unsafe { SDL_CreateProperties() };
196
+ if properties == 0 {
197
+ return Err(sdl_error("could not allocate SDL3 window properties"));
198
+ }
199
+ let title = CString::new(options.title)
200
+ .map_err(|_| Error::from_reason("window title contains a null byte"))?;
201
+
202
+ let result = (|| {
203
+ if !unsafe {
204
+ SDL_SetStringProperty(
205
+ properties,
206
+ SDL_PROP_WINDOW_CREATE_TITLE_STRING,
207
+ title.as_ptr(),
208
+ )
209
+ } {
210
+ return Err(sdl_error("could not set SDL window title"));
211
+ }
212
+ set_number_property(
213
+ properties,
214
+ SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER,
215
+ options.width.into(),
216
+ )?;
217
+ set_number_property(
218
+ properties,
219
+ SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER,
220
+ options.height.into(),
221
+ )?;
222
+ set_bool_property(
223
+ properties,
224
+ SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN,
225
+ options.resizable,
226
+ )?;
227
+ set_bool_property(
228
+ properties,
229
+ SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN,
230
+ options.borderless,
231
+ )?;
232
+ set_bool_property(
233
+ properties,
234
+ SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN,
235
+ options.transparent,
236
+ )?;
237
+ set_bool_property(
238
+ properties,
239
+ SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN,
240
+ true,
241
+ )?;
242
+ if use_webgl || use_x11_transparent_visual {
243
+ set_bool_property(properties, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true)?;
244
+ }
245
+ if !use_webgl {
246
+ set_bool_property(
247
+ properties,
248
+ SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN,
249
+ true,
250
+ )?;
251
+ }
252
+ if let (Some(x), Some(y)) = (options.x, options.y) {
253
+ set_number_property(properties, SDL_PROP_WINDOW_CREATE_X_NUMBER, x.into())?;
254
+ set_number_property(properties, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y.into())?;
255
+ }
256
+
257
+ let window = unsafe { SDL_CreateWindowWithProperties(properties) };
258
+ if window.is_null() {
259
+ return Err(sdl_error("could not create SDL3 window"));
260
+ }
261
+ let id = unsafe { SDL_GetWindowID(window) };
262
+ if id == 0 {
263
+ unsafe { SDL_DestroyWindow(window) };
264
+ return Err(sdl_error("SDL3 window has no id"));
265
+ }
266
+ let gl_context = if use_webgl {
267
+ let context = unsafe { SDL_GL_CreateContext(window) };
268
+ if context.is_null() {
269
+ unsafe { SDL_DestroyWindow(window) };
270
+ return Err(sdl_error("could not create the SDL3 OpenGL ES context"));
271
+ }
272
+ if !unsafe { SDL_GL_MakeCurrent(window, context) } {
273
+ unsafe {
274
+ SDL_GL_DestroyContext(context);
275
+ SDL_DestroyWindow(window);
276
+ }
277
+ return Err(sdl_error(
278
+ "could not make the SDL3 OpenGL ES context current",
279
+ ));
280
+ }
281
+ context as usize
282
+ } else {
283
+ 0
284
+ };
285
+ let actual_transparent =
286
+ unsafe { (SDL_GetWindowFlags(window).0 & SDL_WINDOW_TRANSPARENT.0) != 0 };
287
+ Ok(Self {
288
+ window: window as usize,
289
+ gl_context,
290
+ id: id.into(),
291
+ transparent: actual_transparent,
292
+ })
293
+ })();
294
+ unsafe { SDL_DestroyProperties(properties) };
295
+
296
+ match result {
297
+ Ok(window) => {
298
+ WINDOW_COUNT.fetch_add(1, Ordering::SeqCst);
299
+ Ok(window)
300
+ }
301
+ Err(error) => {
302
+ if WINDOW_COUNT.load(Ordering::SeqCst) == 0 {
303
+ unsafe { SDL_QuitSubSystem(SDL_INIT_VIDEO) };
304
+ }
305
+ Err(error)
306
+ }
307
+ }
308
+ }
309
+
310
+ #[napi(getter)]
311
+ pub fn id(&self) -> u32 {
312
+ self.id
313
+ }
314
+
315
+ #[napi(getter)]
316
+ pub fn destroyed(&self) -> bool {
317
+ self.window == 0
318
+ }
319
+
320
+ #[napi(getter)]
321
+ pub fn video_driver(&self) -> String {
322
+ current_video_driver()
323
+ }
324
+
325
+ #[napi(getter)]
326
+ pub fn transparent(&self) -> bool {
327
+ self.transparent
328
+ }
329
+
330
+ #[napi(getter)]
331
+ pub fn x(&self) -> Result<i32> {
332
+ Ok(self.position()?.0)
333
+ }
334
+
335
+ #[napi(getter)]
336
+ pub fn y(&self) -> Result<i32> {
337
+ Ok(self.position()?.1)
338
+ }
339
+
340
+ #[napi(getter)]
341
+ pub fn pixel_width(&self) -> Result<i32> {
342
+ Ok(self.pixel_size()?.0)
343
+ }
344
+
345
+ #[napi(getter)]
346
+ pub fn pixel_height(&self) -> Result<i32> {
347
+ Ok(self.pixel_size()?.1)
348
+ }
349
+
350
+ #[napi(getter)]
351
+ pub fn refresh_rate(&self) -> f64 {
352
+ let window = self.window_pointer();
353
+ if window.is_null() {
354
+ return 60.0;
355
+ }
356
+ unsafe {
357
+ let mode = SDL_GetCurrentDisplayMode(SDL_GetDisplayForWindow(window));
358
+ if mode.is_null() || (*mode).refresh_rate <= 0.0 {
359
+ 60.0
360
+ } else {
361
+ (*mode).refresh_rate.into()
362
+ }
363
+ }
364
+ }
365
+
366
+ #[napi(getter)]
367
+ pub fn surface(&self) -> Result<NativeSurfaceDescriptor> {
368
+ let properties = unsafe { SDL_GetWindowProperties(self.require_window()?) };
369
+ if properties == 0 {
370
+ return Err(sdl_error("could not read SDL3 window properties"));
371
+ }
372
+ let driver = current_video_driver();
373
+ let pointer = |name| unsafe {
374
+ SDL_GetPointerProperty(properties, name, std::ptr::null_mut()) as usize
375
+ };
376
+ let number = |name| unsafe { SDL_GetNumberProperty(properties, name, 0) as usize };
377
+ match driver.as_str() {
378
+ "windows" => {
379
+ let hwnd = pointer(SDL_PROP_WINDOW_WIN32_HWND_POINTER);
380
+ let instance = pointer(SDL_PROP_WINDOW_WIN32_INSTANCE_POINTER);
381
+ if hwnd == 0 || instance == 0 {
382
+ return Err(sdl_error("SDL3 did not expose Win32 window handles"));
383
+ }
384
+ Ok(NativeSurfaceDescriptor {
385
+ version: 1,
386
+ api: "win32".to_owned(),
387
+ window: pointer_buffer(hwnd),
388
+ display: None,
389
+ instance: Some(pointer_buffer(instance)),
390
+ egl_window: None,
391
+ })
392
+ }
393
+ "x11" => {
394
+ let display = pointer(SDL_PROP_WINDOW_X11_DISPLAY_POINTER);
395
+ let xwindow = number(SDL_PROP_WINDOW_X11_WINDOW_NUMBER);
396
+ if display == 0 || xwindow == 0 {
397
+ return Err(sdl_error("SDL3 did not expose X11 window handles"));
398
+ }
399
+ Ok(NativeSurfaceDescriptor {
400
+ version: 1,
401
+ api: "x11".to_owned(),
402
+ window: pointer_buffer(xwindow),
403
+ display: Some(pointer_buffer(display)),
404
+ instance: None,
405
+ egl_window: None,
406
+ })
407
+ }
408
+ "wayland" => {
409
+ let display = pointer(SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER);
410
+ let surface = pointer(SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER);
411
+ let egl_window = pointer(SDL_PROP_WINDOW_WAYLAND_EGL_WINDOW_POINTER);
412
+ if display == 0 || surface == 0 {
413
+ return Err(sdl_error("SDL3 did not expose Wayland window handles"));
414
+ }
415
+ Ok(NativeSurfaceDescriptor {
416
+ version: 1,
417
+ api: "wayland".to_owned(),
418
+ window: pointer_buffer(surface),
419
+ display: Some(pointer_buffer(display)),
420
+ instance: None,
421
+ egl_window: (egl_window != 0).then(|| pointer_buffer(egl_window)),
422
+ })
423
+ }
424
+ _ => Err(Error::from_reason(format!(
425
+ "unsupported SDL3 video driver: {driver}"
426
+ ))),
427
+ }
428
+ }
429
+
430
+ #[napi]
431
+ pub fn set_position(&self, x: i32, y: i32) -> Result<bool> {
432
+ if current_video_driver() == "wayland" {
433
+ return Ok(false);
434
+ }
435
+ if unsafe { SDL_SetWindowPosition(self.require_window()?, x, y) } {
436
+ Ok(true)
437
+ } else {
438
+ Err(sdl_error("could not move SDL3 window"))
439
+ }
440
+ }
441
+
442
+ #[napi]
443
+ pub fn minimize(&self) -> Result<()> {
444
+ self.window_action(SDL_MinimizeWindow, "could not minimize SDL3 window")
445
+ }
446
+
447
+ #[napi]
448
+ pub fn maximize(&self) -> Result<()> {
449
+ self.window_action(SDL_MaximizeWindow, "could not maximize SDL3 window")
450
+ }
451
+
452
+ #[napi]
453
+ pub fn restore(&self) -> Result<()> {
454
+ self.window_action(SDL_RestoreWindow, "could not restore SDL3 window")
455
+ }
456
+
457
+ #[napi]
458
+ pub fn make_gl_current(&self) -> Result<bool> {
459
+ if self.gl_context == 0 {
460
+ return Err(Error::from_reason("window has no SDL3 OpenGL context"));
461
+ }
462
+ Ok(unsafe { SDL_GL_MakeCurrent(self.require_window()?, self.gl_context as SDL_GLContext) })
463
+ }
464
+
465
+ #[napi]
466
+ pub fn set_gl_swap_interval(&self, interval: i32) -> Result<bool> {
467
+ self.make_gl_current()?;
468
+ Ok(unsafe { SDL_GL_SetSwapInterval(interval) })
469
+ }
470
+
471
+ #[napi]
472
+ pub fn swap_gl(&self) -> Result<bool> {
473
+ self.make_gl_current()?;
474
+ Ok(unsafe { SDL_GL_SwapWindow(self.require_window()?) })
475
+ }
476
+
477
+ #[napi]
478
+ pub fn gl_version(&self) -> Result<String> {
479
+ self.make_gl_current()?;
480
+ type GetString = unsafe extern "C" fn(u32) -> *const u8;
481
+ let name = c"glGetString";
482
+ let procedure = unsafe { SDL_GL_GetProcAddress(name.as_ptr()) }
483
+ .ok_or_else(|| Error::from_reason("SDL3 did not load glGetString"))?;
484
+ let get_string =
485
+ unsafe { std::mem::transmute::<unsafe extern "C" fn(), GetString>(procedure) };
486
+ let version = unsafe { get_string(0x1F02) };
487
+ if version.is_null() {
488
+ return Err(Error::from_reason("SDL3 glGetString returned null"));
489
+ }
490
+ Ok(unsafe { CStr::from_ptr(version.cast()) }
491
+ .to_string_lossy()
492
+ .into_owned())
493
+ }
494
+
495
+ #[napi]
496
+ pub fn destroy(&mut self) {
497
+ self.destroy_inner();
498
+ }
499
+
500
+ fn window_pointer(&self) -> *mut SDL_Window {
501
+ self.window as *mut SDL_Window
502
+ }
503
+
504
+ fn require_window(&self) -> Result<*mut SDL_Window> {
505
+ let window = self.window_pointer();
506
+ if window.is_null() {
507
+ Err(Error::from_reason("native window is destroyed"))
508
+ } else {
509
+ Ok(window)
510
+ }
511
+ }
512
+
513
+ fn position(&self) -> Result<(i32, i32)> {
514
+ let (mut x, mut y) = (0, 0);
515
+ if unsafe { SDL_GetWindowPosition(self.require_window()?, &mut x, &mut y) } {
516
+ Ok((x, y))
517
+ } else {
518
+ Err(sdl_error("could not query SDL3 window position"))
519
+ }
520
+ }
521
+
522
+ fn pixel_size(&self) -> Result<(i32, i32)> {
523
+ let (mut width, mut height) = (0, 0);
524
+ if unsafe { SDL_GetWindowSizeInPixels(self.require_window()?, &mut width, &mut height) } {
525
+ Ok((width, height))
526
+ } else {
527
+ Err(sdl_error("could not query SDL3 window pixel size"))
528
+ }
529
+ }
530
+
531
+ fn window_action(
532
+ &self,
533
+ action: unsafe extern "C" fn(*mut SDL_Window) -> bool,
534
+ context: &str,
535
+ ) -> Result<()> {
536
+ if unsafe { action(self.require_window()?) } {
537
+ Ok(())
538
+ } else {
539
+ Err(sdl_error(context))
540
+ }
541
+ }
542
+
543
+ fn destroy_inner(&mut self) {
544
+ if self.window == 0 {
545
+ return;
546
+ }
547
+ if self.gl_context != 0 {
548
+ unsafe { SDL_GL_DestroyContext(self.gl_context as SDL_GLContext) };
549
+ self.gl_context = 0;
550
+ }
551
+ unsafe { SDL_DestroyWindow(self.window_pointer()) };
552
+ self.window = 0;
553
+ if WINDOW_COUNT.fetch_sub(1, Ordering::SeqCst) == 1 {
554
+ unsafe { SDL_QuitSubSystem(SDL_INIT_VIDEO) };
555
+ }
556
+ }
557
+ }
558
+
559
+ impl Drop for NativeSdlWindow {
560
+ fn drop(&mut self) {
561
+ self.destroy_inner();
562
+ }
563
+ }
564
+
565
+ fn empty_event(kind: &str, window_id: u32) -> NativeWindowEvent {
566
+ NativeWindowEvent {
567
+ kind: kind.to_owned(),
568
+ window_id,
569
+ x: None,
570
+ y: None,
571
+ dx: None,
572
+ dy: None,
573
+ button: None,
574
+ key: None,
575
+ scancode: None,
576
+ repeat: None,
577
+ shift: None,
578
+ ctrl: None,
579
+ alt: None,
580
+ super_key: None,
581
+ flipped: None,
582
+ }
583
+ }
584
+
585
+ /// Drains SDL's process-wide queue. JavaScript routes each event by window id.
586
+ #[napi]
587
+ pub fn poll_events() -> Vec<NativeWindowEvent> {
588
+ let mut events = Vec::new();
589
+ let mut raw = SDL_Event::default();
590
+ while unsafe { SDL_PollEvent(&mut raw) } {
591
+ let event_type = unsafe { raw.r#type };
592
+ if event_type == SDL_EVENT_QUIT.0 {
593
+ events.push(empty_event("quit", 0));
594
+ } else if event_type == SDL_EVENT_WINDOW_CLOSE_REQUESTED.0 {
595
+ events.push(empty_event("close", unsafe { raw.window.windowID.into() }));
596
+ } else if event_type == SDL_EVENT_WINDOW_RESIZED.0
597
+ || event_type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED.0
598
+ {
599
+ events.push(empty_event("resize", unsafe { raw.window.windowID.into() }));
600
+ } else if event_type == SDL_EVENT_WINDOW_MOVED.0 {
601
+ let raw = unsafe { raw.window };
602
+ let mut event = empty_event("move", raw.windowID.into());
603
+ event.x = Some(raw.data1.into());
604
+ event.y = Some(raw.data2.into());
605
+ events.push(event);
606
+ } else if event_type == SDL_EVENT_WINDOW_MINIMIZED.0 {
607
+ events.push(empty_event("minimize", unsafe {
608
+ raw.window.windowID.into()
609
+ }));
610
+ } else if event_type == SDL_EVENT_WINDOW_MAXIMIZED.0 {
611
+ events.push(empty_event("maximize", unsafe {
612
+ raw.window.windowID.into()
613
+ }));
614
+ } else if event_type == SDL_EVENT_WINDOW_RESTORED.0 {
615
+ events.push(empty_event("restore", unsafe {
616
+ raw.window.windowID.into()
617
+ }));
618
+ } else if event_type == SDL_EVENT_MOUSE_MOTION.0 {
619
+ let raw = unsafe { raw.motion };
620
+ let mut event = empty_event("mouseMove", raw.windowID.into());
621
+ event.x = Some(raw.x.into());
622
+ event.y = Some(raw.y.into());
623
+ events.push(event);
624
+ } else if event_type == SDL_EVENT_MOUSE_BUTTON_DOWN.0
625
+ || event_type == SDL_EVENT_MOUSE_BUTTON_UP.0
626
+ {
627
+ let raw = unsafe { raw.button };
628
+ let mut event = empty_event(
629
+ if event_type == SDL_EVENT_MOUSE_BUTTON_DOWN.0 {
630
+ "mouseButtonDown"
631
+ } else {
632
+ "mouseButtonUp"
633
+ },
634
+ raw.windowID.into(),
635
+ );
636
+ event.x = Some(raw.x.into());
637
+ event.y = Some(raw.y.into());
638
+ event.button = Some(raw.button.into());
639
+ events.push(event);
640
+ } else if event_type == SDL_EVENT_MOUSE_WHEEL.0 {
641
+ let raw = unsafe { raw.wheel };
642
+ let mut event = empty_event("mouseWheel", raw.windowID.into());
643
+ event.x = Some(raw.mouse_x.into());
644
+ event.y = Some(raw.mouse_y.into());
645
+ event.dx = Some(raw.x.into());
646
+ event.dy = Some(raw.y.into());
647
+ event.flipped = Some(raw.direction == SDL_MOUSEWHEEL_FLIPPED);
648
+ events.push(event);
649
+ } else if event_type == SDL_EVENT_KEY_DOWN.0 || event_type == SDL_EVENT_KEY_UP.0 {
650
+ let raw = unsafe { raw.key };
651
+ let mut event = empty_event(
652
+ if event_type == SDL_EVENT_KEY_DOWN.0 {
653
+ "keyDown"
654
+ } else {
655
+ "keyUp"
656
+ },
657
+ raw.windowID.into(),
658
+ );
659
+ let key_name = unsafe { SDL_GetKeyName(raw.key) };
660
+ event.key = (!key_name.is_null())
661
+ .then(|| unsafe { CStr::from_ptr(key_name).to_string_lossy().into_owned() });
662
+ event.scancode = Some(raw.scancode.0 as u32);
663
+ event.repeat = Some(raw.repeat);
664
+ event.shift = Some((raw.r#mod.0 & SDL_KMOD_SHIFT.0) != 0);
665
+ event.ctrl = Some((raw.r#mod.0 & SDL_KMOD_CTRL.0) != 0);
666
+ event.alt = Some((raw.r#mod.0 & SDL_KMOD_ALT.0) != 0);
667
+ event.super_key = Some((raw.r#mod.0 & SDL_KMOD_GUI.0) != 0);
668
+ events.push(event);
669
+ }
670
+ }
671
+ events
672
+ }
37
673
 
38
674
  pub struct CompositorFrameTask {
39
675
  timeout_ms: u32,
@@ -48,11 +684,8 @@ impl Task for CompositorFrameTask {
48
684
  {
49
685
  return Ok(wait_for_windows_compositor(self.timeout_ms));
50
686
  }
51
-
52
687
  #[cfg(not(windows))]
53
- {
54
- Ok(false)
55
- }
688
+ Ok(false)
56
689
  }
57
690
 
58
691
  fn resolve(&mut self, _env: Env, signaled: Self::Output) -> Result<Self::JsValue> {
@@ -60,7 +693,6 @@ impl Task for CompositorFrameTask {
60
693
  }
61
694
  }
62
695
 
63
- /// Waits off the JavaScript thread for the next Windows compositor clock tick.
64
696
  #[napi]
65
697
  pub fn wait_for_compositor_frame(timeout_ms: Option<u32>) -> AsyncTask<CompositorFrameTask> {
66
698
  AsyncTask::new(CompositorFrameTask {
@@ -71,13 +703,11 @@ pub fn wait_for_compositor_frame(timeout_ms: Option<u32>) -> AsyncTask<Composito
71
703
  #[cfg(windows)]
72
704
  fn wait_for_windows_compositor(timeout_ms: u32) -> bool {
73
705
  type WaitForCompositorClock = unsafe extern "system" fn(u32, *const HANDLE, u32) -> u32;
74
-
75
706
  let library_name: Vec<u16> = "dcomp.dll\0".encode_utf16().collect();
76
707
  let module = unsafe { LoadLibraryW(library_name.as_ptr()) };
77
708
  if module.is_null() {
78
709
  return false;
79
710
  }
80
-
81
711
  let procedure =
82
712
  unsafe { GetProcAddress(module, b"DCompositionWaitForCompositorClock\0".as_ptr()) };
83
713
  let signaled = match procedure {
@@ -91,28 +721,16 @@ fn wait_for_windows_compositor(timeout_ms: u32) -> bool {
91
721
  }
92
722
  None => false,
93
723
  };
94
- unsafe {
95
- FreeLibrary(module);
96
- }
724
+ unsafe { FreeLibrary(module) };
97
725
  signaled
98
726
  }
99
727
 
100
728
  #[napi]
101
729
  pub fn set_transparent(native_data: Buffer, transparent: bool) -> Result<()> {
102
730
  #[cfg(windows)]
103
- {
104
- let hwnd = hwnd_from_native_data(&native_data)?;
105
- configure_dwm_transparency(hwnd, transparent)?;
106
- }
107
-
731
+ configure_dwm_transparency(hwnd_from_native_data(&native_data)?, transparent)?;
108
732
  #[cfg(not(windows))]
109
- {
110
- let _ = (native_data, transparent);
111
- return Err(Error::from_reason(
112
- "transparent native windows are supported only on Windows 11",
113
- ));
114
- }
115
-
733
+ let _ = (native_data, transparent);
116
734
  Ok(())
117
735
  }
118
736
 
@@ -128,7 +746,6 @@ fn configure_dwm_transparency(hwnd: HWND, transparent: bool) -> Result<()> {
128
746
  "could not create DWM transparency region",
129
747
  ));
130
748
  }
131
-
132
749
  let blur = DWM_BLURBEHIND {
133
750
  dwFlags: if transparent {
134
751
  DWM_BB_ENABLE | DWM_BB_BLURREGION
@@ -141,9 +758,7 @@ fn configure_dwm_transparency(hwnd: HWND, transparent: bool) -> Result<()> {
141
758
  };
142
759
  let result = unsafe { DwmEnableBlurBehindWindow(hwnd, &blur) };
143
760
  if !region.is_null() {
144
- unsafe {
145
- DeleteObject(region as _);
146
- }
761
+ unsafe { DeleteObject(region as _) };
147
762
  }
148
763
  if result < 0 {
149
764
  return Err(Error::from_reason(format!(
@@ -182,10 +797,8 @@ impl ModalFrameController {
182
797
  ) -> Result<Self> {
183
798
  #[cfg(windows)]
184
799
  let hwnd = hwnd_from_native_data(&native_data)?;
185
-
186
800
  #[cfg(not(windows))]
187
801
  let _ = native_data;
188
-
189
802
  Ok(Self {
190
803
  env,
191
804
  frame_callback,
@@ -211,21 +824,20 @@ impl ModalFrameController {
211
824
  return Ok(());
212
825
  }
213
826
  self.previous_userdata = unsafe { GetWindowLongPtrW(self.hwnd, GWLP_USERDATA) };
214
- let self_pointer = self as *mut Self as isize;
215
- let previous_proc =
216
- unsafe { SetWindowLongPtrW(self.hwnd, GWLP_USERDATA, self_pointer) };
827
+ unsafe { SetWindowLongPtrW(self.hwnd, GWLP_USERDATA, self as *mut Self as isize) };
217
828
  self.previous_proc = unsafe {
218
- SetWindowLongPtrW(self.hwnd, GWLP_WNDPROC, modal_window_proc as usize as isize)
829
+ SetWindowLongPtrW(
830
+ self.hwnd,
831
+ GWLP_WNDPROC,
832
+ modal_window_proc as *const () as usize as isize,
833
+ )
219
834
  };
220
835
  if self.previous_proc == 0 {
221
- unsafe {
222
- SetWindowLongPtrW(self.hwnd, GWLP_USERDATA, self.previous_userdata);
223
- }
836
+ unsafe { SetWindowLongPtrW(self.hwnd, GWLP_USERDATA, self.previous_userdata) };
224
837
  return Err(Error::from_reason(
225
838
  "could not install Windows modal window hook",
226
839
  ));
227
840
  }
228
- let _ = previous_proc;
229
841
  self.attached = true;
230
842
  }
231
843
  Ok(())
@@ -280,29 +892,29 @@ unsafe extern "system" fn modal_window_proc(
280
892
  wparam: WPARAM,
281
893
  lparam: LPARAM,
282
894
  ) -> LRESULT {
283
- let controller = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut ModalFrameController;
895
+ let controller = unsafe { GetWindowLongPtrW(hwnd, GWLP_USERDATA) } as *mut ModalFrameController;
284
896
  if !controller.is_null() {
285
- let controller = &mut *controller;
897
+ let controller = unsafe { &mut *controller };
286
898
  match message {
287
899
  WM_ENTERSIZEMOVE | WM_ENTERMENULOOP => {
288
900
  controller.invoke_state(true);
289
- SetTimer(hwnd, MODAL_TIMER_ID, 16, None);
901
+ unsafe { SetTimer(hwnd, MODAL_TIMER_ID, 16, None) };
290
902
  }
291
903
  WM_TIMER if wparam == MODAL_TIMER_ID => {
292
904
  controller.invoke_frame();
293
905
  return 0;
294
906
  }
295
907
  WM_EXITSIZEMOVE | WM_EXITMENULOOP => {
296
- KillTimer(hwnd, MODAL_TIMER_ID);
908
+ unsafe { KillTimer(hwnd, MODAL_TIMER_ID) };
297
909
  controller.invoke_state(false);
298
910
  }
299
911
  _ => {}
300
912
  }
301
- let previous = std::mem::transmute::<isize, WNDPROC>(controller.previous_proc);
913
+ let previous = unsafe { std::mem::transmute::<isize, WNDPROC>(controller.previous_proc) };
302
914
  return match previous {
303
- Some(proc) => CallWindowProcW(Some(proc), hwnd, message, wparam, lparam),
304
- None => DefWindowProcW(hwnd, message, wparam, lparam),
915
+ Some(proc) => unsafe { CallWindowProcW(Some(proc), hwnd, message, wparam, lparam) },
916
+ None => unsafe { DefWindowProcW(hwnd, message, wparam, lparam) },
305
917
  };
306
918
  }
307
- DefWindowProcW(hwnd, message, wparam, lparam)
919
+ unsafe { DefWindowProcW(hwnd, message, wparam, lparam) }
308
920
  }