Files
galaxy/crates/galaxyui/src/platform/headless/delegate.rs
T

217 lines
5.9 KiB
Rust

use std::mem::ManuallyDrop;
use std::sync::mpsc::Sender;
use std::sync::{Arc, OnceLock};
use std::thread;
use parking_lot::Mutex;
use super::event_loop::AppEvent;
use crate::clipboard::InMemoryClipboard;
use crate::notification::{NotificationSendError, RequestPermissionsOutcome};
use crate::platform::{self, Cursor};
/// Stores the ID of the application's main thread, which we can reference
/// to determine if a given thread is the main thread or not.
static MAIN_THREAD_ID: OnceLock<thread::ThreadId> = OnceLock::new();
/// Marks the current thread as the application's main thread.
///
/// Panics if called more than once.
pub(super) fn mark_current_thread_as_main() {
MAIN_THREAD_ID
.set(thread::current().id())
.expect("should only call mark_current_thread_as_main once!");
}
pub struct AppDelegate {
clipboard: InMemoryClipboard,
cursor_shape: Mutex<Cursor>,
event_sender: Sender<AppEvent>,
}
impl AppDelegate {
pub(super) fn new(event_sender: Sender<AppEvent>) -> Self {
Self {
clipboard: InMemoryClipboard::default(),
cursor_shape: Mutex::new(Cursor::Arrow),
event_sender,
}
}
fn send_event(&self, event: AppEvent) {
if self.event_sender.send(event).is_err() {
log::debug!("Tried to send event, but event loop is no longer running");
}
}
}
impl platform::Delegate for AppDelegate {
fn dispatch_delegate(&self) -> Arc<dyn platform::DispatchDelegate> {
Arc::new(DispatchDelegate {
event_sender: self.event_sender.clone(),
})
}
fn request_user_attention(&self, _window_id: crate::WindowId) {
// Unsupported.
}
fn clipboard(&mut self) -> &mut dyn crate::Clipboard {
&mut self.clipboard
}
fn system_theme(&self) -> platform::SystemTheme {
platform::SystemTheme::Light
}
fn open_url(&self, url: &str) {
#[cfg(target_os = "macos")]
{
// Use macOS platform implementation
crate::platform::mac::Window::open_url(url);
}
#[cfg(not(target_os = "macos"))]
{
// Reuse the winit implementation for non-mac platforms
crate::windowing::winit::delegate::open_url_in_system(url);
}
}
fn open_file_path(&self, _path: &std::path::Path) {
// Unsupported.
}
fn open_file_path_in_explorer(&self, _path: &std::path::Path) {
// Unsupported.
}
fn open_file_picker(
&self,
callback: platform::FilePickerCallback,
_file_picker_config: platform::FilePickerConfiguration,
) {
self.send_event(AppEvent::RunCallback(Box::new(move |ctx| {
callback(Ok(vec![]), ctx);
})));
}
fn open_save_file_picker(
&self,
callback: platform::SaveFilePickerCallback,
_config: platform::SaveFilePickerConfiguration,
) {
self.send_event(AppEvent::RunCallback(Box::new(move |ctx| {
callback(None, ctx);
})));
}
fn application_bundle_info(
&self,
_bundle_identifier: &str,
) -> Option<crate::ApplicationBundleInfo<'_>> {
// This is unsupported, though we could delegate to the macOS implementation.
None
}
fn show_native_platform_modal(
&self,
_id: crate::modals::ModalId,
_modal: crate::modals::AlertDialog,
) {
// Unsupported.
}
fn request_desktop_notification_permissions(
&self,
on_completion: platform::RequestNotificationPermissionsCallback,
) {
self.send_event(AppEvent::RunCallback(Box::new(move |ctx| {
on_completion(RequestPermissionsOutcome::PermissionsDenied, ctx);
})));
}
fn send_desktop_notification(
&self,
_notification_content: crate::notification::UserNotification,
_window_id: crate::WindowId,
on_error: platform::SendNotificationErrorCallback,
) {
self.send_event(AppEvent::RunCallback(Box::new(move |ctx| {
on_error(NotificationSendError::PermissionsDenied, ctx);
})));
}
fn set_cursor_shape(&self, cursor: Cursor) {
*self.cursor_shape.lock() = cursor;
}
#[cfg(feature = "test-util")]
fn get_cursor_shape(&self) -> Cursor {
*self.cursor_shape.lock()
}
fn close_ime_async(&self, _window_id: crate::WindowId) {
// Unsupported.
}
fn is_ime_open(&self) -> bool {
false
}
fn open_character_palette(&self) {
// Unsupported.
}
fn set_accessibility_contents(&self, _content: crate::accessibility::AccessibilityContent) {
// Unsupported.
}
fn register_global_shortcut(&self, _shortcut: crate::keymap::Keystroke) {
// Unsupported.
}
fn unregister_global_shortcut(&self, _shortcut: &crate::keymap::Keystroke) {
// Unsupported.
}
fn terminate_app(&self, termination_mode: platform::TerminationMode) {
self.send_event(AppEvent::Terminate(termination_mode));
}
fn is_screen_reader_enabled(&self) -> Option<bool> {
None
}
fn microphone_access_state(&self) -> platform::MicrophoneAccessState {
platform::MicrophoneAccessState::Denied
}
fn is_headless(&self) -> bool {
true
}
}
struct DispatchDelegate {
event_sender: Sender<AppEvent>,
}
impl platform::DispatchDelegate for DispatchDelegate {
fn is_main_thread(&self) -> bool {
thread::current().id()
== *MAIN_THREAD_ID
.get()
.expect("should have marked a thread as the main thread")
}
fn run_on_main_thread(&self, task: async_task::Runnable) {
// See crate::windowing::winit::delegate::DispatchDelegate for why we use ManuallyDrop.
if self
.event_sender
.send(AppEvent::RunTask(ManuallyDrop::new(task)))
.is_err()
{
log::debug!("Tried to send event, but event loop is no longer running");
}
}
}