Skip to content
Closed
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
1 change: 1 addition & 0 deletions src/changelog/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ changelog entry.

### Added

- Added `open_files_event` to `ApplicationHandler` to handle file open requests (currently only on macOS).
- Add `ActiveEventLoop::create_proxy()`.
- On Web, add `ActiveEventLoopExtWeb::is_cursor_lock_raw()` to determine if
`DeviceEvent::MouseMotion` is returning raw data, not OS accelerated, when using
Expand Down
2 changes: 1 addition & 1 deletion winit-appkit/src/app_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ impl AppState {
}

#[track_caller]
fn with_handler(
pub fn with_handler(
self: &Rc<Self>,
callback: impl FnOnce(&mut dyn ApplicationHandler, &ActiveEventLoop),
) {
Expand Down
76 changes: 76 additions & 0 deletions winit-appkit/src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,78 @@ impl Default for PlatformSpecificEventLoopAttributes {
}
}

use objc2::{define_class, msg_send, MainThreadOnly};
use objc2_app_kit::NSApplicationDelegate;
use objc2_app_kit::NSApplicationDelegateReply;
use objc2_foundation::{NSArray, NSObject, NSURL};
use std::path::PathBuf;
use tracing::{trace, warn};

define_class!(
#[unsafe(super(NSObject))]
#[thread_kind = MainThreadOnly]
#[name = "AppDelegate"]
struct AppDelegate;

unsafe impl NSObjectProtocol for AppDelegate {}

unsafe impl NSApplicationDelegate for AppDelegate {
#[unsafe(method(application:openFiles:))]
#[allow(non_snake_case)]
fn application_openFiles(&self, application: &NSApplication, files: &NSArray<NSURL>) {
trace!("Triggered `application:openFiles:`");
let mtm =
MainThreadMarker::new().expect("must be on main thread for application:openFiles:");

let mut paths: Vec<PathBuf> = Vec::new();
unsafe {
for ns_url in files {
if let Some(ns_path_str) = ns_url.path() {
let path_str = ns_path_str.to_string();
let path_buf = PathBuf::from(path_str);
if path_buf.exists() {
paths.push(path_buf);
} else {
warn!(
"Received non-existent path from application:openFiles: {:?}",
path_buf
);
}
} else {
warn!(
"Received non-file URL or malformed path from application:openFiles:"
);
}
}

if paths.is_empty() {
// According to Apple's documentation, you should always call replyToOpenOrPrint.
// NSApplicationDelegateReply::Cancel indicates failure or no action.
application.replyToOpenOrPrint(NSApplicationDelegateReply::Cancel);
} else {
application.replyToOpenOrPrint(NSApplicationDelegateReply::Success);

let app_state = AppState::get(mtm);
let cloned_paths = paths;

app_state.with_handler(
move |app_handler, active_event_loop: &ActiveEventLoop| {
app_handler.open_files_event(active_event_loop, cloned_paths);
},
);
}
}
trace!("Completed `application:openFiles:`");
}
}
);

impl AppDelegate {
fn new(mtm: MainThreadMarker) -> Retained<Self> {
unsafe { msg_send![super(Self::alloc(mtm).set_ivars(())), init] }
}
}

impl EventLoop {
pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
let mtm = MainThreadMarker::new()
Expand All @@ -184,9 +256,13 @@ impl EventLoop {
attributes.activate_ignoring_other_apps,
);

let delegate = AppDelegate::new(mtm);

// Initialize the application (if it has not already been).
let app = NSApplication::sharedApplication(mtm);

app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));

// Override `sendEvent:` on the application to forward to our application state.
override_send_event(&app);

Expand Down
8 changes: 8 additions & 0 deletions winit-core/src/application/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! End user application handling.

use std::path::PathBuf;
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
use crate::window::WindowId;
Expand All @@ -18,6 +19,13 @@ pub trait ApplicationHandler {
let _ = (event_loop, cause);
}

fn open_files_event(&mut self, event_loop: &dyn ActiveEventLoop, paths: Vec<PathBuf>) {
// Default implementation (optional, or leave it for implementers)
let _ = event_loop; // Mark as used
let _ = paths; // Mark as used
}


/// Emitted when the application has been resumed.
///
/// See [`suspended()`][Self::suspended].
Expand Down
Loading