Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,12 @@ pub enum ScrollDelta {
pub enum MouseEvent {
/// The mouse cursor was moved
CursorMoved {
/// The logical coordinates of the mouse position
/// The logical coordinates of the mouse position (window-relative)
position: Point,
/// The logical coordinates of the mouse position in screen space (absolute)
/// This remains constant even when the window resizes, making it suitable
/// for drag operations that resize the window.
screen_position: Point,
/// The modifiers that were held down just before the event.
modifiers: Modifiers,
},
Expand Down Expand Up @@ -79,17 +83,21 @@ pub enum MouseEvent {
CursorLeft,

DragEntered {
/// The logical coordinates of the mouse position
/// The logical coordinates of the mouse position (window-relative)
position: Point,
/// The logical coordinates of the mouse position in screen space (absolute)
screen_position: Point,
/// The modifiers that were held down just before the event.
modifiers: Modifiers,
/// Data being dragged
data: DropData,
},

DragMoved {
/// The logical coordinates of the mouse position
/// The logical coordinates of the mouse position (window-relative)
position: Point,
/// The logical coordinates of the mouse position in screen space (absolute)
screen_position: Point,
/// The modifiers that were held down just before the event.
modifiers: Modifiers,
/// Data being dragged
Expand All @@ -99,8 +107,10 @@ pub enum MouseEvent {
DragLeft,

DragDropped {
/// The logical coordinates of the mouse position
/// The logical coordinates of the mouse position (window-relative)
position: Point,
/// The logical coordinates of the mouse position in screen space (absolute)
screen_position: Point,
/// The modifiers that were held down just before the event.
modifiers: Modifiers,
/// Data being dragged
Expand Down
53 changes: 53 additions & 0 deletions src/macos/cursor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::MouseCursor;
use cocoa::base::id;
use objc::{class, msg_send, sel, sel_impl};

pub fn mouse_cursor_to_nscursor(cursor: MouseCursor) -> id {
unsafe {
let nscursor_class = class!(NSCursor);
match cursor {
MouseCursor::Default => msg_send![nscursor_class, arrowCursor],
MouseCursor::Hand => msg_send![nscursor_class, pointingHandCursor],
MouseCursor::HandGrabbing => msg_send![nscursor_class, closedHandCursor],
MouseCursor::Help => msg_send![nscursor_class, arrowCursor], // No help cursor
MouseCursor::Hidden => {
// Return a null cursor for hidden - will be handled specially
std::ptr::null_mut()
}
MouseCursor::Text => msg_send![nscursor_class, IBeamCursor],
MouseCursor::VerticalText => msg_send![nscursor_class, IBeamCursor],
MouseCursor::Working => msg_send![nscursor_class, arrowCursor],
MouseCursor::PtrWorking => msg_send![nscursor_class, arrowCursor],
MouseCursor::NotAllowed => msg_send![nscursor_class, operationNotAllowedCursor],
MouseCursor::PtrNotAllowed => msg_send![nscursor_class, operationNotAllowedCursor],
MouseCursor::ZoomIn => msg_send![nscursor_class, arrowCursor],
MouseCursor::ZoomOut => msg_send![nscursor_class, arrowCursor],
MouseCursor::Alias => msg_send![nscursor_class, dragLinkCursor],
MouseCursor::Copy => msg_send![nscursor_class, dragCopyCursor],
MouseCursor::Move => msg_send![nscursor_class, arrowCursor],
MouseCursor::AllScroll => msg_send![nscursor_class, arrowCursor],
MouseCursor::Cell => msg_send![nscursor_class, crosshairCursor],
MouseCursor::Crosshair => msg_send![nscursor_class, crosshairCursor],
MouseCursor::EResize => msg_send![nscursor_class, resizeRightCursor],
MouseCursor::NResize => msg_send![nscursor_class, resizeUpCursor],
MouseCursor::NeResize => msg_send![nscursor_class, arrowCursor], // No built-in
MouseCursor::NwResize => msg_send![nscursor_class, arrowCursor], // No built-in
MouseCursor::SResize => msg_send![nscursor_class, resizeDownCursor],
MouseCursor::SeResize => msg_send![nscursor_class, arrowCursor], // No built-in
MouseCursor::SwResize => msg_send![nscursor_class, arrowCursor], // No built-in
MouseCursor::WResize => msg_send![nscursor_class, resizeLeftCursor],
MouseCursor::EwResize => msg_send![nscursor_class, resizeLeftRightCursor],
MouseCursor::NsResize => msg_send![nscursor_class, resizeUpDownCursor],
MouseCursor::NwseResize => {
// Use private API for diagonal resize cursor
msg_send![nscursor_class, _windowResizeNorthWestSouthEastCursor]
}
MouseCursor::NeswResize => {
// Use private API for diagonal resize cursor
msg_send![nscursor_class, _windowResizeNorthEastSouthWestCursor]
}
MouseCursor::ColResize => msg_send![nscursor_class, resizeLeftRightCursor],
MouseCursor::RowResize => msg_send![nscursor_class, resizeUpDownCursor],
}
}
}
1 change: 1 addition & 0 deletions src/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Eventually we should migrate to the objc2 crate and remove this.
#![allow(unexpected_cfgs)]

mod cursor;
mod keyboard;
mod view;
mod window;
Expand Down
65 changes: 56 additions & 9 deletions src/macos/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,17 +393,38 @@ extern "C" fn update_tracking_areas(this: &Object, _self: Sel, _: id) {
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
let state = unsafe { WindowState::from_view(this) };

let point: NSPoint = unsafe {
// Window-relative position (existing behavior)
let window_point: NSPoint = unsafe {
let point = NSEvent::locationInWindow(event);

msg_send![this, convertPoint:point fromView:nil]
};

// Screen-absolute position (new!)
// NSEvent::mouseLocation returns screen coordinates with Y=0 at BOTTOM
// We need to flip Y-axis to match Windows/X11 convention (Y=0 at TOP)
let screen_point: NSPoint = unsafe {
NSEvent::mouseLocation(event)
};

// Get the screen height to flip Y coordinate
let screen_height = unsafe {
let screen: id = msg_send![class!(NSScreen), mainScreen];
let frame: NSRect = msg_send![screen, frame];
frame.size.height
};

let modifiers = unsafe { NSEvent::modifierFlags(event) };

let position = Point { x: point.x, y: point.y };
let position = Point { x: window_point.x, y: window_point.y };
// Flip Y-axis: convert from bottom-origin to top-origin
let screen_position = Point {
x: screen_point.x,
y: screen_height - screen_point.y
};

state.trigger_event(Event::Mouse(MouseEvent::CursorMoved {
position,
screen_position,
modifiers: make_modifiers(modifiers),
}));
}
Expand All @@ -430,9 +451,29 @@ extern "C" fn scroll_wheel(this: &Object, _: Sel, event: id) {
}));
}

fn get_drag_position(sender: id) -> Point {
let point: NSPoint = unsafe { msg_send![sender, draggingLocation] };
Point::new(point.x, point.y)
fn get_drag_position(sender: id) -> (Point, Point) {
// Window-relative position
let window_point: NSPoint = unsafe { msg_send![sender, draggingLocation] };

// For drag events, we need to get screen position from the global mouse location
// since NSDraggingInfo doesn't provide it directly
// NSEvent::mouseLocation returns coordinates with Y=0 at BOTTOM
let screen_point: NSPoint = unsafe {
let ns_event_class: id = msg_send![class!(NSEvent), class];
msg_send![ns_event_class, mouseLocation]
};

// Get screen height to flip Y coordinate (convert from bottom-origin to top-origin)
let screen_height = unsafe {
let screen: id = msg_send![class!(NSScreen), mainScreen];
let frame: NSRect = msg_send![screen, frame];
frame.size.height
};

(
Point::new(window_point.x, window_point.y),
Point::new(screen_point.x, screen_height - screen_point.y)
)
}

fn get_drop_data(sender: id) -> DropData {
Expand Down Expand Up @@ -473,9 +514,11 @@ extern "C" fn dragging_entered(this: &Object, _sel: Sel, sender: id) -> NSUInteg
let state = unsafe { WindowState::from_view(this) };
let modifiers = state.keyboard_state().last_mods();
let drop_data = get_drop_data(sender);
let (position, screen_position) = get_drag_position(sender);

let event = MouseEvent::DragEntered {
position: get_drag_position(sender),
position,
screen_position,
modifiers: make_modifiers(modifiers),
data: drop_data,
};
Expand All @@ -487,9 +530,11 @@ extern "C" fn dragging_updated(this: &Object, _sel: Sel, sender: id) -> NSUInteg
let state = unsafe { WindowState::from_view(this) };
let modifiers = state.keyboard_state().last_mods();
let drop_data = get_drop_data(sender);
let (position, screen_position) = get_drag_position(sender);

let event = MouseEvent::DragMoved {
position: get_drag_position(sender),
position,
screen_position,
modifiers: make_modifiers(modifiers),
data: drop_data,
};
Expand All @@ -508,9 +553,11 @@ extern "C" fn perform_drag_operation(this: &Object, _sel: Sel, sender: id) -> BO
let state = unsafe { WindowState::from_view(this) };
let modifiers = state.keyboard_state().last_mods();
let drop_data = get_drop_data(sender);
let (position, screen_position) = get_drag_position(sender);

let event = MouseEvent::DragDropped {
position: get_drag_position(sender),
position,
screen_position,
modifiers: make_modifiers(modifiers),
data: drop_data,
};
Expand Down
14 changes: 12 additions & 2 deletions src/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,18 @@ impl<'a> Window<'a> {
}
}

pub fn set_mouse_cursor(&mut self, _mouse_cursor: MouseCursor) {
todo!()
pub fn set_mouse_cursor(&mut self, mouse_cursor: MouseCursor) {
let ns_cursor = crate::macos::cursor::mouse_cursor_to_nscursor(mouse_cursor);

unsafe {
if ns_cursor.is_null() {
// Hide cursor
let _: () = msg_send![class!(NSCursor), hide];
} else {
// Set the cursor
let _: () = msg_send![ns_cursor, set];
}
}
}

#[cfg(feature = "opengl")]
Expand Down
19 changes: 15 additions & 4 deletions src/win/drop_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub(super) struct DropTarget {
// These are cached since DragOver and DragLeave callbacks don't provide them,
// and handling drag move events gets awkward on the client end otherwise
drag_position: Point,
drag_screen_position: Point,
drop_data: DropData,
}

Expand All @@ -81,6 +82,7 @@ impl DropTarget {
window_state,

drag_position: Point::new(0.0, 0.0),
drag_screen_position: Point::new(0.0, 0.0),
drop_data: DropData::None,
}
}
Expand Down Expand Up @@ -114,10 +116,16 @@ impl DropTarget {
let Some(window_state) = self.window_state.upgrade() else {
return;
};
let mut pt = POINT { x: pt.x, y: pt.y };
unsafe { ScreenToClient(window_state.hwnd, &mut pt as *mut POINT) };
let phy_point = PhyPoint::new(pt.x, pt.y);
self.drag_position = phy_point.to_logical(&window_state.window_info());

// Screen-absolute position (pt is already in screen coordinates)
let screen_phy_point = PhyPoint::new(pt.x, pt.y);
self.drag_screen_position = screen_phy_point.to_logical(&window_state.window_info());

// Window-relative position (convert from screen to client)
let mut window_pt = POINT { x: pt.x, y: pt.y };
unsafe { ScreenToClient(window_state.hwnd, &mut window_pt as *mut POINT) };
let window_phy_point = PhyPoint::new(window_pt.x, window_pt.y);
self.drag_position = window_phy_point.to_logical(&window_state.window_info());
}

fn parse_drop_data(&mut self, data_object: &IDataObject) {
Expand Down Expand Up @@ -213,6 +221,7 @@ impl DropTarget {

let event = MouseEvent::DragEntered {
position: drop_target.drag_position,
screen_position: drop_target.drag_screen_position,
modifiers,
data: drop_target.drop_data.clone(),
};
Expand All @@ -237,6 +246,7 @@ impl DropTarget {

let event = MouseEvent::DragMoved {
position: drop_target.drag_position,
screen_position: drop_target.drag_screen_position,
modifiers,
data: drop_target.drop_data.clone(),
};
Expand Down Expand Up @@ -269,6 +279,7 @@ impl DropTarget {

let event = MouseEvent::DragDropped {
position: drop_target.drag_position,
screen_position: drop_target.drag_screen_position,
modifiers,
data: drop_target.drop_data.clone(),
};
Expand Down
14 changes: 14 additions & 0 deletions src/win/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,27 @@ unsafe fn wnd_proc_inner(
.on_event(&mut window, enter_event);
}

// Window-relative position (from lparam)
let x = (lparam & 0xFFFF) as i16 as i32;
let y = ((lparam >> 16) & 0xFFFF) as i16 as i32;

let physical_pos = PhyPoint { x, y };
let logical_pos = physical_pos.to_logical(&window_state.window_info.borrow());

// Screen-absolute position (using GetCursorPos)
let mut screen_physical_pos = POINT { x: 0, y: 0 };
unsafe {
winapi::um::winuser::GetCursorPos(&mut screen_physical_pos);
}
let screen_physical = PhyPoint {
x: screen_physical_pos.x,
y: screen_physical_pos.y
};
let screen_logical_pos = screen_physical.to_logical(&window_state.window_info.borrow());

let move_event = Event::Mouse(MouseEvent::CursorMoved {
position: logical_pos,
screen_position: screen_logical_pos,
modifiers: window_state
.keyboard_state
.borrow()
Expand Down
7 changes: 7 additions & 0 deletions src/x11/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,14 @@ impl EventLoop {
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
let logical_pos = physical_pos.to_logical(&self.window.window_info);

let screen_physical_pos = PhyPoint::new(event.root_x as i32, event.root_y as i32);
let screen_logical_pos = screen_physical_pos.to_logical(&self.window.window_info);

self.handler.on_event(
&mut crate::Window::new(Window { inner: &self.window }),
Event::Mouse(MouseEvent::CursorMoved {
position: logical_pos,
screen_position: screen_logical_pos,
modifiers: key_mods(event.state),
}),
);
Expand All @@ -198,10 +202,13 @@ impl EventLoop {
// we generate a CursorMoved as well, so the mouse position from here isn't lost
let physical_pos = PhyPoint::new(event.event_x as i32, event.event_y as i32);
let logical_pos = physical_pos.to_logical(&self.window.window_info);
let screen_physical_pos = PhyPoint::new(event.root_x as i32, event.root_y as i32);
let screen_logical_pos = screen_physical_pos.to_logical(&self.window.window_info);
self.handler.on_event(
&mut crate::Window::new(Window { inner: &self.window }),
Event::Mouse(MouseEvent::CursorMoved {
position: logical_pos,
screen_position: screen_logical_pos,
modifiers: key_mods(event.state),
}),
);
Expand Down