diff --git a/examples/open_window.rs b/examples/open_window.rs index 8b344b5..1371354 100644 --- a/examples/open_window.rs +++ b/examples/open_window.rs @@ -1,6 +1,4 @@ -use std::sync::mpsc; - -use baseview::Event; +use baseview::{Event, Window, WindowHandler}; fn main() { let window_open_options = baseview::WindowOpenOptions { @@ -10,25 +8,21 @@ fn main() { parent: baseview::Parent::None, }; - let (_app_message_tx, app_message_rx) = mpsc::channel::<()>(); - - // Send _app_message_tx to a separate thread, then send messages to the GUI thread. - - let _ = baseview::Window::::open(window_open_options, app_message_rx); + let _handle = Window::open::(window_open_options); } + struct MyProgram {} -impl baseview::AppWindow for MyProgram { - type AppMessage = (); +impl WindowHandler for MyProgram { + type Message = (); - fn build(_window_handle: baseview::RawWindow, window_info: &baseview::WindowInfo) -> Self { - println!("Window info: {:?}", window_info); + fn build(window: &mut Window) -> Self { Self {} } - fn draw(&mut self) {} + fn draw(&mut self, window: &mut Window) {} - fn on_event(&mut self, event: Event) { + fn on_event(&mut self, window: &mut Window, event: Event) { match event { Event::CursorMotion(x, y) => { println!("Cursor moved, x: {}, y: {}", x, y); @@ -69,5 +63,5 @@ impl baseview::AppWindow for MyProgram { } } - fn on_app_message(&mut self, _message: Self::AppMessage) {} + fn on_message(&mut self, window: &mut Window, _message: Self::Message) {} } diff --git a/src/lib.rs b/src/lib.rs index 406bb91..c4ed84f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,24 +33,12 @@ pub struct WindowOpenOptions<'a> { pub parent: Parent, } -pub trait AppWindow { - type AppMessage; +pub trait WindowHandler { + type Message; - fn build(window_handle: RawWindow, window_info: &WindowInfo) -> Self; + fn build(window: &mut Window) -> Self; - fn draw(&mut self); - fn on_event(&mut self, event: Event); - fn on_app_message(&mut self, message: Self::AppMessage); -} - -/// A wrapper for a `RawWindowHandle`. Some context creators expect an `&impl HasRawWindowHandle`. -#[derive(Debug, Copy, Clone)] -pub struct RawWindow { - pub raw_window_handle: raw_window_handle::RawWindowHandle, -} - -unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow { - fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { - self.raw_window_handle - } + fn draw(&mut self, window: &mut Window); + fn on_event(&mut self, window: &mut Window, event: Event); + fn on_message(&mut self, window: &mut Window, message: Self::Message); } diff --git a/src/macos/window.rs b/src/macos/window.rs index ebc67d1..40c54b4 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -1,27 +1,24 @@ use std::ffi::c_void; -use std::sync::mpsc; use cocoa::appkit::{ NSApp, NSApplication, NSApplicationActivateIgnoringOtherApps, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSRunningApplication, NSView, NSWindow, NSWindowStyleMask, }; -use cocoa::base::{nil, NO}; +use cocoa::base::{id, nil, NO}; use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString}; use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; -use crate::{ - AppWindow, Event, MouseButtonID, MouseScroll, RawWindow, WindowInfo, WindowOpenOptions, -}; +use crate::{MouseScroll, WindowHandler, WindowOpenOptions}; -pub struct Window { - app_window: A, - app_message_rx: mpsc::Receiver, +pub struct Window { + ns_window: id, + ns_view: id, } -impl Window { - pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver) -> Self { +impl Window { + pub fn open(options: WindowOpenOptions) -> WindowHandle { unsafe { let _pool = NSAutoreleasePool::new(nil); @@ -33,7 +30,7 @@ impl Window { NSSize::new(options.width as f64, options.height as f64), ); - let window = NSWindow::alloc(nil) + let ns_window = NSWindow::alloc(nil) .initWithContentRect_styleMask_backing_defer_( rect, NSWindowStyleMask::NSTitledWindowMask, @@ -41,37 +38,34 @@ impl Window { NO, ) .autorelease(); - window.center(); - window.setTitle_(NSString::alloc(nil).init_str(options.title)); - window.makeKeyAndOrderFront_(nil); + ns_window.center(); + ns_window.setTitle_(NSString::alloc(nil).init_str(options.title)); + ns_window.makeKeyAndOrderFront_(nil); - let view = NSView::alloc(nil).init(); - window.setContentView_(view); + let ns_view = NSView::alloc(nil).init(); + ns_window.setContentView_(ns_view); - let raw_window = RawWindow { - raw_window_handle: RawWindowHandle::MacOS(MacOSHandle { - ns_window: window as *mut c_void, - ns_view: app as *mut c_void, - ..raw_window_handle::macos::MacOSHandle::empty() - }), - }; + let mut window = Window { ns_window, ns_view }; - let window_info = WindowInfo { - width: options.width as u32, - height: options.height as u32, - scale: 1.0, - }; - - let app_window = A::build(raw_window, &window_info); + let handler = H::build(&mut window); let current_app = NSRunningApplication::currentApplication(nil); current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps); app.run(); - Window { - app_window, - app_message_rx, - } + WindowHandle } } } + +unsafe impl HasRawWindowHandle for Window { + fn raw_window_handle(&self) -> RawWindowHandle { + RawWindowHandle::MacOS(MacOSHandle { + ns_window: self.ns_window as *mut c_void, + ns_view: self.ns_view as *mut c_void, + ..MacOSHandle::empty() + }) + } +} + +pub struct WindowHandle; diff --git a/src/win/window.rs b/src/win/window.rs index 0489760..e3b4cfa 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -6,18 +6,19 @@ use winapi::um::winuser::{ AdjustWindowRectEx, CreateWindowExA, DefWindowProcA, DestroyWindow, DispatchMessageA, GetMessageA, GetWindowLongPtrA, MessageBoxA, PostMessageA, RegisterClassA, SetTimer, SetWindowLongPtrA, TranslateMessage, UnregisterClassA, CS_OWNDC, GWLP_USERDATA, MB_ICONERROR, - MB_OK, MB_TOPMOST, MSG, WM_CREATE, WM_MOUSEMOVE, WM_PAINT, WM_SHOWWINDOW, WM_TIMER, WNDCLASSA, - WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, - WS_SIZEBOX, WS_VISIBLE, + MB_OK, MB_TOPMOST, MSG, WM_CLOSE, WM_CREATE, WM_MOUSEMOVE, WM_PAINT, WM_SHOWWINDOW, WM_TIMER, + WNDCLASSA, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, + WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE, }; +use std::cell::RefCell; use std::ffi::c_void; use std::ptr::null_mut; -use std::sync::mpsc; use std::rc::Rc; -use std::cell::RefCell; -use crate::{AppWindow, Event, Parent::WithParent, RawWindow, WindowInfo, WindowOpenOptions}; +use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle}; + +use crate::{Event, Parent::WithParent, WindowHandler, WindowInfo, WindowOpenOptions}; unsafe fn message_box(title: &str, msg: &str) { let title = (title.to_owned() + "\0").as_ptr() as *const i8; @@ -46,14 +47,14 @@ unsafe fn generate_guid() -> String { const WIN_FRAME_TIMER: usize = 4242; -unsafe fn handle_timer(win: &RefCell>, timer_id: usize) { +unsafe fn handle_timer(window_state: &RefCell>, timer_id: usize) { match timer_id { WIN_FRAME_TIMER => {} _ => (), } } -unsafe extern "system" fn wnd_proc( +unsafe extern "system" fn wnd_proc( hwnd: HWND, msg: UINT, wparam: WPARAM, @@ -66,22 +67,33 @@ unsafe extern "system" fn wnd_proc( let win_ptr = GetWindowLongPtrA(hwnd, GWLP_USERDATA) as *const c_void; if !win_ptr.is_null() { - let win = &*(win_ptr as *const RefCell>); + let window_state = &*(win_ptr as *const RefCell>); + let mut window = Window { hwnd }; match msg { WM_MOUSEMOVE => { let x = (lparam & 0xFFFF) as i32; let y = ((lparam >> 16) & 0xFFFF) as i32; - win.borrow_mut().handle_mouse_motion(x, y); + window_state + .borrow_mut() + .handler + .on_event(&mut window, Event::CursorMotion(x, y)); return 0; } WM_TIMER => { - handle_timer(&win, wparam); + handle_timer(&window_state, wparam); return 0; } WM_PAINT => { return 0; } + WM_CLOSE => { + window_state + .borrow_mut() + .handler + .on_event(&mut window, Event::WillClose); + return DefWindowProcA(hwnd, msg, wparam, lparam); + } _ => {} } } @@ -89,13 +101,13 @@ unsafe extern "system" fn wnd_proc( return DefWindowProcA(hwnd, msg, wparam, lparam); } -unsafe fn register_wnd_class() -> ATOM { +unsafe fn register_wnd_class() -> ATOM { // We generate a unique name for the new window class to prevent name collisions let class_name = format!("Baseview-{}", generate_guid()).as_ptr() as *const i8; let wnd_class = WNDCLASSA { style: CS_OWNDC, - lpfnWndProc: Some(wnd_proc::), + lpfnWndProc: Some(wnd_proc::), hInstance: null_mut(), lpszClassName: class_name, cbClsExtra: 0, @@ -113,20 +125,22 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) { UnregisterClassA(wnd_class as _, null_mut()); } -pub struct Window { - pub(crate) hwnd: HWND, +struct WindowState { window_class: ATOM, - app_window: A, - app_message_rx: mpsc::Receiver, scaling: Option, // DPI scale, 96.0 is "default". + handler: H, } -impl Window { - pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver) { +pub struct Window { + hwnd: HWND, +} + +impl Window { + pub fn open(options: WindowOpenOptions) -> WindowHandle { unsafe { let title = (options.title.to_owned() + "\0").as_ptr() as *const i8; - let window_class = register_wnd_class::(); + let window_class = register_wnd_class::(); // todo: manage error ^ let mut flags = WS_POPUPWINDOW @@ -170,28 +184,15 @@ impl Window { ); // todo: manage error ^ - let mut windows_handle = raw_window_handle::windows::WindowsHandle::empty(); - windows_handle.hwnd = hwnd as *mut std::ffi::c_void; + let mut window = Window { hwnd }; - let raw_window = RawWindow { - raw_window_handle: raw_window_handle::RawWindowHandle::Windows(windows_handle), - }; + let handler = H::build(&mut window); - let window_info = WindowInfo { - width: options.width as u32, - height: options.height as u32, - scale: 1.0, - }; - - let app_window = A::build(raw_window, &window_info); - - let window = Window { - hwnd, + let window_state = Rc::new(RefCell::new(WindowState { window_class, - app_window, - app_message_rx, scaling: None, - }; + handler, + })); let win = Rc::new(RefCell::new(window)); @@ -212,19 +213,18 @@ impl Window { } } } - } - pub fn close(&mut self) { - self.app_window.on_event(Event::WillClose); - - // todo: see https://github.com/wrl/rutabaga/blob/f30ff67e157375cafdbafe5fb549f1790443a3a8/src/platform/win/window.c#L402 - unsafe { - DestroyWindow(self.hwnd); - unregister_wnd_class(self.window_class); - } - } - - pub(crate) fn handle_mouse_motion(&mut self, x: i32, y: i32) { - self.app_window.on_event(Event::CursorMotion(x, y)); + WindowHandle } } + +unsafe impl HasRawWindowHandle for Window { + fn raw_window_handle(&self) -> RawWindowHandle { + RawWindowHandle::Windows(WindowsHandle { + hwnd: self.hwnd as *mut std::ffi::c_void, + ..WindowsHandle::empty() + }) + } +} + +pub struct WindowHandle; diff --git a/src/x11/window.rs b/src/x11/window.rs index 3f23726..2974de6 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -1,23 +1,19 @@ use std::ffi::CStr; use std::os::raw::{c_ulong, c_void}; -use std::sync::mpsc; use super::XcbConnection; -use crate::{ - AppWindow, Event, MouseButtonID, MouseScroll, Parent, RawWindow, WindowInfo, WindowOpenOptions, -}; +use crate::{Event, MouseButtonID, MouseScroll, Parent, WindowHandler, WindowOpenOptions}; -use raw_window_handle::RawWindowHandle; +use raw_window_handle::{unix::XlibHandle, HasRawWindowHandle, RawWindowHandle}; -pub struct Window { - scaling: f64, +pub struct Window { xcb_connection: XcbConnection, - app_window: A, - app_message_rx: mpsc::Receiver, + window_id: u32, + scaling: f64, } -impl Window { - pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver) -> Self { +impl Window { + pub fn open(options: WindowOpenOptions) -> WindowHandle { // Convert the parent to a X11 window ID if we're given one let parent = match options.parent { Parent::None => None, @@ -93,131 +89,130 @@ impl Window { xcb_connection.conn.flush(); - let raw_handle = RawWindowHandle::Xlib(raw_window_handle::unix::XlibHandle { - window: window_id as c_ulong, - display: xcb_connection.conn.get_raw_dpy() as *mut c_void, - ..raw_window_handle::unix::XlibHandle::empty() - }); - - let raw_window = RawWindow { - raw_window_handle: raw_handle, - }; - let scaling = get_scaling_xft(&xcb_connection) .or(get_scaling_screen_dimensions(&xcb_connection)) .unwrap_or(1.0); - let window_info = WindowInfo { - width: options.width as u32, - height: options.height as u32, - scale: scaling, - }; - - let app_window = A::build(raw_window, &window_info); - - let mut x11_window = Self { - scaling, + let mut window = Self { xcb_connection, - app_window, - app_message_rx, + window_id, + scaling, }; - x11_window.run_event_loop(); + let mut handler = H::build(&mut window); - x11_window + run_event_loop(&mut window, &mut handler); + + WindowHandle } +} - // Event loop - fn run_event_loop(&mut self) { - loop { - // somehow poll self.app_message_rx for messages at the same time +unsafe impl HasRawWindowHandle for Window { + fn raw_window_handle(&self) -> RawWindowHandle { + RawWindowHandle::Xlib(XlibHandle { + window: self.window_id as c_ulong, + display: self.xcb_connection.conn.get_raw_dpy() as *mut c_void, + ..raw_window_handle::unix::XlibHandle::empty() + }) + } +} - let ev = self.xcb_connection.conn.wait_for_event(); - if let Some(event) = ev { - let event_type = event.response_type() & !0x80; +pub struct WindowHandle; - // For all of the keyboard and mouse events, you can fetch - // `x`, `y`, `detail`, and `state`. - // - `x` and `y` are the position inside the window where the cursor currently is - // when the event happened. - // - `detail` will tell you which keycode was pressed/released (for keyboard events) - // or which mouse button was pressed/released (for mouse events). - // For mouse events, here's what the value means (at least on my current mouse): - // 1 = left mouse button - // 2 = middle mouse button (scroll wheel) - // 3 = right mouse button - // 4 = scroll wheel up - // 5 = scroll wheel down - // 8 = lower side button ("back" button) - // 9 = upper side button ("forward" button) - // Note that you *will* get a "button released" event for even the scroll wheel - // events, which you can probably ignore. - // - `state` will tell you the state of the main three mouse buttons and some of - // the keyboard modifier keys at the time of the event. - // http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445 +// Event loop +fn run_event_loop(window: &mut Window, handler: &mut H) { + loop { + let ev = window.xcb_connection.conn.wait_for_event(); + if let Some(event) = ev { + let event_type = event.response_type() & !0x80; - match event_type { - xcb::EXPOSE => { - self.app_window.draw(); + // For all of the keyboard and mouse events, you can fetch + // `x`, `y`, `detail`, and `state`. + // - `x` and `y` are the position inside the window where the cursor currently is + // when the event happened. + // - `detail` will tell you which keycode was pressed/released (for keyboard events) + // or which mouse button was pressed/released (for mouse events). + // For mouse events, here's what the value means (at least on my current mouse): + // 1 = left mouse button + // 2 = middle mouse button (scroll wheel) + // 3 = right mouse button + // 4 = scroll wheel up + // 5 = scroll wheel down + // 8 = lower side button ("back" button) + // 9 = upper side button ("forward" button) + // Note that you *will* get a "button released" event for even the scroll wheel + // events, which you can probably ignore. + // - `state` will tell you the state of the main three mouse buttons and some of + // the keyboard modifier keys at the time of the event. + // http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445 + + match event_type { + xcb::EXPOSE => { + handler.draw(window); + } + xcb::MOTION_NOTIFY => { + let event = unsafe { xcb::cast_event::(&event) }; + let detail = event.detail(); + + if detail != 4 && detail != 5 { + handler.on_event( + window, + Event::CursorMotion(event.event_x() as i32, event.event_y() as i32), + ); } - xcb::MOTION_NOTIFY => { - let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); + } + xcb::BUTTON_PRESS => { + let event = unsafe { xcb::cast_event::(&event) }; + let detail = event.detail(); - if detail != 4 && detail != 5 { - self.app_window.on_event(Event::CursorMotion( - event.event_x() as i32, - event.event_y() as i32, - )); - } - } - xcb::BUTTON_PRESS => { - let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); - - match detail { - 4 => { - self.app_window.on_event(Event::MouseScroll(MouseScroll { + match detail { + 4 => { + handler.on_event( + window, + Event::MouseScroll(MouseScroll { x_delta: 0.0, y_delta: 1.0, - })); - } - 5 => { - self.app_window.on_event(Event::MouseScroll(MouseScroll { + }), + ); + } + 5 => { + handler.on_event( + window, + Event::MouseScroll(MouseScroll { x_delta: 0.0, y_delta: -1.0, - })); - } - detail => { - let button_id = mouse_id(detail); - self.app_window.on_event(Event::MouseDown(button_id)); - } + }), + ); } - } - xcb::BUTTON_RELEASE => { - let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); - - if detail != 4 && detail != 5 { + detail => { let button_id = mouse_id(detail); - self.app_window.on_event(Event::MouseUp(button_id)); + handler.on_event(window, Event::MouseDown(button_id)); } } - xcb::KEY_PRESS => { - let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); + } + xcb::BUTTON_RELEASE => { + let event = unsafe { xcb::cast_event::(&event) }; + let detail = event.detail(); - self.app_window.on_event(Event::KeyDown(detail)); + if detail != 4 && detail != 5 { + let button_id = mouse_id(detail); + handler.on_event(window, Event::MouseUp(button_id)); } - xcb::KEY_RELEASE => { - let event = unsafe { xcb::cast_event::(&event) }; - let detail = event.detail(); + } + xcb::KEY_PRESS => { + let event = unsafe { xcb::cast_event::(&event) }; + let detail = event.detail(); - self.app_window.on_event(Event::KeyUp(detail)); - } - _ => { - println!("Unhandled event type: {:?}", event_type); - } + handler.on_event(window, Event::KeyDown(detail)); + } + xcb::KEY_RELEASE => { + let event = unsafe { xcb::cast_event::(&event) }; + let detail = event.detail(); + + handler.on_event(window, Event::KeyUp(detail)); + } + _ => { + println!("Unhandled event type: {:?}", event_type); } } }