Skip to content

Commit

Permalink
Merge branch 'master' into robbert-vdh/fix-windows-resizing-event
Browse files Browse the repository at this point in the history
  • Loading branch information
glowcoil authored Mar 26, 2024
2 parents 1d25b15 + 65d9704 commit aeb3a2b
Show file tree
Hide file tree
Showing 11 changed files with 687 additions and 427 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ keyboard-types = { version = "0.6.1", default-features = false }
raw-window-handle = "0.5"

[target.'cfg(target_os="linux")'.dependencies]
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
x11 = { version = "2.18", features = ["xlib", "xcursor"] }
xcb-util = { version = "0.3", features = ["icccm"] }
x11rb = { version = "0.13.0", features = ["cursor", "resource_manager", "allow-unsafe-code"] }
x11 = { version = "2.21", features = ["xlib", "xcursor", "xlib_xcb"] }
nix = "0.22.0"

[target.'cfg(target_os="windows")'.dependencies]
Expand All @@ -40,3 +39,4 @@ uuid = { version = "0.8", features = ["v4"] }

[dev-dependencies]
rtrb = "0.2"
softbuffer = "0.3.4"
141 changes: 141 additions & 0 deletions examples/open_parented.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use baseview::{
Event, EventStatus, PhySize, Window, WindowEvent, WindowHandle, WindowHandler,
WindowScalePolicy,
};
use std::num::NonZeroU32;

struct ParentWindowHandler {
ctx: softbuffer::Context,
surface: softbuffer::Surface,
current_size: PhySize,
damaged: bool,

child_window: Option<WindowHandle>,
}

impl ParentWindowHandler {
pub fn new(window: &mut Window) -> Self {
let ctx = unsafe { softbuffer::Context::new(window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&ctx, window) }.unwrap();
surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap();

let window_open_options = baseview::WindowOpenOptions {
title: "baseview child".into(),
size: baseview::Size::new(256.0, 256.0),
scale: WindowScalePolicy::SystemScaleFactor,

// TODO: Add an example that uses the OpenGL context
#[cfg(feature = "opengl")]
gl_config: None,
};
let child_window =
Window::open_parented(window, window_open_options, ChildWindowHandler::new);

// TODO: no way to query physical size initially?
Self {
ctx,
surface,
current_size: PhySize::new(512, 512),
damaged: true,
child_window: Some(child_window),
}
}
}

impl WindowHandler for ParentWindowHandler {
fn on_frame(&mut self, _window: &mut Window) {
let mut buf = self.surface.buffer_mut().unwrap();
if self.damaged {
buf.fill(0xFFAAAAAA);
self.damaged = false;
}
buf.present().unwrap();
}

fn on_event(&mut self, _window: &mut Window, event: Event) -> EventStatus {
match event {
Event::Window(WindowEvent::Resized(info)) => {
println!("Parent Resized: {:?}", info);
let new_size = info.physical_size();
self.current_size = new_size;

if let (Some(width), Some(height)) =
(NonZeroU32::new(new_size.width), NonZeroU32::new(new_size.height))
{
self.surface.resize(width, height).unwrap();
self.damaged = true;
}
}
Event::Mouse(e) => println!("Parent Mouse event: {:?}", e),
Event::Keyboard(e) => println!("Parent Keyboard event: {:?}", e),
Event::Window(e) => println!("Parent Window event: {:?}", e),
}

EventStatus::Captured
}
}

struct ChildWindowHandler {
ctx: softbuffer::Context,
surface: softbuffer::Surface,
current_size: PhySize,
damaged: bool,
}

impl ChildWindowHandler {
pub fn new(window: &mut Window) -> Self {
let ctx = unsafe { softbuffer::Context::new(window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&ctx, window) }.unwrap();
surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap();

// TODO: no way to query physical size initially?
Self { ctx, surface, current_size: PhySize::new(256, 256), damaged: true }
}
}

impl WindowHandler for ChildWindowHandler {
fn on_frame(&mut self, _window: &mut Window) {
let mut buf = self.surface.buffer_mut().unwrap();
if self.damaged {
buf.fill(0xFFAA0000);
self.damaged = false;
}
buf.present().unwrap();
}

fn on_event(&mut self, _window: &mut Window, event: Event) -> EventStatus {
match event {
Event::Window(WindowEvent::Resized(info)) => {
println!("Child Resized: {:?}", info);
let new_size = info.physical_size();
self.current_size = new_size;

if let (Some(width), Some(height)) =
(NonZeroU32::new(new_size.width), NonZeroU32::new(new_size.height))
{
self.surface.resize(width, height).unwrap();
self.damaged = true;
}
}
Event::Mouse(e) => println!("Child Mouse event: {:?}", e),
Event::Keyboard(e) => println!("Child Keyboard event: {:?}", e),
Event::Window(e) => println!("Child Window event: {:?}", e),
}

EventStatus::Captured
}
}

fn main() {
let window_open_options = baseview::WindowOpenOptions {
title: "baseview".into(),
size: baseview::Size::new(512.0, 512.0),
scale: WindowScalePolicy::SystemScaleFactor,

// TODO: Add an example that uses the OpenGL context
#[cfg(feature = "opengl")]
gl_config: None,
};

Window::open_blocking(window_open_options, ParentWindowHandler::new);
}
65 changes: 49 additions & 16 deletions examples/open_window.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::num::NonZeroU32;
use std::time::Duration;

use rtrb::{Consumer, RingBuffer};

#[cfg(target_os = "macos")]
use baseview::copy_to_clipboard;
use baseview::{Event, EventStatus, MouseEvent, Window, WindowHandler, WindowScalePolicy};
use baseview::{
Event, EventStatus, MouseEvent, PhySize, Window, WindowEvent, WindowHandler, WindowScalePolicy,
};

#[derive(Debug, Clone)]
enum Message {
Expand All @@ -13,32 +16,48 @@ enum Message {

struct OpenWindowExample {
rx: Consumer<Message>,

ctx: softbuffer::Context,
surface: softbuffer::Surface,
current_size: PhySize,
damaged: bool,
}

impl WindowHandler for OpenWindowExample {
fn on_frame(&mut self, _window: &mut Window) {
let mut buf = self.surface.buffer_mut().unwrap();
if self.damaged {
buf.fill(0xFFAAAAAA);
self.damaged = false;
}
buf.present().unwrap();

while let Ok(message) = self.rx.pop() {
println!("Message: {:?}", message);
}
}

fn on_event(&mut self, _window: &mut Window, event: Event) -> EventStatus {
match event {
Event::Mouse(e) => {
println!("Mouse event: {:?}", e);

#[cfg(target_os = "macos")]
match e {
MouseEvent::ButtonPressed { .. } => {
copy_to_clipboard(&"This is a test!")
}
_ => (),
match &event {
#[cfg(target_os = "macos")]
Event::Mouse(MouseEvent::ButtonPressed { .. }) => copy_to_clipboard(&"This is a test!"),
Event::Window(WindowEvent::Resized(info)) => {
println!("Resized: {:?}", info);
let new_size = info.physical_size();
self.current_size = new_size;

if let (Some(width), Some(height)) =
(NonZeroU32::new(new_size.width), NonZeroU32::new(new_size.height))
{
self.surface.resize(width, height).unwrap();
self.damaged = true;
}
}
Event::Keyboard(e) => println!("Keyboard event: {:?}", e),
Event::Window(e) => println!("Window event: {:?}", e),
_ => {}
}

log_event(&event);

EventStatus::Captured
}
}
Expand All @@ -56,13 +75,27 @@ fn main() {

let (mut tx, rx) = RingBuffer::new(128);

::std::thread::spawn(move || loop {
::std::thread::sleep(Duration::from_secs(5));
std::thread::spawn(move || loop {
std::thread::sleep(Duration::from_secs(5));

if let Err(_) = tx.push(Message::Hello) {
println!("Failed sending message");
}
});

Window::open_blocking(window_open_options, |_| OpenWindowExample { rx });
Window::open_blocking(window_open_options, |window| {
let ctx = unsafe { softbuffer::Context::new(window) }.unwrap();
let mut surface = unsafe { softbuffer::Surface::new(&ctx, window) }.unwrap();
surface.resize(NonZeroU32::new(512).unwrap(), NonZeroU32::new(512).unwrap()).unwrap();

OpenWindowExample { ctx, surface, rx, current_size: PhySize::new(512, 512), damaged: true }
});
}

fn log_event(event: &Event) {
match event {
Event::Mouse(e) => println!("Mouse event: {:?}", e),
Event::Keyboard(e) => println!("Keyboard event: {:?}", e),
Event::Window(e) => println!("Window event: {:?}", e),
}
}
79 changes: 79 additions & 0 deletions src/macos/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ use super::{
/// Name of the field used to store the `WindowState` pointer.
pub(super) const BASEVIEW_STATE_IVAR: &str = "baseview_state";

#[link(name = "AppKit", kind = "framework")]
extern "C" {
static NSWindowDidBecomeKeyNotification: id;
static NSWindowDidResignKeyNotification: id;
}

macro_rules! add_simple_mouse_class_method {
($class:ident, $sel:ident, $event:expr) => {
#[allow(non_snake_case)]
Expand Down Expand Up @@ -94,6 +100,18 @@ macro_rules! add_simple_keyboard_class_method {
};
}

unsafe fn register_notification(observer: id, notification_name: id, object: id) {
let notification_center: id = msg_send![class!(NSNotificationCenter), defaultCenter];

let _: () = msg_send![
notification_center,
addObserver:observer
selector:sel!(handleNotification:)
name:notification_name
object:object
];
}

pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id {
let class = create_view_class();

Expand All @@ -103,6 +121,9 @@ pub(super) unsafe fn create_view(window_options: &WindowOpenOptions) -> id {

view.initWithFrame_(NSRect::new(NSPoint::new(0., 0.), NSSize::new(size.width, size.height)));

register_notification(view, NSWindowDidBecomeKeyNotification, nil);
register_notification(view, NSWindowDidResignKeyNotification, nil);

let _: id = msg_send![
view,
registerForDraggedTypes: NSArray::arrayWithObjects(nil, &[NSFilenamesPboardType])
Expand All @@ -124,6 +145,14 @@ unsafe fn create_view_class() -> &'static Class {
sel!(acceptsFirstResponder),
property_yes as extern "C" fn(&Object, Sel) -> BOOL,
);
class.add_method(
sel!(becomeFirstResponder),
become_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
);
class.add_method(
sel!(resignFirstResponder),
resign_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
);
class.add_method(sel!(isFlipped), property_yes as extern "C" fn(&Object, Sel) -> BOOL);
class.add_method(
sel!(preservesContentInLiveResize),
Expand Down Expand Up @@ -177,6 +206,10 @@ unsafe fn create_view_class() -> &'static Class {
dragging_updated as extern "C" fn(&Object, Sel, id) -> NSUInteger,
);
class.add_method(sel!(draggingExited:), dragging_exited as extern "C" fn(&Object, Sel, id));
class.add_method(
sel!(handleNotification:),
handle_notification as extern "C" fn(&Object, Sel, id),
);

add_mouse_button_class_method!(class, mouseDown, ButtonPressed, MouseButton::Left);
add_mouse_button_class_method!(class, mouseUp, ButtonReleased, MouseButton::Left);
Expand Down Expand Up @@ -208,6 +241,25 @@ extern "C" fn accepts_first_mouse(_this: &Object, _sel: Sel, _event: id) -> BOOL
YES
}

extern "C" fn become_first_responder(this: &Object, _sel: Sel) -> BOOL {
let state = unsafe { WindowState::from_view(this) };
let is_key_window = unsafe {
let window: id = msg_send![this, window];
let is_key_window: BOOL = msg_send![window, isKeyWindow];
is_key_window == YES
};
if is_key_window {
state.trigger_event(Event::Window(WindowEvent::Focused));
}
YES
}

extern "C" fn resign_first_responder(this: &Object, _sel: Sel) -> BOOL {
let state = unsafe { WindowState::from_view(this) };
state.trigger_event(Event::Window(WindowEvent::Unfocused));
YES
}

extern "C" fn window_should_close(this: &Object, _: Sel, _sender: id) -> BOOL {
let state = unsafe { WindowState::from_view(this) };

Expand Down Expand Up @@ -473,3 +525,30 @@ extern "C" fn dragging_exited(this: &Object, _sel: Sel, _sender: id) {

on_event(&state, MouseEvent::DragLeft);
}

extern "C" fn handle_notification(this: &Object, _cmd: Sel, notification: id) {
unsafe {
let state = WindowState::from_view(this);

// The subject of the notication, in this case an NSWindow object.
let notification_object: id = msg_send![notification, object];

// The NSWindow object associated with our NSView.
let window: id = msg_send![this, window];

let first_responder: id = msg_send![window, firstResponder];

// Only trigger focus events if the NSWindow that's being notified about is our window,
// and if the window's first responder is our NSView.
// If the first responder isn't our NSView, the focus events will instead be triggered
// by the becomeFirstResponder and resignFirstResponder methods on the NSView itself.
if notification_object == window && first_responder == this as *const Object as id {
let is_key_window: BOOL = msg_send![window, isKeyWindow];
state.trigger_event(Event::Window(if is_key_window == YES {
WindowEvent::Focused
} else {
WindowEvent::Unfocused
}));
}
}
}
Loading

0 comments on commit aeb3a2b

Please sign in to comment.