Skip to content

Commit

Permalink
Handle URLs for macOS
Browse files Browse the repository at this point in the history
  • Loading branch information
derezzedex authored and hecrj committed May 7, 2024
1 parent 4b3c065 commit b8dbbd4
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 3 deletions.
9 changes: 9 additions & 0 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,13 @@ pub trait ApplicationHandler<T: 'static = ()> {
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}

/// Emitted when the application has received a URL, through a
/// custom URL handler.
///
/// Only supported on macOS.
fn received_url(&mut self, event_loop: &ActiveEventLoop, url: String) {
let _ = event_loop;
let _ = url;
}
}
16 changes: 16 additions & 0 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,21 @@ pub enum Event<T: 'static> {
///
/// [`ApplicationHandler::memory_warning`]: crate::application::ApplicationHandler::memory_warning
MemoryWarning,

/// Emitted when the event loop receives an event that only occurs on some specific platform.
PlatformSpecific(PlatformSpecific),
}

/// Describes an event from some specific platform.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlatformSpecific {
MacOS(MacOS),
}

/// Describes an event that only happens in `MacOS`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MacOS {
ReceivedUrl(String),
}

impl<T> Event<T> {
Expand All @@ -119,6 +134,7 @@ impl<T> Event<T> {
Suspended => Ok(Suspended),
Resumed => Ok(Resumed),
MemoryWarning => Ok(MemoryWarning),
PlatformSpecific(event) => Ok(PlatformSpecific(event)),
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use web_time::{Duration, Instant};

use crate::application::ApplicationHandler;
use crate::error::{EventLoopError, OsError};
use crate::event::Event;
use crate::event::{self, Event};
use crate::monitor::MonitorHandle;
use crate::platform_impl;
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
Expand Down Expand Up @@ -633,5 +633,8 @@ pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
Event::AboutToWait => app.about_to_wait(event_loop),
Event::LoopExiting => app.exiting(event_loop),
Event::MemoryWarning => app.memory_warning(event_loop),
Event::PlatformSpecific(event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(url))) => {
app.received_url(event_loop, url)
},
}
}
61 changes: 59 additions & 2 deletions src/platform_impl/macos/app_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use std::time::Instant;

use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2::{
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol, NSSize};

Expand All @@ -17,7 +19,9 @@ use super::observer::{EventLoopWaker, RunLoop};
use super::window::WinitWindow;
use super::{menu, WindowId, DEVICE_ID};
use crate::dpi::PhysicalSize;
use crate::event::{DeviceEvent, Event, InnerSizeWriter, StartCause, WindowEvent};
use crate::event::{
DeviceEvent, Event, InnerSizeWriter, MacOS, PlatformSpecific, StartCause, WindowEvent,
};
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
use crate::window::WindowId as RootWindowId;

Expand Down Expand Up @@ -54,6 +58,14 @@ pub(super) struct State {
pending_redraw: RefCell<Vec<WindowId>>,
}

/// Apple constants
#[allow(non_upper_case_globals)]
pub const kInternetEventClass: u32 = 0x4755524c;
#[allow(non_upper_case_globals)]
pub const kAEGetURL: u32 = 0x4755524c;
#[allow(non_upper_case_globals)]
pub const keyDirectObject: u32 = 0x2d2d2d2d;

declare_class!(
#[derive(Debug)]
pub(super) struct ApplicationDelegate;
Expand Down Expand Up @@ -116,6 +128,33 @@ declare_class!(
}
}

#[method(applicationWillFinishLaunching:)]
fn will_finish_launching(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationWillFinishLaunching");

unsafe {
let event_manager = class!(NSAppleEventManager);
let shared_manager: *mut AnyObject =
msg_send![event_manager, sharedAppleEventManager];

let () = msg_send![shared_manager,
setEventHandler: self
andSelector: sel!(handleEvent:withReplyEvent:)
forEventClass: kInternetEventClass
andEventID: kAEGetURL
];
}
}

#[method(handleEvent:withReplyEvent:)]
fn handle_url(&self, event: *mut AnyObject, _reply: u64) {
if let Some(string) = parse_url(event) {
self.handle_event(Event::PlatformSpecific(PlatformSpecific::MacOS(
MacOS::ReceivedUrl(string),
)));
}
}

#[method(applicationWillTerminate:)]
fn will_terminate(&self, _sender: Option<&AnyObject>) {
trace_scope!("applicationWillTerminate:");
Expand Down Expand Up @@ -459,3 +498,21 @@ fn window_activation_hack(app: &NSApplication) {
}
})
}

fn parse_url(event: *mut AnyObject) -> Option<String> {
unsafe {
let class: u32 = msg_send![event, eventClass];
let id: u32 = msg_send![event, eventID];
if class != kInternetEventClass || id != kAEGetURL {
return None;
}
let subevent: *mut AnyObject = msg_send![event, paramDescriptorForKeyword: keyDirectObject];
let nsstring: *mut AnyObject = msg_send![subevent, stringValue];
let cstr: *const i8 = msg_send![nsstring, UTF8String];
if !cstr.is_null() {
Some(std::ffi::CStr::from_ptr(cstr).to_string_lossy().into_owned())
} else {
None
}
}
}

0 comments on commit b8dbbd4

Please sign in to comment.