diff --git a/build.rs b/build.rs index aaefcc99..50ba1b61 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,7 @@ use std::env; fn main() { let target = env::var("TARGET").unwrap_or_else(|e| panic!("{}", e)); - if target.contains("darwin") || target.contains("ios"){ + if target.contains("darwin") || target.contains("ios") { println!("cargo:rustc-link-lib=framework=MetalKit"); } } diff --git a/examples/instancing.rs b/examples/instancing.rs index 04988949..f2ec3678 100644 --- a/examples/instancing.rs +++ b/examples/instancing.rs @@ -167,7 +167,31 @@ impl EventHandler for Stage { } fn main() { - let mut conf = conf::Conf::default(); + // std::thread::spawn(|| { + // let mut conf = conf::Conf::default(); + // let metal = std::env::args().nth(1).as_deref() == Some("metal"); + // conf.platform.apple_gfx_api = if metal { + // conf::AppleGfxApi::Metal + // } else { + // conf::AppleGfxApi::OpenGl + // }; + + // miniquad::start(conf, move || Box::new(Stage::new())); + // }); + + //let mut conf = conf::Conf::default(); + let mut conf = conf::Conf { + window_title: "Miniquad".to_string(), + window_width: 1024, + window_height: 768, + fullscreen: true, + platform: conf::Platform { + linux_backend: conf::LinuxBackend::WaylandOnly, + ..Default::default() + }, + ..Default::default() + }; + let metal = std::env::args().nth(1).as_deref() == Some("metal"); conf.platform.apple_gfx_api = if metal { conf::AppleGfxApi::Metal diff --git a/examples/window_conf.rs b/examples/window_conf.rs index 92fe6f9e..5e8e628a 100644 --- a/examples/window_conf.rs +++ b/examples/window_conf.rs @@ -18,6 +18,10 @@ fn main() { window_width: 1024, window_height: 768, fullscreen: true, + platform: conf::Platform { + linux_backend: conf::LinuxBackend::WaylandOnly, + ..Default::default() + }, ..Default::default() }, || { diff --git a/src/graphics/metal.rs b/src/graphics/metal.rs index cb27b007..b2d55a2f 100644 --- a/src/graphics/metal.rs +++ b/src/graphics/metal.rs @@ -269,7 +269,7 @@ pub struct MetalContext { impl MetalContext { pub fn new() -> MetalContext { unsafe { - let view = crate::window::apple_view().unwrap(); + let view = crate::window::apple_view(); assert!(!view.is_null()); let device: ObjcId = msg_send![view, device]; assert!(!device.is_null()); diff --git a/src/lib.rs b/src/lib.rs index 89125d74..3e2c3c39 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![doc = include_str!("../README.md")] +#![allow(warnings)] pub mod conf; mod event; @@ -36,26 +37,17 @@ pub mod date { } } -use std::cell::RefCell; -thread_local! { - static NATIVE_DISPLAY: RefCell> = RefCell::new(None); -} -pub(crate) fn with_native_display(f: &mut dyn FnMut(&mut dyn crate::native::NativeDisplay)) { - NATIVE_DISPLAY.with(|d| (d.borrow().as_ref().unwrap())(f)) -} +use std::sync::{Mutex, OnceLock}; -// I wish "with_native_display" could be generic over return value, but function -// pointer to a generic function requires way too much unsafe :( -macro_rules! with_native_display { - ($target:ident, $f:expr) => {{ - let mut res = Default::default(); - - with_native_display(&mut |$target| { - res = $f; - }); +static NATIVE_DISPLAY: OnceLock> = OnceLock::new(); - res - }}; +fn set_display(display: native::NativeDisplayData) { + NATIVE_DISPLAY.set(Mutex::new(display)); +} +fn native_display() -> &'static Mutex { + NATIVE_DISPLAY + .get() + .expect("Backend did not initialized NATIVE_DISPLAY yet.") //|| Mutex::new(Default::default())) } /// Window and associated to window rendering context related functions. @@ -63,22 +55,47 @@ macro_rules! with_native_display { pub mod window { use super::*; + /// The same as + /// ```ignore + /// if metal { + /// Box::new(MetalContext::new()) + /// } else { + /// Box::new(GlContext::new()) + /// }; + /// ``` + /// but under #[cfg] gate to avoid MetalContext on non-apple platforms + pub fn new_rendering_backend() -> Box { + #[cfg(target_vendor = "apple")] + { + if window::apple_gfx_api() == conf::AppleGfxApi::Metal { + Box::new(MetalContext::new()) + } else { + Box::new(GlContext::new()) + } + } + #[cfg(not(target_vendor = "apple"))] + Box::new(GlContext::new()) + } + /// The current framebuffer size in pixels /// NOTE: [High DPI Rendering](../conf/index.html#high-dpi-rendering) pub fn screen_size() -> (f32, f32) { - with_native_display!(d, d.screen_size()) + let d = native_display().lock().unwrap(); + (d.screen_width as f32, d.screen_height as f32) } /// The dpi scaling factor (window pixels to framebuffer pixels) /// NOTE: [High DPI Rendering](../conf/index.html#high-dpi-rendering) pub fn dpi_scale() -> f32 { - with_native_display!(d, d.dpi_scale()) + let d = native_display().lock().unwrap(); + d.dpi_scale } /// True when high_dpi was requested and actually running in a high-dpi scenario /// NOTE: [High DPI Rendering](../conf/index.html#high-dpi-rendering) pub fn high_dpi() -> bool { - with_native_display!(d, d.high_dpi()) + let d = native_display().lock().unwrap(); + d.high_dpi } /// This function simply quits the application without @@ -89,7 +106,13 @@ pub mod window { /// happen in the order_quit implmentation) and execution might continue for some time after /// But the window is going to be inevitably closed at some point. pub fn order_quit() { - with_native_display!(d, d.order_quit()) + let mut d = native_display().lock().unwrap(); + d.quit_ordered = true; + } + + /// Shortcut for `order_quit`. Will add a legacy attribute at some point. + pub fn quit() { + order_quit() } /// Calling request_quit() will trigger "quit_requested_event" event , giving @@ -98,7 +121,8 @@ pub mod window { /// If the event handler callback does nothing, the application will be quit as usual. /// To prevent this, call the function "cancel_quit()"" from inside the event handler. pub fn request_quit() { - with_native_display!(d, d.request_quit()) + let mut d = native_display().lock().unwrap(); + d.quit_requested = true; } /// Cancels a pending quit request, either initiated @@ -107,9 +131,9 @@ pub mod window { /// function makes sense is from inside the event handler callback when /// the "quit_requested_event" event has been received pub fn cancel_quit() { - with_native_display!(d, d.cancel_quit()) + let mut d = native_display().lock().unwrap(); + d.quit_requested = false; } - /// Capture mouse cursor to the current window /// On WASM this will automatically hide cursor /// On desktop this will bound cursor to windows border @@ -117,88 +141,83 @@ pub mod window { /// so set_cursor_grab(false) on window's focus lost is recommended. /// TODO: implement window focus events pub fn set_cursor_grab(grab: bool) { - with_native_display!(d, d.set_cursor_grab(grab)) + let mut d = native_display().lock().unwrap(); + d.native_requests.send(native::Request::SetCursorGrab(grab)); } /// Show or hide the mouse cursor pub fn show_mouse(shown: bool) { - with_native_display!(d, d.show_mouse(shown)) + let mut d = native_display().lock().unwrap(); + d.native_requests.send(native::Request::ShowMouse(shown)); } /// Set the mouse cursor icon. pub fn set_mouse_cursor(cursor_icon: CursorIcon) { - with_native_display!(d, d.set_mouse_cursor(cursor_icon)) + let mut d = native_display().lock().unwrap(); + d.native_requests + .send(native::Request::SetMouseCursor(cursor_icon)); } /// Set the application's window size. pub fn set_window_size(new_width: u32, new_height: u32) { - with_native_display!(d, d.set_window_size(new_width, new_height)) + let mut d = native_display().lock().unwrap(); + d.native_requests.send(native::Request::SetWindowSize { + new_width, + new_height, + }); } pub fn set_fullscreen(fullscreen: bool) { - with_native_display!(d, d.set_fullscreen(fullscreen)) + let mut d = native_display().lock().unwrap(); + d.native_requests + .send(native::Request::SetFullscreen(fullscreen)); } /// Get current OS clipboard value pub fn clipboard_get() -> Option { - with_native_display!(d, d.clipboard_get()) + let mut d = native_display().lock().unwrap(); + d.clipboard.get() } /// Save value to OS clipboard pub fn clipboard_set(data: &str) { - with_native_display!(d, d.clipboard_set(data)) + let mut d = native_display().lock().unwrap(); + d.clipboard.set(data) } pub fn dropped_file_count() -> usize { - with_native_display!(d, d.dropped_file_count()) + //with_native_display!(d, d.dropped_file_count()) + unimplemented!() } pub fn dropped_file_bytes(index: usize) -> Option> { - with_native_display!(d, d.dropped_file_bytes(index)) + //with_native_display!(d, d.dropped_file_bytes(index)) + unimplemented!() } pub fn dropped_file_path(index: usize) -> Option { - with_native_display!(d, d.dropped_file_path(index)) - } - - /// Shortcut for `order_quit`. Will add a legacy attribute at some point. - pub fn quit() { - with_native_display!(d, d.order_quit()) + //with_native_display!(d, d.dropped_file_path(index)) + unimplemented!() } /// Show/hide onscreen keyboard. /// Only works on Android right now. pub fn show_keyboard(show: bool) { - with_native_display!(d, d.show_keyboard(show)) + let mut d = native_display().lock().unwrap(); + d.native_requests.send(native::Request::ShowKeyboard(show)); } - /// The same as - /// ```ignore - /// if metal { - /// Box::new(MetalContext::new()) - /// } else { - /// Box::new(GlContext::new()) - /// }; - /// ``` - /// but under #[cfg] gate to avoid MetalContext on non-apple platforms - pub fn new_rendering_backend() -> Box { - #[cfg(target_vendor = "apple")] - { - if with_native_display!(d, d.apple_gfx_api() == conf::AppleGfxApi::Metal) { - Box::new(MetalContext::new()) - } else { - Box::new(GlContext::new()) - } - } - #[cfg(not(target_vendor = "apple"))] - Box::new(GlContext::new()) + #[cfg(target_vendor = "apple")] + pub fn apple_gfx_api() -> crate::conf::AppleGfxApi { + let d = native_display().lock().unwrap(); + d.gfx_api } - #[cfg(target_vendor = "apple")] - pub unsafe fn apple_view() -> Option { - with_native_display!(d, d.apple_view()) + pub fn apple_view() -> crate::native::apple::frameworks::ObjcId { + let d = native_display().lock().unwrap(); + d.view } - - #[cfg(target_os = "ios")] - pub unsafe fn apple_view_ctrl() -> Option { - with_native_display!(d, d.apple_view_ctrl()) + #[cfg(target_ios = "ios")] + pub fn apple_view_ctrl() -> crate::native::apple::frameworks::ObjcId { + let d = native_display().lock().unwrap(); + d.view_ctrl } } diff --git a/src/native.rs b/src/native.rs index 7fa98b68..fa28d555 100644 --- a/src/native.rs +++ b/src/native.rs @@ -1,6 +1,10 @@ -/// Most backends happened to have exactly the same fields in their *Display struct -/// Maybe something like this may in some public API some day? -/// (important data from this struct is available through function like Context::screen_size) +use std::sync::mpsc; + +#[derive(Default)] +pub(crate) struct DroppedFiles { + pub paths: Vec, + pub bytes: Vec>, +} pub(crate) struct NativeDisplayData { pub screen_width: i32, pub screen_height: i32, @@ -8,54 +12,62 @@ pub(crate) struct NativeDisplayData { pub high_dpi: bool, pub quit_requested: bool, pub quit_ordered: bool, -} + pub native_requests: mpsc::Sender, + pub clipboard: Box, + pub dropped_files: DroppedFiles, -impl Default for NativeDisplayData { - fn default() -> NativeDisplayData { + #[cfg(target_vendor = "apple")] + pub view: crate::native::apple::frameworks::ObjcId, + #[cfg(target_os = "ios")] + pub view_ctrl: crate::native::apple::frameworks::ObjcId, + #[cfg(target_vendor = "apple")] + pub gfx_api: crate::conf::AppleGfxApi, +} +#[cfg(target_vendor = "apple")] +unsafe impl Send for NativeDisplayData {} +#[cfg(target_vendor = "apple")] +unsafe impl Sync for NativeDisplayData {} + +impl NativeDisplayData { + pub fn new( + screen_width: i32, + screen_height: i32, + native_requests: mpsc::Sender, + clipboard: Box, + ) -> NativeDisplayData { NativeDisplayData { - screen_width: 1, - screen_height: 1, + screen_width, + screen_height, dpi_scale: 1., high_dpi: false, quit_requested: false, quit_ordered: false, + native_requests, + clipboard, + dropped_files: Default::default(), + #[cfg(target_vendor = "apple")] + gfx_api: crate::conf::AppleGfxApi::OpenGl, + #[cfg(target_vendor = "apple")] + view: std::ptr::null_mut(), + #[cfg(target_os = "ios")] + view_ctrl: std::ptr::null_mut(), } } } -pub trait NativeDisplay: std::any::Any { - fn screen_size(&self) -> (f32, f32); - fn dpi_scale(&self) -> f32; - fn high_dpi(&self) -> bool; - fn order_quit(&mut self); - fn request_quit(&mut self); - fn cancel_quit(&mut self); - - fn set_cursor_grab(&mut self, _grab: bool); - fn show_mouse(&mut self, _shown: bool); - fn set_mouse_cursor(&mut self, _cursor_icon: crate::CursorIcon); - fn set_window_size(&mut self, _new_width: u32, _new_height: u32); - fn set_fullscreen(&mut self, _fullscreen: bool); - fn clipboard_get(&mut self) -> Option; - fn clipboard_set(&mut self, _data: &str); - fn dropped_file_count(&mut self) -> usize { - 0 - } - fn dropped_file_bytes(&mut self, _index: usize) -> Option> { - None - } - fn dropped_file_path(&mut self, _index: usize) -> Option { - None - } - fn show_keyboard(&mut self, _show: bool) {} - #[cfg(target_vendor = "apple")] - fn apple_gfx_api(&self) -> crate::conf::AppleGfxApi; - #[cfg(target_vendor = "apple")] - fn apple_view(&mut self) -> Option; - #[cfg(target_os = "ios")] - fn apple_view_ctrl(&mut self) -> Option; +#[derive(Debug)] +pub(crate) enum Request { + SetCursorGrab(bool), + ShowMouse(bool), + SetMouseCursor(crate::CursorIcon), + SetWindowSize { new_width: u32, new_height: u32 }, + SetFullscreen(bool), + ShowKeyboard(bool), +} - fn as_any(&mut self) -> &mut dyn std::any::Any; +pub trait Clipboard: Send + Sync { + fn get(&mut self) -> Option; + fn set(&mut self, string: &str); } pub mod module; diff --git a/src/native/android.rs b/src/native/android.rs index 650225a8..5bd6d6b1 100644 --- a/src/native/android.rs +++ b/src/native/android.rs @@ -2,7 +2,7 @@ use crate::{ event::{EventHandler, KeyCode, TouchPhase}, native::{ egl::{self, LibEgl}, - NativeDisplay, + NativeDisplayData, }, }; @@ -86,83 +86,6 @@ fn send_message(message: Message) { static mut ACTIVITY: ndk_sys::jobject = std::ptr::null_mut(); static mut VM: *mut ndk_sys::JavaVM = std::ptr::null_mut(); -struct AndroidDisplay { - screen_width: f32, - screen_height: f32, - fullscreen: bool, -} - -mod tl_display { - use super::*; - use crate::{native::NativeDisplay, NATIVE_DISPLAY}; - - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub(super) fn with(mut f: impl FnMut(&mut AndroidDisplay) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - - pub(super) fn set_display(display: AndroidDisplay) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } -} - -impl NativeDisplay for AndroidDisplay { - fn screen_size(&self) -> (f32, f32) { - (self.screen_width as _, self.screen_height as _) - } - fn dpi_scale(&self) -> f32 { - 1. - } - fn high_dpi(&self) -> bool { - true - } - fn order_quit(&mut self) {} - fn request_quit(&mut self) {} - fn cancel_quit(&mut self) {} - fn set_cursor_grab(&mut self, _grab: bool) {} - fn show_mouse(&mut self, _shown: bool) {} - fn set_mouse_cursor(&mut self, _cursor: crate::CursorIcon) {} - fn set_window_size(&mut self, _new_width: u32, _new_height: u32) {} - fn set_fullscreen(&mut self, fullscreen: bool) { - unsafe { - let env = attach_jni_env(); - set_full_screen(env, fullscreen); - } - self.fullscreen = fullscreen; - } - fn clipboard_get(&mut self) -> Option { - None - } - fn clipboard_set(&mut self, _data: &str) {} - fn show_keyboard(&mut self, show: bool) { - unsafe { - let env = attach_jni_env(); - ndk_utils::call_void_method!(env, ACTIVITY, "showKeyboard", "(Z)V", show as i32); - } - } - fn as_any(&mut self) -> &mut dyn std::any::Any { - self - } -} - pub unsafe fn console_debug(msg: *const ::std::os::raw::c_char) { ndk_sys::__android_log_write( ndk_sys::android_LogPriority_ANDROID_LOG_DEBUG as _, @@ -212,6 +135,7 @@ struct MainThreadState { window: *mut ndk_sys::ANativeWindow, event_handler: Box, quit: bool, + fullscreen: bool, } impl MainThreadState { @@ -271,10 +195,11 @@ impl MainThreadState { self.update_surface(window); } - tl_display::with(|d| { + { + let mut d = crate::native_display().lock().unwrap(); d.screen_width = width as _; d.screen_height = height as _; - }); + } self.event_handler.resize_event(width as _, height as _); } Message::Touch { @@ -300,7 +225,7 @@ impl MainThreadState { } Message::Pause => self.event_handler.window_minimized_event(), Message::Resume => { - if tl_display::with(|d| d.fullscreen) { + if self.fullscreen { unsafe { let env = attach_jni_env(); set_full_screen(env, true); @@ -326,6 +251,32 @@ impl MainThreadState { } } } + + fn process_request(&mut self, request: crate::native::Request) { + use crate::native::Request::*; + unsafe { + match request { + SetFullscreen(fullscreen) => { + unsafe { + let env = attach_jni_env(); + set_full_screen(env, fullscreen); + } + self.fullscreen = fullscreen; + } + ShowKeyboard(show) => unsafe { + let env = attach_jni_env(); + ndk_utils::call_void_method!( + env, + ACTIVITY, + "showKeyboard", + "(Z)V", + show as i32 + ); + }, + _ => {} + } + } + } } /// Get the JNI Env by calling ndk's AttachCurrentThread @@ -359,6 +310,20 @@ pub unsafe fn attach_jni_env() -> *mut ndk_sys::JNIEnv { env } +pub struct AndroidClipboard {} +impl AndroidClipboard { + pub fn new() -> AndroidClipboard { + AndroidClipboard {} + } +} +impl crate::native::Clipboard for AndroidClipboard { + fn get(&mut self) -> Option { + None + } + + fn set(&mut self, data: &str) {} +} + pub unsafe fn run(conf: crate::conf::Conf, f: F) where F: 'static + FnOnce() -> Box, @@ -437,12 +402,13 @@ where panic!(); } - let display = AndroidDisplay { - screen_width, - screen_height, - fullscreen: conf.fullscreen, - }; - tl_display::set_display(display); + let (tx, requests_rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(AndroidClipboard::new()); + crate::set_display(NativeDisplayData { + high_dpi: conf.high_dpi, + ..NativeDisplayData::new(conf.window_width, conf.window_height, tx, clipboard) + }); + let event_handler = f.0(); let mut s = MainThreadState { libegl, @@ -453,9 +419,14 @@ where window, event_handler, quit: false, + fullscreen: conf.fullscreen, }; while !s.quit { + while let Ok(request) = requests_rx.try_recv() { + s.process_request(request); + } + // process all the messages from the main thread while let Ok(msg) = rx.try_recv() { s.process_message(msg); diff --git a/src/native/ios.rs b/src/native/ios.rs index 027c86d3..8bb4d71c 100644 --- a/src/native/ios.rs +++ b/src/native/ios.rs @@ -14,48 +14,24 @@ use { }, NativeDisplayData, }, + native_display, }, std::os::raw::c_void, }; struct IosDisplay { - data: NativeDisplayData, view: ObjcId, view_ctrl: ObjcId, _textfield_dlg: ObjcId, textfield: ObjcId, gfx_api: conf::AppleGfxApi, -} -impl crate::native::NativeDisplay for IosDisplay { - fn screen_size(&self) -> (f32, f32) { - (self.data.screen_width as _, self.data.screen_height as _) - } - fn dpi_scale(&self) -> f32 { - self.data.dpi_scale - } - fn high_dpi(&self) -> bool { - self.data.high_dpi - } - fn order_quit(&mut self) { - self.data.quit_ordered = true; - } - fn request_quit(&mut self) { - self.data.quit_requested = true; - } - fn cancel_quit(&mut self) { - self.data.quit_requested = false; - } + event_handler: Option>, + _gles2: bool, + f: Option Box>>, +} - fn set_cursor_grab(&mut self, _grab: bool) {} - fn show_mouse(&mut self, _show: bool) {} - fn set_mouse_cursor(&mut self, _cursor: crate::CursorIcon) {} - fn set_window_size(&mut self, _new_width: u32, _new_height: u32) {} - fn set_fullscreen(&mut self, _fullscreen: bool) {} - fn clipboard_get(&mut self) -> Option { - None - } - fn clipboard_set(&mut self, _data: &str) {} +impl IosDisplay { fn show_keyboard(&mut self, show: bool) { unsafe { if show { @@ -65,66 +41,12 @@ impl crate::native::NativeDisplay for IosDisplay { } } } - #[cfg(target_vendor = "apple")] - fn apple_gfx_api(&self) -> crate::conf::AppleGfxApi { - self.gfx_api - } - #[cfg(target_vendor = "apple")] - fn apple_view(&mut self) -> Option { - Some(self.view) - } - - #[cfg(target_vendor = "apple")] - fn apple_view_ctrl(&mut self) -> Option { - Some(self.view_ctrl) - } - - fn as_any(&mut self) -> &mut dyn std::any::Any { - self - } -} -mod tl_display { - use super::*; - use crate::{native::NativeDisplay, NATIVE_DISPLAY}; - - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub(super) fn with(mut f: impl FnMut(&mut IosDisplay) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - - pub(super) fn set_display(display: IosDisplay) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } -} - -struct WindowPayload { - event_handler: Option>, - _gles2: bool, - f: Option Box>>, } -fn get_window_payload(this: &Object) -> &mut WindowPayload { +fn get_window_payload(this: &Object) -> &mut IosDisplay { unsafe { let ptr: *mut c_void = *this.get_ivar("display_ptr"); - &mut *(ptr as *mut WindowPayload) + &mut *(ptr as *mut IosDisplay) } } @@ -141,12 +63,10 @@ pub fn define_glk_or_mtk_view(superclass: &Class) -> *const Class { let ios_touch: ObjcId = msg_send![enumerator, nextObject]; let mut ios_pos: NSPoint = msg_send![ios_touch, locationInView: this]; - tl_display::with(|d| { - if d.data.high_dpi { - ios_pos.x *= 2.; - ios_pos.y *= 2.; - } - }); + if native_display().lock().unwrap().high_dpi { + ios_pos.x *= 2.; + ios_pos.y *= 2.; + } callback(touch_id, ios_pos.x as _, ios_pos.y as _); } @@ -244,7 +164,7 @@ pub fn define_glk_or_mtk_view_dlg(superclass: &Class) -> *const Class { if payload.event_handler.is_none() { let f = payload.f.take().unwrap(); - if tl_display::with(|d| d.gfx_api == AppleGfxApi::OpenGl) { + if payload.gfx_api == AppleGfxApi::OpenGl { crate::native::gl::load_gl_funcs(|proc| { let name = std::ffi::CString::new(proc).unwrap(); @@ -257,7 +177,7 @@ pub fn define_glk_or_mtk_view_dlg(superclass: &Class) -> *const Class { let main_screen: ObjcId = unsafe { msg_send![class!(UIScreen), mainScreen] }; let screen_rect: NSRect = unsafe { msg_send![main_screen, bounds] }; - let high_dpi = tl_display::with(|d| d.data.high_dpi); + let high_dpi = native_display().lock().unwrap().high_dpi; let (screen_width, screen_height) = if high_dpi { ( @@ -271,13 +191,14 @@ pub fn define_glk_or_mtk_view_dlg(superclass: &Class) -> *const Class { ) }; - if tl_display::with(|d| d.data.screen_width != screen_width) - || tl_display::with(|d| d.data.screen_height != screen_height) + if native_display().lock().unwrap().screen_width != screen_width + || native_display().lock().unwrap().screen_height != screen_height { - tl_display::with(|d| { - d.data.screen_width = screen_width; - d.data.screen_height = screen_height; - }); + { + let mut d = native_display().lock().unwrap(); + d.screen_width = screen_width; + d.screen_height = screen_height; + } if let Some(ref mut event_handler) = payload.event_handler { event_handler.resize_event(screen_width as _, screen_height as _); } @@ -398,6 +319,14 @@ unsafe fn create_metal_view(screen_rect: NSRect, _sample_count: i32, _high_dpi: } } +struct IosClipboard; +impl crate::native::Clipboard for IosClipboard { + fn get(&mut self) -> Option { + None + } + fn set(&mut self, _data: &str) {} +} + pub fn define_app_delegate() -> *const Class { let superclass = class!(NSObject); let mut decl = ClassDecl::new("NSAppDelegate", superclass).unwrap(); @@ -465,20 +394,21 @@ pub fn define_app_delegate() -> *const Class { (textfield_dlg, textfield) }; - tl_display::set_display(IosDisplay { - data: NativeDisplayData { - screen_width, - screen_height, - high_dpi: conf.high_dpi, - ..Default::default() - }, + let (tx, rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(IosClipboard); + crate::set_display(NativeDisplayData { + high_dpi: conf.high_dpi, + gfx_api: conf.platform.apple_gfx_api, + ..NativeDisplayData::new(conf.window_width, conf.window_height, tx, clipboard) + }); + + let payload = Box::new(IosDisplay { view: view.view, view_ctrl: view.view_ctrl, textfield, _textfield_dlg: textfield_dlg, gfx_api: conf.platform.apple_gfx_api, - }); - let payload = Box::new(WindowPayload { + f: Some(Box::new(f)), event_handler: None, _gles2: view._gles2, diff --git a/src/native/linux_wayland.rs b/src/native/linux_wayland.rs index 262a1713..b1d024a8 100644 --- a/src/native/linux_wayland.rs +++ b/src/native/linux_wayland.rs @@ -24,7 +24,8 @@ fn wl_fixed_to_double(f: i32) -> f32 { (f as f32) / 256.0 } -pub struct WaylandDisplay { +/// A thing to pass around within *void pointer of wayland's event handler +struct WaylandPayload { client: LibWaylandClient, // this is libwayland-egl.so, a library with ~4 functions // not the libEGL.so(which will be loaded, but not here) @@ -49,84 +50,9 @@ pub struct WaylandDisplay { focused_window: *mut wl_surface, //xkb_state: xkb::XkbState, decorations: Option, - closed: bool, - - data: NativeDisplayData, -} -impl crate::native::NativeDisplay for WaylandDisplay { - fn screen_size(&self) -> (f32, f32) { - (self.data.screen_width as _, self.data.screen_height as _) - } - fn dpi_scale(&self) -> f32 { - self.data.dpi_scale - } - fn high_dpi(&self) -> bool { - self.data.high_dpi - } - fn order_quit(&mut self) { - self.data.quit_ordered = true; - } - fn request_quit(&mut self) { - self.data.quit_requested = true; - } - fn cancel_quit(&mut self) { - self.data.quit_requested = false; - } - - fn set_cursor_grab(&mut self, _grab: bool) {} - fn show_mouse(&mut self, _shown: bool) {} - fn set_mouse_cursor(&mut self, _cursor_icon: crate::CursorIcon) {} - fn set_window_size(&mut self, _new_width: u32, _new_height: u32) {} - fn set_fullscreen(&mut self, _fullscreen: bool) {} - fn clipboard_get(&mut self) -> Option { - None - } - fn clipboard_set(&mut self, _data: &str) {} - fn as_any(&mut self) -> &mut dyn std::any::Any { - self - } -} -pub mod tl_display { - use super::*; - use crate::{native::NativeDisplay, NATIVE_DISPLAY}; - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub fn with(mut f: impl FnMut(&mut WaylandDisplay) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - - pub fn is_display_set() -> bool { - DISPLAY.with(|d| d.borrow().is_some()) - } - - pub fn set_display(display: WaylandDisplay) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } -} - -/// A thing to pass around within *void pointer of wayland's event handler -struct WaylandPayload { event_handler: Option>, - client: LibWaylandClient, - surface: *mut wl_surface, + closed: bool, } #[macro_export] @@ -175,7 +101,7 @@ unsafe extern "C" fn seat_handle_capabilities( seat: *mut wl_seat, caps: wl_seat_capability, ) { - let display: &mut WaylandDisplay = &mut *(data as *mut _); + let display: &mut WaylandPayload = &mut *(data as *mut _); if caps & wl_seat_capability_WL_SEAT_CAPABILITY_POINTER != 0 { // struct wl_pointer *pointer = wl_seat_get_pointer (seat); @@ -229,7 +155,7 @@ unsafe extern "C" fn keyboard_handle_keymap( fd: i32, size: u32, ) { - let display: &mut WaylandDisplay = &mut *(data as *mut _); + let display: &mut WaylandPayload = &mut *(data as *mut _); let map_shm = libc::mmap( std::ptr::null_mut::(), size as usize, @@ -274,7 +200,7 @@ unsafe extern "C" fn keyboard_handle_key( key: u32, state: u32, ) { - let display: &mut WaylandDisplay = &mut *(data as *mut _); + let display: &mut WaylandPayload = &mut *(data as *mut _); // https://wayland-book.com/seat/keyboard.html // To translate this to an XKB scancode, you must add 8 to the evdev scancode. let keysym = (display.xkb.xkb_state_key_get_one_sym)(display.xkb_state, key + 8); @@ -290,7 +216,7 @@ unsafe extern "C" fn keyboard_handle_modifiers( mods_locked: u32, group: u32, ) { - let display: &mut WaylandDisplay = &mut *(data as *mut _); + let display: &mut WaylandPayload = &mut *(data as *mut _); (display.xkb.xkb_state_update_mask)( display.xkb_state, mods_depressed, @@ -443,12 +369,7 @@ unsafe extern "C" fn registry_add_object( interface: *const ::std::os::raw::c_char, version: u32, ) { - assert!( - !tl_display::is_display_set(), - "registry_add_object assume display was not moved into a thread local yet" - ); - - let display: &mut WaylandDisplay = &mut *(data as *mut _); + let display: &mut WaylandPayload = &mut *(data as *mut _); let interface = std::ffi::CStr::from_ptr(interface).to_str().unwrap(); match interface { @@ -547,9 +468,8 @@ unsafe extern "C" fn xdg_toplevel_handle_close( _xdg_toplevel: *mut extensions::xdg_shell::xdg_toplevel, ) { assert!(!data.is_null()); - tl_display::with(|d| { - d.closed = true; - }); + let payload: &mut WaylandPayload = &mut *(data as *mut _); + payload.closed = true; } unsafe extern "C" fn xdg_toplevel_handle_configure( @@ -561,34 +481,43 @@ unsafe extern "C" fn xdg_toplevel_handle_configure( ) -> () { assert!(!data.is_null()); let payload: &mut WaylandPayload = &mut *(data as *mut _); + let mut d = crate::native_display().lock().unwrap(); if width != 0 && height != 0 { - tl_display::with(|display| { - let (egl_w, egl_h) = if display.decorations.is_some() { - // Otherwise window will resize iteself on sway - // I have no idea why - ( - width - decorations::Decorations::WIDTH * 2, - height - decorations::Decorations::BAR_HEIGHT - decorations::Decorations::WIDTH, - ) - } else { - (width, height) - }; - (display.egl.wl_egl_window_resize)(display.egl_window, egl_w, egl_h, 0, 0); - - display.data.screen_width = width; - display.data.screen_height = height; - - if let Some(ref decorations) = display.decorations { - decorations.resize(&mut display.client, width, height); - } - }); + let (egl_w, egl_h) = if payload.decorations.is_some() { + // Otherwise window will resize iteself on sway + // I have no idea why + ( + width - decorations::Decorations::WIDTH * 2, + height - decorations::Decorations::BAR_HEIGHT - decorations::Decorations::WIDTH, + ) + } else { + (width, height) + }; + (payload.egl.wl_egl_window_resize)(payload.egl_window, egl_w, egl_h, 0, 0); + + d.screen_width = width; + d.screen_height = height; + + if let Some(ref decorations) = payload.decorations { + drop(d); + decorations.resize(&mut payload.client, width, height); + } + if let Some(ref mut event_handler) = payload.event_handler { event_handler.resize_event(width as _, height as _); } } } +struct WaylandClipboard; +impl crate::native::Clipboard for WaylandClipboard { + fn get(&mut self) -> Option { + None + } + fn set(&mut self, _data: &str) {} +} + pub fn run(conf: &crate::conf::Conf, f: &mut Option) -> Option<()> where F: 'static + FnOnce() -> Box, @@ -619,7 +548,7 @@ where let xkb_context = (xkb.xkb_context_new)(0); - let mut display = WaylandDisplay { + let mut display = WaylandPayload { client: client.clone(), egl, xkb, @@ -639,11 +568,17 @@ where pointer: std::ptr::null_mut(), keyboard: std::ptr::null_mut(), focused_window: std::ptr::null_mut(), - //xkb_state: xkb::XkbState::new(), decorations: None, + event_handler: None, closed: false, - data: Default::default(), }; + + let (tx, rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(WaylandClipboard); + crate::set_display(NativeDisplayData { + ..NativeDisplayData::new(conf.window_width, conf.window_height, tx, clipboard) + }); + (display.client.wl_proxy_add_listener)( registry, ®istry_listener as *const _ as _, @@ -691,16 +626,10 @@ where configure: Some(xdg_surface_handle_configure), }; - let mut payload = WaylandPayload { - event_handler: None, - client: client.clone(), - surface: display.surface, - }; - (display.client.wl_proxy_add_listener)( xdg_surface as _, &xdg_surface_listener as *const _ as _, - &mut payload as *mut _ as _, + &mut display as *mut _ as _, ); display.xdg_toplevel = wl_request_constructor!( @@ -719,7 +648,7 @@ where (display.client.wl_proxy_add_listener)( display.xdg_toplevel as _, &xdg_toplevel_listener as *const _ as _, - &mut payload as *mut _ as _, + &mut display as *mut _ as _, ); wl_request!(display.client, display.surface, WL_SURFACE_COMMIT); @@ -773,13 +702,9 @@ where conf.window_height, )); } - display.data.screen_width = conf.window_width; - display.data.screen_height = conf.window_height; - - tl_display::set_display(display); let event_handler = (f.take().unwrap())(); - payload.event_handler = Some(event_handler); + display.event_handler = Some(event_handler); let mut keymods = KeyMods { shift: false, @@ -790,10 +715,10 @@ where let mut repeated_keys: HashSet = HashSet::new(); let (mut last_mouse_x, mut last_mouse_y) = (0.0, 0.0); - while tl_display::with(|d| d.closed == false) { + while display.closed == false { (client.wl_display_dispatch_pending)(wdisplay); - if let Some(ref mut event_handler) = payload.event_handler { + if let Some(ref mut event_handler) = display.event_handler { for keycode in &repeated_keys { event_handler.key_down_event(keycode.clone(), keymods, true); } diff --git a/src/native/linux_wayland/decorations.rs b/src/native/linux_wayland/decorations.rs index 3e4046fa..b2cbbe6d 100644 --- a/src/native/linux_wayland/decorations.rs +++ b/src/native/linux_wayland/decorations.rs @@ -10,7 +10,7 @@ use crate::{ native::linux_wayland::{ extensions::viewporter::{wp_viewport, wp_viewport_interface, wp_viewporter}, libwayland_client::*, - shm, WaylandDisplay, + shm, WaylandPayload, }, wl_request, wl_request_constructor, }; @@ -30,7 +30,7 @@ pub(crate) struct Decorations { } unsafe fn create_decoration( - display: &mut WaylandDisplay, + display: &mut WaylandPayload, compositor: *mut wl_compositor, subcompositor: *mut wl_subcompositor, parent: *mut wl_surface, @@ -81,7 +81,7 @@ impl Decorations { pub const WIDTH: i32 = 2; pub const BAR_HEIGHT: i32 = 15; - pub unsafe fn new(display: &mut WaylandDisplay, width: i32, height: i32) -> Decorations { + pub(super) unsafe fn new(display: &mut WaylandPayload, width: i32, height: i32) -> Decorations { let buffer = shm::create_shm_buffer( &mut display.client, display.shm, diff --git a/src/native/linux_x11.rs b/src/native/linux_x11.rs index b6f445ca..dbdc1776 100644 --- a/src/native/linux_x11.rs +++ b/src/native/linux_x11.rs @@ -10,7 +10,7 @@ mod xi_input; use crate::{ event::EventHandler, - native::{egl, gl, NativeDisplay, NativeDisplayData}, + native::{egl, gl, NativeDisplayData, Request}, CursorIcon, }; @@ -18,303 +18,18 @@ use libx11::*; use std::collections::HashMap; -// part of the X11 display that lives in thread local and is accessible from miniquad::window pub struct X11Display { libx11: LibX11, + libxi: xi_input::LibXi, display: *mut Display, root: Window, window: Window, - data: NativeDisplayData, + repeated_keycodes: [bool; 256], empty_cursor: libx11::Cursor, cursor_cache: HashMap, } -// part of X11 display that lives on a main loop -pub struct X11MainLoopData { - libx11: LibX11, - libxi: xi_input::LibXi, - display: *mut Display, - root: Window, - repeated_keycodes: [bool; 256], -} - -pub mod tl_display { - use super::*; - use crate::NATIVE_DISPLAY; - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub fn with(mut f: impl FnMut(&mut X11Display) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - - pub fn set_display(display: X11Display) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } -} - -impl crate::native::NativeDisplay for X11Display { - fn screen_size(&self) -> (f32, f32) { - (self.data.screen_width as _, self.data.screen_height as _) - } - fn dpi_scale(&self) -> f32 { - self.data.dpi_scale - } - fn high_dpi(&self) -> bool { - self.data.high_dpi - } - fn order_quit(&mut self) { - self.data.quit_ordered = true; - } - fn request_quit(&mut self) { - self.data.quit_requested = true; - } - fn cancel_quit(&mut self) { - self.data.quit_requested = false; - } - fn set_cursor_grab(&mut self, grab: bool) { - unsafe { - self.set_cursor_grab(self.window, grab); - } - } - fn show_mouse(&mut self, shown: bool) { - unsafe { - if shown { - self.set_cursor(self.window, Some(CursorIcon::Default)); - } else { - self.set_cursor(self.window, None); - } - } - } - - fn set_mouse_cursor(&mut self, cursor_icon: CursorIcon) { - unsafe { - self.set_cursor(self.window, Some(cursor_icon)); - } - } - - fn set_window_size(&mut self, new_width: u32, new_height: u32) { - unsafe { - self.set_window_size(self.window, new_width as i32, new_height as i32); - } - } - - fn set_fullscreen(&mut self, fullscreen: bool) { - unsafe { - self.set_fullscreen(self.window, fullscreen); - } - } - - fn clipboard_get(&mut self) -> Option { - use std::ffi::CString; - - let bufname = CString::new("CLIPBOARD").unwrap(); - let fmtname = CString::new("UTF8_STRING").unwrap(); - - unsafe { - clipboard::get_clipboard( - &mut self.libx11, - self.display, - self.window, - bufname.as_ptr(), - fmtname.as_ptr(), - ) - } - } - - fn clipboard_set(&mut self, data: &str) { - use std::ffi::CString; - - let bufname = CString::new("CLIPBOARD").unwrap(); - - unsafe { - clipboard::claim_clipboard_ownership( - &mut self.libx11, - self.display, - self.window, - bufname.as_ptr(), - data.to_owned(), - ); - }; - } - - fn as_any(&mut self) -> &mut dyn std::any::Any { - self - } -} - impl X11Display { - pub unsafe fn new(display: &mut X11MainLoopData, window: Window, w: i32, h: i32) -> X11Display { - X11Display { - libx11: display.libx11.clone(), - display: display.display, - root: display.root, - window, - empty_cursor: x_cursor::create_empty_cursor( - display.display, - display.root, - &mut display.libx11, - ), - cursor_cache: HashMap::new(), - data: NativeDisplayData { - screen_width: w, - screen_height: h, - dpi_scale: display.libx11.update_system_dpi(display.display), - ..Default::default() - }, - } - } - pub unsafe fn set_cursor_grab(&mut self, window: Window, grab: bool) { - (self.libx11.XUngrabPointer)(self.display, 0); - - if grab { - (self.libx11.XGrabPointer)( - self.display, - window, - true as _, - (ButtonPressMask - | ButtonReleaseMask - | EnterWindowMask - | LeaveWindowMask - | PointerMotionMask - | PointerMotionHintMask - | Button1MotionMask - | Button2MotionMask - | Button3MotionMask - | Button4MotionMask - | Button5MotionMask - | ButtonMotionMask - | KeymapStateMask) as libc::c_uint, - GrabModeAsync, - GrabModeAsync, - window, - 0, - 0, // CurrentTime - ); - } - - (self.libx11.XFlush)(self.display); - } - pub unsafe fn set_cursor(&mut self, window: Window, cursor: Option) { - let libx11 = &mut self.libx11; - let display = self.display; - - let cursor = match cursor { - None => self.empty_cursor, - Some(cursor_icon) => *self.cursor_cache.entry(cursor_icon).or_insert_with(|| { - (libx11.XCreateFontCursor)( - display, - match cursor_icon { - CursorIcon::Default => libx11::XC_left_ptr, - CursorIcon::Help => libx11::XC_question_arrow, - CursorIcon::Pointer => libx11::XC_hand2, - CursorIcon::Wait => libx11::XC_watch, - CursorIcon::Crosshair => libx11::XC_crosshair, - CursorIcon::Text => libx11::XC_xterm, - CursorIcon::Move => libx11::XC_fleur, - CursorIcon::NotAllowed => libx11::XC_pirate, - CursorIcon::EWResize => libx11::XC_sb_h_double_arrow, - CursorIcon::NSResize => libx11::XC_sb_v_double_arrow, - CursorIcon::NESWResize => libx11::XC_top_right_corner, - CursorIcon::NWSEResize => libx11::XC_top_left_corner, - }, - ) - }), - }; - (libx11.XDefineCursor)(display, window, cursor); - } - // TODO: _fullscreen is not used, this function always setting window fullscreen - // should be able to able to go back from fullscreen to windowed instead - unsafe fn set_fullscreen(&mut self, window: Window, _fullscreen: bool) { - let wm_state = (self.libx11.XInternAtom)( - self.display, - b"_NET_WM_STATE\x00" as *const u8 as *const _, - false as _, - ); - let wm_fullscreen = (self.libx11.XInternAtom)( - self.display, - b"_NET_WM_STATE_FULLSCREEN\x00" as *const u8 as *const _, - false as _, - ); - - // this is the first method to make window fullscreen - // hide it, change _NET_WM_STATE_FULLSCREEN property and than show it back - // someone on stackoverflow mentioned that this is not working on ubuntu/unity though - { - (self.libx11.XLowerWindow)(self.display, window); - (self.libx11.XUnmapWindow)(self.display, window); - (self.libx11.XSync)(self.display, false as _); - - let mut atoms: [Atom; 2] = [wm_fullscreen, 0 as _]; - (self.libx11.XChangeProperty)( - self.display, - window, - wm_state, - 4 as _, - 32, - PropModeReplace, - atoms.as_mut_ptr() as *mut _ as *mut _, - 1, - ); - (self.libx11.XMapWindow)(self.display, window); - (self.libx11.XRaiseWindow)(self.display, window); - (self.libx11.XFlush)(self.display); - } - - // however, this is X, so just in case - the second method - // send ClientMessage to the window with request to change property to fullscreen - { - let mut data = [0isize; 5]; - - data[0] = 1; - data[1] = wm_fullscreen as isize; - data[2] = 0; - - let mut ev = XClientMessageEvent { - type_0: 33, - serial: 0, - send_event: true as _, - message_type: wm_state, - window: window, - display: self.display, - format: 32, - data: ClientMessageData { - l: std::mem::transmute(data), - }, - }; - (self.libx11.XSendEvent)( - self.display as _, - self.root, - false as _, - (1048576 | 131072) as _, - &mut ev as *mut XClientMessageEvent as *mut _, - ); - } - } - - unsafe fn set_window_size(&mut self, window: Window, new_width: i32, new_height: i32) { - (self.libx11.XResizeWindow)(self.display, window, new_width, new_height); - (self.libx11.XFlush)(self.display); - } -} - -impl X11MainLoopData { unsafe fn process_event(&mut self, event: &mut XEvent, event_handler: &mut dyn EventHandler) { match (*event).type_0 { 2 => { @@ -398,23 +113,24 @@ impl X11MainLoopData { event_handler.window_minimized_event(); } 22 => { - if (*event).xconfigure.width != tl_display::with(|d| d.data.screen_width) - || (*event).xconfigure.height != tl_display::with(|d| d.data.screen_height) + let mut d = crate::native_display().try_lock().unwrap(); + if (*event).xconfigure.width != d.screen_width + || (*event).xconfigure.height != d.screen_height { let width = (*event).xconfigure.width; let height = (*event).xconfigure.height; - tl_display::with(|d| { - d.data.screen_width = width; - d.data.screen_height = height; - }); + d.screen_width = width; + d.screen_height = height; + drop(d); event_handler.resize_event(width as _, height as _); } } 33 => { + let mut d = crate::native_display().try_lock().unwrap(); if (*event).xclient.message_type == self.libx11.extensions.wm_protocols { let protocol = (*event).xclient.data.l[0 as libc::c_int as usize] as Atom; if protocol == self.libx11.extensions.wm_delete_window { - tl_display::with(|d| d.data.quit_requested = true); + d.quit_requested = true; } } } @@ -439,23 +155,187 @@ impl X11MainLoopData { _ => {} }; - if tl_display::with(|d| d.data.quit_requested && !d.data.quit_ordered) { + let mut d = crate::native_display().try_lock().unwrap(); + if d.quit_requested && !d.quit_ordered { + drop(d); event_handler.quit_requested_event(); - tl_display::with(|d| { - if d.data.quit_requested { - d.data.quit_ordered = true + let mut d = crate::native_display().try_lock().unwrap(); + if d.quit_requested { + d.quit_ordered = true + } + } + } + + // TODO: _fullscreen is not used, this function always setting window fullscreen + // should be able to able to go back from fullscreen to windowed instead + unsafe fn set_fullscreen(&mut self, window: Window, _fullscreen: bool) { + let wm_state = (self.libx11.XInternAtom)( + self.display, + b"_NET_WM_STATE\x00" as *const u8 as *const _, + false as _, + ); + let wm_fullscreen = (self.libx11.XInternAtom)( + self.display, + b"_NET_WM_STATE_FULLSCREEN\x00" as *const u8 as *const _, + false as _, + ); + + // this is the first method to make window fullscreen + // hide it, change _NET_WM_STATE_FULLSCREEN property and than show it back + // someone on stackoverflow mentioned that this is not working on ubuntu/unity though + { + (self.libx11.XLowerWindow)(self.display, window); + (self.libx11.XUnmapWindow)(self.display, window); + (self.libx11.XSync)(self.display, false as _); + + let mut atoms: [Atom; 2] = [wm_fullscreen, 0 as _]; + (self.libx11.XChangeProperty)( + self.display, + window, + wm_state, + 4 as _, + 32, + PropModeReplace, + atoms.as_mut_ptr() as *mut _ as *mut _, + 1, + ); + (self.libx11.XMapWindow)(self.display, window); + (self.libx11.XRaiseWindow)(self.display, window); + (self.libx11.XFlush)(self.display); + } + + // however, this is X, so just in case - the second method + // send ClientMessage to the window with request to change property to fullscreen + { + let mut data = [0isize; 5]; + + data[0] = 1; + data[1] = wm_fullscreen as isize; + data[2] = 0; + + let mut ev = XClientMessageEvent { + type_0: 33, + serial: 0, + send_event: true as _, + message_type: wm_state, + window: window, + display: self.display, + format: 32, + data: ClientMessageData { + l: std::mem::transmute(data), + }, + }; + (self.libx11.XSendEvent)( + self.display as _, + self.root, + false as _, + (1048576 | 131072) as _, + &mut ev as *mut XClientMessageEvent as *mut _, + ); + } + } + + unsafe fn set_window_size(&mut self, window: Window, new_width: i32, new_height: i32) { + (self.libx11.XResizeWindow)(self.display, window, new_width, new_height); + (self.libx11.XFlush)(self.display); + } + + fn show_mouse(&mut self, shown: bool) { + unsafe { + if shown { + self.set_cursor(self.window, Some(CursorIcon::Default)); + } else { + self.set_cursor(self.window, None); + } + } + } + + pub unsafe fn set_cursor_grab(&mut self, window: Window, grab: bool) { + (self.libx11.XUngrabPointer)(self.display, 0); + + if grab { + (self.libx11.XGrabPointer)( + self.display, + window, + true as _, + (ButtonPressMask + | ButtonReleaseMask + | EnterWindowMask + | LeaveWindowMask + | PointerMotionMask + | PointerMotionHintMask + | Button1MotionMask + | Button2MotionMask + | Button3MotionMask + | Button4MotionMask + | Button5MotionMask + | ButtonMotionMask + | KeymapStateMask) as libc::c_uint, + GrabModeAsync, + GrabModeAsync, + window, + 0, + 0, // CurrentTime + ); + } + + (self.libx11.XFlush)(self.display); + } + pub unsafe fn set_cursor(&mut self, window: Window, cursor: Option) { + let libx11 = &mut self.libx11; + let display = self.display; + + let cursor = match cursor { + None => self.empty_cursor, + Some(cursor_icon) => *self.cursor_cache.entry(cursor_icon).or_insert_with(|| { + (libx11.XCreateFontCursor)( + display, + match cursor_icon { + CursorIcon::Default => libx11::XC_left_ptr, + CursorIcon::Help => libx11::XC_question_arrow, + CursorIcon::Pointer => libx11::XC_hand2, + CursorIcon::Wait => libx11::XC_watch, + CursorIcon::Crosshair => libx11::XC_crosshair, + CursorIcon::Text => libx11::XC_xterm, + CursorIcon::Move => libx11::XC_fleur, + CursorIcon::NotAllowed => libx11::XC_pirate, + CursorIcon::EWResize => libx11::XC_sb_h_double_arrow, + CursorIcon::NSResize => libx11::XC_sb_v_double_arrow, + CursorIcon::NESWResize => libx11::XC_top_right_corner, + CursorIcon::NWSEResize => libx11::XC_top_left_corner, + }, + ) + }), + }; + (libx11.XDefineCursor)(display, window, cursor); + } + + fn process_request(&mut self, request: Request) { + use Request::*; + unsafe { + match request { + SetCursorGrab(grab) => self.set_cursor_grab(self.window, grab), + ShowMouse(show) => self.show_mouse(show), + SetMouseCursor(icon) => self.set_cursor(self.window, Some(icon)), + SetWindowSize { + new_width, + new_height, + } => self.set_window_size(self.window, new_width as _, new_height as _), + SetFullscreen(fullscreen) => self.set_fullscreen(self.window, fullscreen), + ShowKeyboard(show) => { + eprintln!("Not implemented for X11") } - }); + } } } } unsafe fn glx_main_loop( - mut display: X11MainLoopData, + mut display: X11Display, conf: &crate::conf::Conf, f: &mut Option, screen: i32, -) -> Result<(), X11MainLoopData> +) -> Result<(), X11Display> where F: 'static + FnOnce() -> Box, { @@ -465,11 +345,12 @@ where }; let visual = glx.visual; let depth = glx.depth; - let window = display - .libx11 - .create_window(display.root, display.display, visual, depth, conf); + display.window = + display + .libx11 + .create_window(display.root, display.display, visual, depth, conf); - let (glx_context, glx_window) = glx.create_context(display.display, window); + let (glx_context, glx_window) = glx.create_context(display.display, display.window); glx.swap_interval( display.display, glx_window, @@ -478,21 +359,35 @@ where ); gl::load_gl_funcs(|proc| glx.libgl.get_procaddr(proc)); - display.libx11.show_window(display.display, window); + display.libx11.show_window(display.display, display.window); (display.libx11.XFlush)(display.display); - let (w, h) = display.libx11.query_window_size(display.display, window); - - tl_display::set_display(X11Display::new(&mut display, window, w, h)); + let (w, h) = display + .libx11 + .query_window_size(display.display, display.window); + let (tx, rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(clipboard::X11Clipboard::new( + display.libx11.clone(), + display.display, + display.window, + )); + crate::set_display(NativeDisplayData { + high_dpi: conf.high_dpi, + dpi_scale: display.libx11.update_system_dpi(display.display), + ..NativeDisplayData::new(w, h, tx, clipboard) + }); if conf.fullscreen { - tl_display::with(|d| d.set_fullscreen(window, true)); + display.set_fullscreen(display.window, true); } let mut event_handler = (f.take().unwrap())(); - while !tl_display::with(|d| d.data.quit_ordered) { + while !crate::native_display().try_lock().unwrap().quit_ordered { + while let Ok(request) = rx.try_recv() { + display.process_request(request); + } glx.make_current(display.display, glx_window, glx_context); let count = (display.libx11.XPending)(display.display); @@ -510,18 +405,18 @@ where } glx.destroy_context(display.display, glx_window, glx_context); - (display.libx11.XUnmapWindow)(display.display, window); - (display.libx11.XDestroyWindow)(display.display, window); + (display.libx11.XUnmapWindow)(display.display, display.window); + (display.libx11.XDestroyWindow)(display.display, display.window); (display.libx11.XCloseDisplay)(display.display); Ok(()) } unsafe fn egl_main_loop( - mut display: X11MainLoopData, + mut display: X11Display, conf: &crate::conf::Conf, f: &mut Option, -) -> Result<(), X11MainLoopData> +) -> Result<(), X11Display> where F: 'static + FnOnce() -> Box, { @@ -530,7 +425,7 @@ where _ => return Err(display), }; - let window = + display.window = display .libx11 .create_window(display.root, display.display, std::ptr::null_mut(), 0, conf); @@ -545,7 +440,7 @@ where let egl_surface = (egl_lib.eglCreateWindowSurface.unwrap())( egl_display, config, - window, + display.window, std::ptr::null_mut(), ); @@ -563,20 +458,35 @@ where .expect("non-null function pointer")(name.as_ptr() as _) }); - display.libx11.show_window(display.display, window); - let (w, h) = display.libx11.query_window_size(display.display, window); - - tl_display::set_display(X11Display::new(&mut display, window, w, h)); + display.libx11.show_window(display.display, display.window); + let (w, h) = display + .libx11 + .query_window_size(display.display, display.window); + let (tx, rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(clipboard::X11Clipboard::new( + display.libx11.clone(), + display.display, + display.window, + )); + crate::set_display(NativeDisplayData { + high_dpi: conf.high_dpi, + dpi_scale: display.libx11.update_system_dpi(display.display), + ..NativeDisplayData::new(w, h, tx, clipboard) + }); if conf.fullscreen { - tl_display::with(|d| d.set_fullscreen(window, true)); + display.set_fullscreen(display.window, true) } (display.libx11.XFlush)(display.display); let mut event_handler = (f.take().unwrap())(); - while !tl_display::with(|d| d.data.quit_ordered) { + while !crate::native_display().try_lock().unwrap().quit_ordered { + while let Ok(request) = rx.try_recv() { + display.process_request(request); + } + let count = (display.libx11.XPending)(display.display); for _ in 0..count { @@ -592,8 +502,8 @@ where (display.libx11.XFlush)(display.display); } - (display.libx11.XUnmapWindow)(display.display, window); - (display.libx11.XDestroyWindow)(display.display, window); + (display.libx11.XUnmapWindow)(display.display, display.window); + (display.libx11.XDestroyWindow)(display.display, display.window); (display.libx11.XCloseDisplay)(display.display); Ok(()) @@ -633,12 +543,15 @@ where (libx11.XkbSetDetectableAutoRepeat)(x11_display, true as _, std::ptr::null_mut()); libx11.load_extensions(x11_display); - let mut display = X11MainLoopData { + let mut display = X11Display { + empty_cursor: x_cursor::create_empty_cursor(x11_display, x11_root, &mut libx11), display: x11_display, root: x11_root, + window: 0, libx11, libxi, repeated_keycodes: [false; 256], + cursor_cache: HashMap::new(), }; display diff --git a/src/native/linux_x11/clipboard.rs b/src/native/linux_x11/clipboard.rs index 8ace5283..aba5ab58 100644 --- a/src/native/linux_x11/clipboard.rs +++ b/src/native/linux_x11/clipboard.rs @@ -13,7 +13,7 @@ const AnyPropertyType: libc::c_long = 0 as libc::c_long; type Time = libc::c_ulong; -pub unsafe fn get_clipboard( +unsafe fn get_clipboard( libx11: &mut LibX11, display: *mut Display, window: Window, @@ -103,7 +103,7 @@ static mut MESSAGE: Option = None; /// Claim that our app is X11 clipboard owner /// Now when some other linux app will ask X11 for clipboard content - it will be redirected to our app -pub unsafe fn claim_clipboard_ownership( +unsafe fn claim_clipboard_ownership( libx11: &mut LibX11, display: *mut Display, window: Window, @@ -185,3 +185,55 @@ pub(crate) unsafe fn respond_to_clipboard_request( ); } } + +pub struct X11Clipboard { + libx11: LibX11, + display: *mut Display, + window: Window, +} +unsafe impl Send for X11Clipboard {} +unsafe impl Sync for X11Clipboard {} + +impl X11Clipboard { + pub fn new(libx11: LibX11, display: *mut Display, window: Window) -> X11Clipboard { + X11Clipboard { + libx11, + display, + window, + } + } +} +impl crate::native::Clipboard for X11Clipboard { + fn get(&mut self) -> Option { + use std::ffi::CString; + + let bufname = CString::new("CLIPBOARD").unwrap(); + let fmtname = CString::new("UTF8_STRING").unwrap(); + + unsafe { + get_clipboard( + &mut self.libx11, + self.display, + self.window, + bufname.as_ptr(), + fmtname.as_ptr(), + ) + } + } + + fn set(&mut self, data: &str) { + use std::ffi::CString; + + let bufname = CString::new("CLIPBOARD").unwrap(); + + unsafe { + claim_clipboard_ownership( + &mut self.libx11, + self.display, + self.window, + bufname.as_ptr(), + data.to_owned(), + ); + }; + } +} diff --git a/src/native/linux_x11/glx.rs b/src/native/linux_x11/glx.rs index f164e8e6..675ff597 100644 --- a/src/native/linux_x11/glx.rs +++ b/src/native/linux_x11/glx.rs @@ -1,6 +1,6 @@ #![allow(dead_code, non_snake_case)] -use super::{libx11::*}; +use super::libx11::*; use crate::native::module; diff --git a/src/native/linux_x11/libx11_ex.rs b/src/native/linux_x11/libx11_ex.rs index 6c3e0f26..3b3b6ae2 100644 --- a/src/native/linux_x11/libx11_ex.rs +++ b/src/native/linux_x11/libx11_ex.rs @@ -61,7 +61,12 @@ impl LibX11 { (attribs.width, attribs.height) } - pub unsafe fn update_window_title(&mut self, display: *mut Display, window: Window, title: &str) { + pub unsafe fn update_window_title( + &mut self, + display: *mut Display, + window: Window, + title: &str, + ) { let c_title = std::ffi::CString::new(title).unwrap(); (self.Xutf8SetWMProperties)( diff --git a/src/native/macos.rs b/src/native/macos.rs index 0754fe11..0ce4a8f7 100644 --- a/src/native/macos.rs +++ b/src/native/macos.rs @@ -8,17 +8,16 @@ use { event::{EventHandler, MouseButton}, native::{ apple::{apple_util::*, frameworks::*}, - gl, NativeDisplay, NativeDisplayData, + gl, NativeDisplayData, Request, }, - CursorIcon, + native_display, CursorIcon, }, - std::{collections::HashMap, os::raw::c_void}, + std::{collections::HashMap, os::raw::c_void, sync::mpsc::Receiver}, }; pub struct MacosDisplay { window: ObjcId, view: ObjcId, - data: NativeDisplayData, fullscreen: bool, // [NSCursor hide]/unhide calls should be balanced // hide/hide/unhide will keep cursor hidden @@ -28,61 +27,14 @@ pub struct MacosDisplay { current_cursor: CursorIcon, cursors: HashMap, gfx_api: crate::conf::AppleGfxApi, -} - -mod tl_display { - use super::*; - use crate::NATIVE_DISPLAY; - - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn crate::native::NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub(super) fn with(mut f: impl FnMut(&mut MacosDisplay) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - pub(super) fn set_display(display: MacosDisplay) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } + event_handler: Option>, + f: Option Box>>, + modifiers: Modifiers, + native_requests: Receiver, } -impl NativeDisplay for MacosDisplay { - fn screen_size(&self) -> (f32, f32) { - (self.data.screen_width as _, self.data.screen_height as _) - } - fn dpi_scale(&self) -> f32 { - self.data.dpi_scale - } - fn high_dpi(&self) -> bool { - self.data.high_dpi - } - fn order_quit(&mut self) { - self.data.quit_ordered = true; - } - fn request_quit(&mut self) { - self.data.quit_requested = true; - } - fn cancel_quit(&mut self) { - self.data.quit_requested = false; - } - - fn set_cursor_grab(&mut self, _grab: bool) {} +impl MacosDisplay { fn show_mouse(&mut self, show: bool) { if show && !self.cursor_shown { unsafe { @@ -145,49 +97,41 @@ impl NativeDisplay for MacosDisplay { let () = msg_send![pasteboard, writeObjects: arr]; } } - #[cfg(target_vendor = "apple")] - fn apple_gfx_api(&self) -> crate::conf::AppleGfxApi { - self.gfx_api - } - #[cfg(target_vendor = "apple")] - fn apple_view(&mut self) -> Option { - Some(self.view) - } - #[cfg(target_ios = "ios")] - fn apple_view_ctrl(&mut self) -> Option { - Some(self.view_ctrl) - } - fn as_any(&mut self) -> &mut dyn std::any::Any { - self + + pub fn context(&mut self) -> Option<&mut dyn EventHandler> { + let event_handler = self.event_handler.as_deref_mut()?; + + Some(event_handler) } } impl MacosDisplay { fn transform_mouse_point(&self, point: &NSPoint) -> (f32, f32) { - let new_x = point.x as f32 * self.data.dpi_scale; - let new_y = self.data.screen_height as f32 - (point.y as f32 * self.data.dpi_scale) - 1.; + let d = native_display().lock().unwrap(); + let new_x = point.x as f32 * d.dpi_scale; + let new_y = d.screen_height as f32 - (point.y as f32 * d.dpi_scale) - 1.; (new_x, new_y) } unsafe fn update_dimensions(&mut self) -> Option<(i32, i32)> { - if self.data.high_dpi { + let mut d = native_display().lock().unwrap(); + if d.high_dpi { let screen: ObjcId = msg_send![self.window, screen]; let dpi_scale: f64 = msg_send![screen, backingScaleFactor]; - self.data.dpi_scale = dpi_scale as f32; + d.dpi_scale = dpi_scale as f32; } else { - self.data.dpi_scale = 1.0; + d.dpi_scale = 1.0; } let bounds: NSRect = msg_send![self.view, bounds]; - let screen_width = (bounds.size.width as f32 * self.data.dpi_scale) as i32; - let screen_height = (bounds.size.height as f32 * self.data.dpi_scale) as i32; + let screen_width = (bounds.size.width as f32 * d.dpi_scale) as i32; + let screen_height = (bounds.size.height as f32 * d.dpi_scale) as i32; - let dim_changed = - screen_width != self.data.screen_width || screen_height != self.data.screen_height; + let dim_changed = screen_width != d.screen_width || screen_height != d.screen_height; - self.data.screen_width = screen_width; - self.data.screen_height = screen_height; + d.screen_width = screen_width; + d.screen_height = screen_height; if dim_changed { Some((screen_width, screen_height)) @@ -195,6 +139,22 @@ impl MacosDisplay { None } } + + fn process_request(&mut self, request: Request) { + use Request::*; + unsafe { + match request { + ShowMouse(show) => self.show_mouse(show), + SetMouseCursor(icon) => self.set_mouse_cursor(icon), + SetWindowSize { + new_width, + new_height, + } => self.set_window_size(new_width as _, new_height as _), + SetFullscreen(fullscreen) => self.set_fullscreen(fullscreen), + _ => {} + } + } + } } #[derive(Default)] @@ -235,20 +195,6 @@ impl Modifiers { } } } - -struct WindowPayload { - event_handler: Option>, - f: Option Box>>, - modifiers: Modifiers, -} - -impl WindowPayload { - pub fn context(&mut self) -> Option<&mut dyn EventHandler> { - let event_handler = self.event_handler.as_deref_mut()?; - - Some(event_handler) - } -} pub fn define_app_delegate() -> *const Class { let superclass = class!(NSObject); let mut decl = ClassDecl::new("NSAppDelegate", superclass).unwrap(); @@ -272,20 +218,20 @@ pub fn define_cocoa_window_delegate() -> *const Class { } // only give user-code a chance to intervene when sapp_quit() wasn't already called - if !tl_display::with(|d| d.data.quit_ordered) { + if !native_display().lock().unwrap().quit_ordered { // if window should be closed and event handling is enabled, give user code // a chance to intervene via sapp_cancel_quit() - tl_display::with(|d| d.data.quit_requested = true); + native_display().lock().unwrap().quit_requested = true; if let Some(event_handler) = payload.context() { event_handler.quit_requested_event(); } // user code hasn't intervened, quit the app - if tl_display::with(|d| d.data.quit_requested) { - tl_display::with(|d| d.data.quit_ordered = true); + if native_display().lock().unwrap().quit_requested { + native_display().lock().unwrap().quit_ordered = true; } } - if tl_display::with(|d| d.data.quit_ordered) { + if native_display().lock().unwrap().quit_ordered { return YES; } else { return NO; @@ -294,7 +240,7 @@ pub fn define_cocoa_window_delegate() -> *const Class { extern "C" fn window_did_resize(this: &Object, _: Sel, _: ObjcId) { let payload = get_window_payload(this); - if let Some((w, h)) = unsafe { tl_display::with(|d| d.update_dimensions()) } { + if let Some((w, h)) = unsafe { payload.update_dimensions() } { if let Some(event_handler) = payload.context() { event_handler.resize_event(w as _, h as _); } @@ -303,17 +249,19 @@ pub fn define_cocoa_window_delegate() -> *const Class { extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: ObjcId) { let payload = get_window_payload(this); - if let Some((w, h)) = unsafe { tl_display::with(|d| d.update_dimensions()) } { + if let Some((w, h)) = unsafe { payload.update_dimensions() } { if let Some(event_handler) = payload.context() { event_handler.resize_event(w as _, h as _); } } } - extern "C" fn window_did_enter_fullscreen(_: &Object, _: Sel, _: ObjcId) { - tl_display::with(|d| d.fullscreen = true); + extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: ObjcId) { + let payload = get_window_payload(this); + payload.fullscreen = true; } - extern "C" fn window_did_exit_fullscreen(_: &Object, _: Sel, _: ObjcId) { - tl_display::with(|d| d.fullscreen = false); + extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: ObjcId) { + let payload = get_window_payload(this); + payload.fullscreen = false; } let superclass = class!(NSObject); let mut decl = ClassDecl::new("RenderWindowDelegate", superclass).unwrap(); @@ -383,7 +331,7 @@ unsafe fn view_base_decl(decl: &mut ClassDecl) { unsafe { let point: NSPoint = msg_send!(event, locationInWindow); - let point = tl_display::with(|d| d.transform_mouse_point(&point)); + let point = payload.transform_mouse_point(&point); if let Some(event_handler) = payload.context() { event_handler.mouse_motion_event(point.0, point.1); } @@ -395,7 +343,7 @@ unsafe fn view_base_decl(decl: &mut ClassDecl) { unsafe { let point: NSPoint = msg_send!(event, locationInWindow); - let point = tl_display::with(|d| d.transform_mouse_point(&point)); + let point = payload.transform_mouse_point(&point); if let Some(event_handler) = payload.context() { if down { event_handler.mouse_button_down_event(btn, point.0, point.1); @@ -439,16 +387,18 @@ unsafe fn view_base_decl(decl: &mut ClassDecl) { } } extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) { + let payload = get_window_payload(this); + unsafe { - let cursor_id = tl_display::with(|d| { - let current_cursor = d.current_cursor; - let cursor_id = *d + let cursor_id = { + let current_cursor = payload.current_cursor; + let cursor_id = *payload .cursors .entry(current_cursor) .or_insert_with(|| load_mouse_cursor(current_cursor.clone())); assert!(!cursor_id.is_null()); cursor_id - }); + }; let bounds: NSRect = msg_send![this, bounds]; let _: () = msg_send![ @@ -488,7 +438,7 @@ unsafe fn view_base_decl(decl: &mut ClassDecl) { extern "C" fn flags_changed(this: &Object, _sel: Sel, event: ObjcId) { fn produce_event( - payload: &mut WindowPayload, + payload: &mut MacosDisplay, keycode: crate::KeyCode, mods: crate::KeyMods, old_pressed: bool, @@ -649,7 +599,7 @@ pub fn define_opengl_view_class() -> *const Class { let superclass = superclass(this); let () = msg_send![super(this, superclass), reshape]; - if let Some((w, h)) = tl_display::with(|d| d.update_dimensions()) { + if let Some((w, h)) = payload.update_dimensions() { if let Some(event_handler) = payload.context() { event_handler.resize_event(w as _, h as _); } @@ -659,6 +609,11 @@ pub fn define_opengl_view_class() -> *const Class { extern "C" fn draw_rect(this: &Object, _sel: Sel, _rect: NSRect) { let payload = get_window_payload(this); + + while let Ok(request) = payload.native_requests.try_recv() { + payload.process_request(request); + } + if let Some(event_handler) = payload.context() { event_handler.update(); event_handler.draw(); @@ -669,9 +624,10 @@ pub fn define_opengl_view_class() -> *const Class { assert!(!ctx.is_null()); let () = msg_send![ctx, flushBuffer]; - if tl_display::with(|d| d.data.quit_requested || d.data.quit_ordered) { - let window = tl_display::with(|d| d.window); - let () = msg_send![window, performClose: nil]; + let d = native_display().lock().unwrap(); + if d.quit_requested || d.quit_ordered { + drop(d); + let () = msg_send![payload.window, performClose: nil]; } } } @@ -750,15 +706,20 @@ pub fn define_metal_view_class() -> *const Class { payload.event_handler = Some(f()); } + while let Ok(request) = payload.native_requests.try_recv() { + payload.process_request(request); + } + if let Some(event_handler) = payload.context() { event_handler.update(); event_handler.draw(); } unsafe { - if tl_display::with(|d| d.data.quit_requested || d.data.quit_ordered) { - let window = tl_display::with(|d| d.window); - let () = msg_send![window, performClose: nil]; + let d = native_display().lock().unwrap(); + if d.quit_requested || d.quit_ordered { + drop(d); + let () = msg_send![payload.window, performClose: nil]; } } } @@ -780,10 +741,10 @@ pub fn define_metal_view_class() -> *const Class { return decl.register(); } -fn get_window_payload(this: &Object) -> &mut WindowPayload { +fn get_window_payload(this: &Object) -> &mut MacosDisplay { unsafe { let ptr: *mut c_void = *this.get_ivar("display_ptr"); - &mut *(ptr as *mut WindowPayload) + &mut *(ptr as *mut MacosDisplay) } } @@ -855,28 +816,38 @@ unsafe fn create_opengl_view(window_frame: NSRect, sample_count: i32, high_dpi: view } +struct MacosClipboard; +impl crate::native::Clipboard for MacosClipboard { + fn get(&mut self) -> Option { + None + } + fn set(&mut self, _data: &str) {} +} + pub unsafe fn run(conf: crate::conf::Conf, f: F) where F: 'static + FnOnce() -> Box, { - let mut payload = WindowPayload { - f: Some(Box::new(f)), - event_handler: None, - modifiers: Modifiers::default(), - }; + let (tx, rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(MacosClipboard); + crate::set_display(NativeDisplayData { + high_dpi: conf.high_dpi, + gfx_api: conf.platform.apple_gfx_api, + ..NativeDisplayData::new(conf.window_width, conf.window_height, tx, clipboard) + }); let mut display = MacosDisplay { view: std::ptr::null_mut(), window: std::ptr::null_mut(), - data: NativeDisplayData { - high_dpi: conf.high_dpi, - ..Default::default() - }, fullscreen: false, cursor_shown: true, current_cursor: CursorIcon::Default, cursors: HashMap::new(), gfx_api: conf.platform.apple_gfx_api, + f: Some(Box::new(f)), + event_handler: None, + native_requests: rx, + modifiers: Modifiers::default(), }; let app_delegate_class = define_app_delegate(); @@ -919,7 +890,7 @@ where let window_delegate: ObjcId = msg_send![window_delegate_class, new]; let () = msg_send![window, setDelegate: window_delegate]; - (*window_delegate).set_ivar("display_ptr", &mut payload as *mut _ as *mut c_void); + (*window_delegate).set_ivar("display_ptr", &mut display as *mut _ as *mut c_void); let title = str_to_nsstring(&conf.window_title); //let () = msg_send![window, setReleasedWhenClosed: NO]; @@ -931,7 +902,11 @@ where AppleGfxApi::OpenGl => create_opengl_view(window_frame, conf.sample_count, conf.high_dpi), AppleGfxApi::Metal => create_metal_view(window_frame, conf.sample_count, conf.high_dpi), }; - (*view).set_ivar("display_ptr", &mut payload as *mut _ as *mut c_void); + { + let mut d = native_display().lock().unwrap(); + d.view = view; + } + (*view).set_ivar("display_ptr", &mut display as *mut _ as *mut c_void); display.window = window; display.view = view; @@ -940,8 +915,6 @@ where let _ = display.update_dimensions(); - tl_display::set_display(display); - let nstimer: ObjcId = msg_send![ class!(NSTimer), timerWithTimeInterval: 0.001 diff --git a/src/native/wasm.rs b/src/native/wasm.rs index 4990ca0a..e6208fb6 100644 --- a/src/native/wasm.rs +++ b/src/native/wasm.rs @@ -5,81 +5,31 @@ mod keycodes; pub use webgl::*; -use std::{cell::RefCell, path::PathBuf, thread_local}; - -use crate::{event::EventHandler, native::NativeDisplay}; - -#[derive(Default)] -struct DroppedFiles { - paths: Vec, - bytes: Vec>, -} - -struct WasmDisplay { - clipboard: Option, - screen_width: f32, - screen_height: f32, - dropped_files: DroppedFiles, -} - -impl NativeDisplay for WasmDisplay { - fn screen_size(&self) -> (f32, f32) { - (self.screen_width as _, self.screen_height as _) - } - fn dpi_scale(&self) -> f32 { - 1. - } - fn high_dpi(&self) -> bool { - true - } - fn order_quit(&mut self) { - // there is no escape from wasm - } - fn request_quit(&mut self) { - // there is no escape from wasm - } - fn cancel_quit(&mut self) { - // there is no escape from wasm - } - fn set_cursor_grab(&mut self, grab: bool) { - unsafe { sapp_set_cursor_grab(grab) }; - } - fn show_mouse(&mut self, shown: bool) { - unsafe { show_mouse(shown) }; - } - fn set_mouse_cursor(&mut self, cursor: crate::CursorIcon) { - unsafe { - set_mouse_cursor(cursor); - } - } - fn set_window_size(&mut self, _new_width: u32, _new_height: u32) {} - fn set_fullscreen(&mut self, fullscreen: bool) { - unsafe { - sapp_set_fullscreen(fullscreen); - } - } - fn clipboard_get(&mut self) -> Option { - clipboard_get() - } - fn clipboard_set(&mut self, data: &str) { - clipboard_set(data) - } - fn as_any(&mut self) -> &mut dyn std::any::Any { - self - } - fn dropped_file_count(&mut self) -> usize { - self.dropped_files.bytes.len() - } - fn dropped_file_bytes(&mut self, index: usize) -> Option> { - self.dropped_files.bytes.get(index).cloned() - } - fn dropped_file_path(&mut self, index: usize) -> Option { - self.dropped_files.paths.get(index).cloned() - } -} +use std::{ + cell::RefCell, + path::PathBuf, + sync::{mpsc::Receiver, Mutex, OnceLock}, + thread_local, +}; + +use crate::{ + event::EventHandler, + native::{NativeDisplayData, Request}, +}; + +// fn dropped_file_count(&mut self) -> usize { +// self.dropped_files.bytes.len() +// } +// fn dropped_file_bytes(&mut self, index: usize) -> Option> { +// self.dropped_files.bytes.get(index).cloned() +// } +// fn dropped_file_path(&mut self, index: usize) -> Option { +// self.dropped_files.paths.get(index).cloned() +// } thread_local! { static EVENT_HANDLER: RefCell>> = RefCell::new(None); + static REQUESTS: RefCell>> = RefCell::new(None); } fn tl_event_handler T>(f: F) -> T { EVENT_HANDLER.with(|globals| { @@ -89,38 +39,6 @@ fn tl_event_handler T>(f: F) -> T { }) } -mod tl_display { - use super::*; - use crate::{native::NativeDisplay, NATIVE_DISPLAY}; - - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub(super) fn with(f: impl FnOnce(&mut WasmDisplay) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - - pub(super) fn set_display(display: WasmDisplay) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } -} - static mut CURSOR_ICON: crate::CursorIcon = crate::CursorIcon::Default; static mut CURSOR_SHOW: bool = true; @@ -154,11 +72,13 @@ where setup_canvas_size(conf.high_dpi); } - tl_display::set_display(WasmDisplay { - clipboard: None, - screen_width: unsafe { canvas_width() as _ }, - screen_height: unsafe { canvas_height() as _ }, - dropped_files: Default::default(), + let (tx, rx) = std::sync::mpsc::channel(); + REQUESTS.with(|r| *r.borrow_mut() = Some(rx)); + let w = unsafe { canvas_width() as _ }; + let h = unsafe { canvas_height() as _ }; + let clipboard = Box::new(Clipboard); + crate::set_display(NativeDisplayData { + ..NativeDisplayData::new(w, h, tx, clipboard) }); EVENT_HANDLER.with(|g| { *g.borrow_mut() = Some(f()); @@ -265,25 +185,49 @@ pub extern "C" fn allocate_vec_u8(len: usize) -> *mut u8 { ptr } -#[no_mangle] -pub extern "C" fn on_clipboard_paste(msg: *mut u8, len: usize) { - let msg = unsafe { String::from_raw_parts(msg, len, len) }; +static CLIPBOARD: OnceLock>> = OnceLock::new(); +struct Clipboard; +impl crate::native::Clipboard for Clipboard { + fn get(&mut self) -> Option { + CLIPBOARD + .get_or_init(|| Mutex::new(None)) + .lock() + .unwrap() + .clone() + } - tl_display::with(move |display| display.clipboard = Some(msg)); + fn set(&mut self, data: &str) { + let len = data.len(); + let data = std::ffi::CString::new(data).unwrap(); + unsafe { sapp_set_clipboard(data.as_ptr(), len) }; + } } -pub fn clipboard_get() -> Option { - tl_display::with(|display| display.clipboard.clone()) -} +#[no_mangle] +pub extern "C" fn on_clipboard_paste(msg: *mut u8, len: usize) { + let msg = unsafe { String::from_raw_parts(msg, len, len) }; -pub fn clipboard_set(data: &str) { - let len = data.len(); - let data = std::ffi::CString::new(data).unwrap(); - unsafe { sapp_set_clipboard(data.as_ptr(), len) }; + *CLIPBOARD.get_or_init(|| Mutex::new(None)).lock().unwrap() = Some(msg); } #[no_mangle] pub extern "C" fn frame() { + REQUESTS.with(|r| { + while let Ok(request) = r.borrow_mut().as_mut().unwrap().try_recv() { + use Request::*; + match request { + Request::SetCursorGrab(grab) => unsafe { sapp_set_cursor_grab(grab) }, + Request::ShowMouse(show) => unsafe { show_mouse(show) }, + Request::SetMouseCursor(cursor) => unsafe { + set_mouse_cursor(cursor); + }, + Request::SetFullscreen(fullscreen) => unsafe { + sapp_set_fullscreen(fullscreen); + }, + _ => {} + } + } + }); tl_event_handler(|event_handler| { event_handler.update(); event_handler.draw(); @@ -360,10 +304,11 @@ pub extern "C" fn key_up(key: u32, modifiers: u32) { #[no_mangle] pub extern "C" fn resize(width: i32, height: i32) { - tl_display::with(|display| { - display.screen_width = width as _; - display.screen_height = height as _; - }); + { + let mut d = crate::native_display().lock().unwrap(); + d.screen_width = width as _; + d.screen_height = height as _; + } tl_event_handler(|event_handler| { event_handler.resize_event(width as _, height as _); }); @@ -390,9 +335,8 @@ pub extern "C" fn focus(has_focus: bool) { #[no_mangle] pub extern "C" fn on_files_dropped_start() { - tl_display::with(|display| { - display.dropped_files = Default::default(); - }); + let mut d = crate::native_display().lock().unwrap(); + d.dropped_files = Default::default(); } #[no_mangle] @@ -407,11 +351,10 @@ pub extern "C" fn on_file_dropped( bytes: *mut u8, bytes_len: usize, ) { - tl_display::with(|display| { - let path = PathBuf::from(unsafe { String::from_raw_parts(path, path_len, path_len) }); - let bytes = unsafe { Vec::from_raw_parts(bytes, bytes_len, bytes_len) }; + let mut d = crate::native_display().lock().unwrap(); + let path = PathBuf::from(unsafe { String::from_raw_parts(path, path_len, path_len) }); + let bytes = unsafe { Vec::from_raw_parts(bytes, bytes_len, bytes_len) }; - display.dropped_files.paths.push(path); - display.dropped_files.bytes.push(bytes); - }); + d.dropped_files.paths.push(path); + d.dropped_files.bytes.push(bytes); } diff --git a/src/native/windows.rs b/src/native/windows.rs index 96403e05..7d4f3c71 100644 --- a/src/native/windows.rs +++ b/src/native/windows.rs @@ -1,7 +1,7 @@ use crate::{ conf::{Conf, Icon}, event::{KeyMods, MouseButton}, - native::NativeDisplayData, + native::{NativeDisplayData, Request}, CursorIcon, EventHandler, }; @@ -34,7 +34,6 @@ pub(crate) struct WindowsDisplay { window_resizable: bool, cursor_grabbed: bool, iconified: bool, - display_data: NativeDisplayData, content_scale: f32, window_scale: f32, mouse_scale: f32, @@ -48,63 +47,10 @@ pub(crate) struct WindowsDisplay { msg_dc: HDC, wnd: HWND, dc: HDC, + event_handler: Option>, } -mod tl_display { - use super::*; - use crate::{native::NativeDisplay, NATIVE_DISPLAY}; - - use std::cell::RefCell; - - thread_local! { - static DISPLAY: RefCell> = RefCell::new(None); - } - - fn with_native_display(f: &mut dyn FnMut(&mut dyn NativeDisplay)) { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d); - }) - } - - pub(super) fn with(mut f: impl FnMut(&mut WindowsDisplay) -> T) -> T { - DISPLAY.with(|d| { - let mut d = d.borrow_mut(); - let d = d.as_mut().unwrap(); - f(&mut *d) - }) - } - - pub(super) fn set_display(display: WindowsDisplay) { - DISPLAY.with(|d| *d.borrow_mut() = Some(display)); - NATIVE_DISPLAY.with(|d| *d.borrow_mut() = Some(with_native_display)); - } -} - -impl crate::native::NativeDisplay for WindowsDisplay { - fn screen_size(&self) -> (f32, f32) { - ( - self.display_data.screen_width as _, - self.display_data.screen_height as _, - ) - } - fn dpi_scale(&self) -> f32 { - self.content_scale - } - fn high_dpi(&self) -> bool { - self.content_scale != 1. - } - fn order_quit(&mut self) { - self.display_data.quit_ordered = true; - } - fn request_quit(&mut self) { - self.display_data.quit_requested = true; - } - fn cancel_quit(&mut self) { - self.display_data.quit_requested = false; - } - +impl WindowsDisplay { fn set_cursor_grab(&mut self, grab: bool) { self.cursor_grabbed = grab; unsafe { @@ -171,35 +117,17 @@ impl crate::native::NativeDisplay for WindowsDisplay { final_new_height = (rect.bottom - rect.top) as _; } - // TODO: find a better solution! - // miniquad assumes that wndproc is only triggered from main loop, from - // DispatchMessage - // which makes it safe to borrow NativeDisplay in the wndproc. - // Well.. this assumption was wrong. - // When new message is coming to a window, WinAPI check the thread. If the - // messages comes from the same thread - it calls the handler directly, without - // waiting for a queue processing. - // This makes impossible to call basically any winapi function directly - // from miniquad's event handlers. - // This was a miniquad's architecture mistake, something should be changed, - // but to makes things works - here is this fix. - struct SendHack(*mut T); - unsafe impl Send for SendHack {} - unsafe impl Sync for SendHack {} - let wnd = SendHack(self.wnd); - std::thread::spawn(move || { - unsafe { - SetWindowPos( - wnd.0, - HWND_TOP, - x, - y, - final_new_width as i32, - final_new_height as i32, - SWP_NOMOVE, - ) - }; - }); + unsafe { + SetWindowPos( + self.wnd, + HWND_TOP, + x, + y, + final_new_width as i32, + final_new_height as i32, + SWP_NOMOVE, + ) + }; } fn set_fullscreen(&mut self, fullscreen: bool) { @@ -214,60 +142,36 @@ impl crate::native::NativeDisplay for WindowsDisplay { SetWindowLong(self.wnd, GWL_STYLE, win_style as _); if self.fullscreen { - struct SendHack(*mut T); - unsafe impl Send for SendHack {} - unsafe impl Sync for SendHack {} - let wnd = SendHack(self.wnd); - // check set_window_size for "why threads" - std::thread::spawn(move || { - SetWindowPos( - wnd.0, - HWND_TOP, - 0, - 0, - GetSystemMetrics(SM_CXSCREEN), - GetSystemMetrics(SM_CYSCREEN), - SWP_FRAMECHANGED , - ); - }); + SetWindowPos( + self.wnd, + HWND_TOP, + 0, + 0, + GetSystemMetrics(SM_CXSCREEN), + GetSystemMetrics(SM_CYSCREEN), + SWP_FRAMECHANGED, + ); } else { - let w = self.display_data.screen_width; - let h = self.display_data.screen_height; - struct SendHack(*mut T); - unsafe impl Send for SendHack {} - unsafe impl Sync for SendHack {} - let wnd = SendHack(self.wnd); - // check set_window_size for "why threads" - std::thread::spawn(move || { - SetWindowPos( - wnd.0, - HWND_TOP, - 0, - 0, - // this is probably not correct: with high dpi content_width and window_width are actually different.. - w, - h, - SWP_FRAMECHANGED, - ); - }); + let (w, h) = { + let d = crate::native_display().lock().unwrap(); + (d.screen_width, d.screen_height) + }; + + SetWindowPos( + self.wnd, + HWND_TOP, + 0, + 0, + // this is probably not correct: with high dpi content_width and window_width are actually different.. + w, + h, + SWP_FRAMECHANGED, + ); } ShowWindow(self.wnd, SW_SHOW); }; } - fn clipboard_get(&mut self) -> Option { - unsafe { clipboard::get_clipboard_text() } - } - fn clipboard_set(&mut self, data: &str) { - unsafe { clipboard::set_clipboard_text(data) } - } - fn as_any(&mut self) -> &mut dyn std::any::Any { - self - } -} - -struct WindowPayload { - event_handler: Box, } fn get_win_style(is_fullscreen: bool, is_resizable: bool) -> DWORD { @@ -353,31 +257,31 @@ unsafe extern "system" fn win32_wndproc( if display_ptr == 0 { return DefWindowProcW(hwnd, umsg, wparam, lparam); } - let &mut WindowPayload { - ref mut event_handler, - } = &mut *(display_ptr as *mut WindowPayload); + let payload = &mut *(display_ptr as *mut WindowsDisplay); + let event_handler = payload.event_handler.as_mut().unwrap(); match umsg { WM_CLOSE => { let mut quit_requested = false; - tl_display::with(|display| { - // only give user a chance to intervene when sapp_quit() wasn't already called - if !display.display_data.quit_ordered { - // if window should be closed and event handling is enabled, give user code - // a change to intervene via sapp_cancel_quit() - display.display_data.quit_requested = true; - quit_requested = true; - // if user code hasn't intervened, quit the app - if display.display_data.quit_requested { - display.display_data.quit_ordered = true; - } + let mut d = crate::native_display().lock().unwrap(); + // only give user a chance to intervene when sapp_quit() wasn't already called + if !d.quit_ordered { + // if window should be closed and event handling is enabled, give user code + // a change to intervene via sapp_cancel_quit() + d.quit_requested = true; + quit_requested = true; + // if user code hasn't intervened, quit the app + if d.quit_requested { + d.quit_ordered = true; } - if display.display_data.quit_ordered { - PostQuitMessage(0); - } - }); + } + if d.quit_ordered { + PostQuitMessage(0); + } + if quit_requested { + drop(d); event_handler.quit_requested_event(); } return 0; @@ -385,7 +289,7 @@ unsafe extern "system" fn win32_wndproc( WM_SYSCOMMAND => { match wparam & 0xFFF0 { SC_SCREENSAVE | SC_MONITORPOWER => { - if tl_display::with(|d| d.fullscreen) { + if payload.fullscreen { // disable screen saver and blanking in fullscreen mode return 0; } @@ -401,13 +305,13 @@ unsafe extern "system" fn win32_wndproc( return 1; } WM_SIZE => { - if tl_display::with(|d| d.cursor_grabbed) { + if payload.cursor_grabbed { update_clip_rect(hwnd); } let iconified = wparam == SIZE_MINIMIZED; - if iconified != tl_display::with(|d| d.iconified) { - tl_display::with(|d| d.iconified = iconified); + if iconified != payload.iconified { + payload.iconified = iconified; if iconified { event_handler.window_minimized_event(); } else { @@ -416,54 +320,51 @@ unsafe extern "system" fn win32_wndproc( } } WM_SETCURSOR => { - if tl_display::with(|d| d.user_cursor) && LOWORD(lparam as _) == HTCLIENT as _ { - SetCursor(tl_display::with(|d| d.cursor)); + if payload.user_cursor && LOWORD(lparam as _) == HTCLIENT as _ { + SetCursor(payload.cursor); return 1; } } WM_LBUTTONDOWN => { - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_button_down_event(MouseButton::Left, mouse_x, mouse_y); } WM_RBUTTONDOWN => { - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_button_down_event(MouseButton::Right, mouse_x, mouse_y); } WM_MBUTTONDOWN => { - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_button_down_event(MouseButton::Middle, mouse_x, mouse_y); } WM_LBUTTONUP => { - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_button_up_event(MouseButton::Left, mouse_x, mouse_y); } WM_RBUTTONUP => { - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_button_up_event(MouseButton::Right, mouse_x, mouse_y); } WM_MBUTTONUP => { - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_button_up_event(MouseButton::Middle, mouse_x, mouse_y); } WM_MOUSEMOVE => { - tl_display::with(|d| { - d.mouse_x = GET_X_LPARAM(lparam) as f32 * d.mouse_scale; - d.mouse_y = GET_Y_LPARAM(lparam) as f32 * d.mouse_scale; - }); - + payload.mouse_x = GET_X_LPARAM(lparam) as f32 * payload.mouse_scale; + payload.mouse_y = GET_Y_LPARAM(lparam) as f32 * payload.mouse_scale; // mouse enter was not handled by miniquad anyway // if !_sapp.win32_mouse_tracked { // _sapp.win32_mouse_tracked = true; @@ -480,13 +381,13 @@ unsafe extern "system" fn win32_wndproc( // ); // } - let mouse_x = tl_display::with(|d| d.mouse_x); - let mouse_y = tl_display::with(|d| d.mouse_y); + let mouse_x = payload.mouse_x; + let mouse_y = payload.mouse_y; event_handler.mouse_motion_event(mouse_x, mouse_y); } - WM_MOVE if tl_display::with(|d| d.cursor_grabbed) => { + WM_MOVE if payload.cursor_grabbed => { update_clip_rect(hwnd); } @@ -504,18 +405,16 @@ unsafe extern "system" fn win32_wndproc( panic!("failed to retrieve raw input data"); } - let mouse_scale = tl_display::with(|d| d.mouse_scale); + let mouse_scale = payload.mouse_scale; let mut dx = data.data.mouse().lLastX as f32 * mouse_scale; let mut dy = data.data.mouse().lLastY as f32 * mouse_scale; // convert from normalised absolute coordinates if (data.data.mouse().usFlags & MOUSE_MOVE_ABSOLUTE) == MOUSE_MOVE_ABSOLUTE { - let (width, height) = tl_display::with(|d| { - ( - d.display_data.screen_width as f32, - d.display_data.screen_height as f32, - ) - }); + let (width, height) = { + let d = crate::native_display().lock().unwrap(); + (d.screen_width as f32, d.screen_height as f32) + }; dx = dx / 65535.0 * width; dy = dy / 65535.0 * height; @@ -802,6 +701,7 @@ impl WindowsDisplay { /// updates current window and framebuffer size from the window's client rect, /// returns true if size has changed unsafe fn update_dimensions(&mut self, hwnd: HWND) -> bool { + let mut d = crate::native_display().lock().unwrap(); let mut rect: RECT = std::mem::zeroed(); if GetClientRect(hwnd, &mut rect as *mut _ as _) != 0 { @@ -811,16 +711,14 @@ impl WindowsDisplay { // prevent a framebuffer size of 0 when window is minimized let fb_width = ((window_width as f32 * self.content_scale) as i32).max(1); let fb_height = ((window_height as f32 * self.content_scale) as i32).max(1); - if fb_width != self.display_data.screen_width - || fb_height != self.display_data.screen_height - { - self.display_data.screen_width = fb_width; - self.display_data.screen_height = fb_height; + if fb_width != d.screen_width || fb_height != d.screen_height { + d.screen_width = fb_width; + d.screen_height = fb_height; return true; } } else { - self.display_data.screen_width = 1; - self.display_data.screen_height = 1; + d.screen_width = 1; + d.screen_height = 1; } return false; } @@ -851,6 +749,25 @@ impl WindowsDisplay { self.mouse_scale = 1.0 / self.window_scale; } } + + fn process_request(&mut self, request: Request) { + use Request::*; + unsafe { + match request { + SetCursorGrab(grab) => self.set_cursor_grab(grab), + ShowMouse(show) => self.show_mouse(show), + SetMouseCursor(icon) => self.set_mouse_cursor(icon), + SetWindowSize { + new_width, + new_height, + } => self.set_window_size(new_width as _, new_height as _), + SetFullscreen(fullscreen) => self.set_fullscreen(fullscreen), + ShowKeyboard(show) => { + eprintln!("Not implemented for windows") + } + } + } + } } pub fn run(conf: &Conf, f: F) @@ -889,14 +806,22 @@ where show_cursor: true, user_cursor: false, cursor: std::ptr::null_mut(), - display_data: Default::default(), libopengl32, _msg_wnd: msg_wnd, msg_dc, wnd, dc, + event_handler: None, }; + let (tx, rx) = std::sync::mpsc::channel(); + let clipboard = Box::new(clipboard::WindowsClipboard::new()); + crate::set_display(NativeDisplayData { + high_dpi: conf.high_dpi, + dpi_scale: display.window_scale, + ..NativeDisplayData::new(conf.window_width, conf.window_height, tx, clipboard) + }); + display.update_dimensions(wnd); display.init_dpi(conf.high_dpi); @@ -909,20 +834,19 @@ where super::gl::load_gl_funcs(|proc| display.get_proc_address(proc)); - tl_display::set_display(display); - - let event_handler = f(); + display.event_handler = Some(f()); - let mut p = WindowPayload { event_handler }; - // well, technically this is UB and we are suppose to use *mut WindowPayload instead of &mut WindowPayload forever from now on... - // so if there going to be some weird bugs someday in the future - check this out! #[cfg(target_arch = "x86_64")] - SetWindowLongPtrA(wnd, GWLP_USERDATA, &mut p as *mut _ as isize); + SetWindowLongPtrA(wnd, GWLP_USERDATA, &mut display as *mut _ as isize); #[cfg(target_arch = "i686")] - SetWindowLong(wnd, GWLP_USERDATA, &mut p as *mut _ as isize); + SetWindowLong(wnd, GWLP_USERDATA, &mut display as *mut _ as isize); let mut done = false; - while !(done || tl_display::with(|d| d.display_data.quit_ordered)) { + while !(done || crate::native_display().lock().unwrap().quit_ordered) { + while let Ok(request) = rx.try_recv() { + display.process_request(request); + } + let mut msg: MSG = std::mem::zeroed(); while PeekMessageW(&mut msg as *mut _ as _, NULL as _, 0, 0, PM_REMOVE) != 0 { if WM_QUIT == msg.message { @@ -934,26 +858,28 @@ where } } - p.event_handler.update(); - p.event_handler.draw(); - - SwapBuffers(tl_display::with(|d| d.dc)); - - if tl_display::with(|d| d.update_dimensions(wnd)) { - let width = tl_display::with(|d| d.display_data.screen_width as f32); - let height = tl_display::with(|d| d.display_data.screen_height as f32); - p.event_handler.resize_event(width, height); + display.event_handler.as_mut().unwrap().update(); + display.event_handler.as_mut().unwrap().draw(); + + SwapBuffers(display.dc); + + if display.update_dimensions(wnd) { + let d = crate::native_display().lock().unwrap(); + let width = d.screen_width as f32; + let height = d.screen_height as f32; + drop(d); + display + .event_handler + .as_mut() + .unwrap() + .resize_event(width, height); + } + if crate::native_display().lock().unwrap().quit_requested { + PostMessageW(display.wnd, WM_CLOSE, 0, 0); } - tl_display::with(|d| { - if d.display_data.quit_requested { - PostMessageW(d.wnd, WM_CLOSE, 0, 0); - } - }); } - tl_display::with(|d| { - (d.libopengl32.wglDeleteContext)(gl_ctx); - }); + (display.libopengl32.wglDeleteContext)(gl_ctx); DestroyWindow(wnd); } } diff --git a/src/native/windows/clipboard.rs b/src/native/windows/clipboard.rs index 5839671c..630ac7dc 100644 --- a/src/native/windows/clipboard.rs +++ b/src/native/windows/clipboard.rs @@ -82,11 +82,21 @@ unsafe fn set_raw_clipboard(data: *const u8, len: usize) { SetClipboardData(CF_UNICODETEXT, alloc_handle); } -pub unsafe fn set_clipboard_text(text: &str) { - let text_w = format!("{}\0", text).encode_utf16().collect::>(); - set_raw_clipboard(text_w.as_ptr() as _, text_w.len() * 2); +pub struct WindowsClipboard {} +impl WindowsClipboard { + pub fn new() -> WindowsClipboard { + WindowsClipboard {} + } } +impl crate::native::Clipboard for WindowsClipboard { + fn get(&mut self) -> Option { + unsafe { get_raw_clipboard().map(|data| String::from_utf16_lossy(&data)) } + } -pub unsafe fn get_clipboard_text() -> Option { - get_raw_clipboard().map(|data| String::from_utf16_lossy(&data)) + fn set(&mut self, data: &str) { + unsafe { + let text_w = format!("{}\0", data).encode_utf16().collect::>(); + set_raw_clipboard(text_w.as_ptr() as _, text_w.len() * 2); + } + } }