mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-18 18:08:07 +03:00
Fix or promote leftover TODOs and GPUI APIs (#12514)
fixes https://github.com/zed-industries/zed/issues/11966 Release Notes: - N/A
This commit is contained in:
parent
a6e0c8aca1
commit
94c3101fb0
@ -494,10 +494,5 @@ non_canonical_partial_ord_impl = "allow"
|
|||||||
reversed_empty_ranges = "allow"
|
reversed_empty_ranges = "allow"
|
||||||
type_complexity = "allow"
|
type_complexity = "allow"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
|
||||||
'cfg(gles)', # used in gpui
|
|
||||||
] }
|
|
||||||
|
|
||||||
[workspace.metadata.cargo-machete]
|
[workspace.metadata.cargo-machete]
|
||||||
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]
|
ignored = ["bindgen", "cbindgen", "prost_build", "serde"]
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
"ctrl-0": "zed::ResetBufferFontSize",
|
"ctrl-0": "zed::ResetBufferFontSize",
|
||||||
"ctrl-,": "zed::OpenSettings",
|
"ctrl-,": "zed::OpenSettings",
|
||||||
"ctrl-q": "zed::Quit",
|
"ctrl-q": "zed::Quit",
|
||||||
"alt-f9": "zed::Hide",
|
|
||||||
"f11": "zed::ToggleFullScreen"
|
"f11": "zed::ToggleFullScreen"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,7 +99,6 @@ objc = "0.2"
|
|||||||
|
|
||||||
[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
|
[target.'cfg(any(target_os = "linux", target_os = "windows"))'.dependencies]
|
||||||
flume = "0.11"
|
flume = "0.11"
|
||||||
#TODO: use these on all platforms
|
|
||||||
blade-graphics.workspace = true
|
blade-graphics.workspace = true
|
||||||
blade-macros.workspace = true
|
blade-macros.workspace = true
|
||||||
blade-util.workspace = true
|
blade-util.workspace = true
|
||||||
|
@ -29,10 +29,11 @@ use crate::{
|
|||||||
current_platform, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
|
current_platform, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
|
||||||
AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
AppMetadata, AssetCache, AssetSource, BackgroundExecutor, ClipboardItem, Context,
|
||||||
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
|
DispatchPhase, DisplayId, Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap,
|
||||||
Keystroke, LayoutId, Menu, MenuItem, PathPromptOptions, Pixels, Platform, PlatformDisplay,
|
Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
|
||||||
Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
|
PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
|
||||||
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem, View, ViewContext,
|
RenderablePromptHandle, Reservation, SharedString, SubscriberSet, Subscription, SvgRenderer,
|
||||||
Window, WindowAppearance, WindowContext, WindowHandle, WindowId,
|
Task, TextSystem, View, ViewContext, Window, WindowAppearance, WindowContext, WindowHandle,
|
||||||
|
WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod async_context;
|
mod async_context;
|
||||||
@ -1167,6 +1168,11 @@ impl AppContext {
|
|||||||
self.platform.set_menus(menus, &self.keymap.borrow());
|
self.platform.set_menus(menus, &self.keymap.borrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the menu bar for this application. This will replace any existing menu bar.
|
||||||
|
pub fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
|
||||||
|
self.platform.get_menus()
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the right click menu for the app icon in the dock
|
/// Sets the right click menu for the app icon in the dock
|
||||||
pub fn set_dock_menu(&mut self, menus: Vec<MenuItem>) {
|
pub fn set_dock_menu(&mut self, menus: Vec<MenuItem>) {
|
||||||
self.platform.set_dock_menu(menus, &self.keymap.borrow());
|
self.platform.set_dock_menu(menus, &self.keymap.borrow());
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// todo(linux): remove
|
|
||||||
#![cfg_attr(target_os = "linux", allow(dead_code))]
|
|
||||||
// todo(windows): remove
|
// todo(windows): remove
|
||||||
#![cfg_attr(windows, allow(dead_code))]
|
#![cfg_attr(windows, allow(dead_code))]
|
||||||
|
|
||||||
@ -135,6 +133,10 @@ pub(crate) trait Platform: 'static {
|
|||||||
fn on_reopen(&self, callback: Box<dyn FnMut()>);
|
fn on_reopen(&self, callback: Box<dyn FnMut()>);
|
||||||
|
|
||||||
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
|
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
|
||||||
|
fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap);
|
fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap);
|
||||||
fn add_recent_document(&self, _path: &Path) {}
|
fn add_recent_document(&self, _path: &Path) {}
|
||||||
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
|
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
|
||||||
@ -203,7 +205,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
|
|||||||
fn content_size(&self) -> Size<Pixels>;
|
fn content_size(&self) -> Size<Pixels>;
|
||||||
fn scale_factor(&self) -> f32;
|
fn scale_factor(&self) -> f32;
|
||||||
fn appearance(&self) -> WindowAppearance;
|
fn appearance(&self) -> WindowAppearance;
|
||||||
fn display(&self) -> Rc<dyn PlatformDisplay>;
|
fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
|
||||||
fn mouse_position(&self) -> Point<Pixels>;
|
fn mouse_position(&self) -> Point<Pixels>;
|
||||||
fn modifiers(&self) -> Modifiers;
|
fn modifiers(&self) -> Modifiers;
|
||||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
|
fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
|
||||||
@ -413,6 +415,7 @@ impl PlatformInputHandler {
|
|||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
|
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
|
||||||
self.cx
|
self.cx
|
||||||
.update(|cx| self.handler.text_for_range(range_utf16, cx))
|
.update(|cx| self.handler.text_for_range(range_utf16, cx))
|
||||||
@ -573,13 +576,17 @@ pub(crate) struct WindowParams {
|
|||||||
pub titlebar: Option<TitlebarOptions>,
|
pub titlebar: Option<TitlebarOptions>,
|
||||||
|
|
||||||
/// The kind of window to create
|
/// The kind of window to create
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
pub kind: WindowKind,
|
pub kind: WindowKind,
|
||||||
|
|
||||||
/// Whether the window should be movable by the user
|
/// Whether the window should be movable by the user
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
pub is_movable: bool,
|
pub is_movable: bool,
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
pub focus: bool,
|
pub focus: bool,
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
pub show: bool,
|
pub show: bool,
|
||||||
|
|
||||||
pub display_id: Option<DisplayId>,
|
pub display_id: Option<DisplayId>,
|
||||||
@ -797,10 +804,6 @@ pub enum CursorStyle {
|
|||||||
/// corresponds to the CSS curosr value `row-resize`
|
/// corresponds to the CSS curosr value `row-resize`
|
||||||
ResizeRow,
|
ResizeRow,
|
||||||
|
|
||||||
/// A cursor indicating that something will disappear if moved here
|
|
||||||
/// Does not correspond to a CSS cursor value
|
|
||||||
DisappearingItem,
|
|
||||||
|
|
||||||
/// A text input cursor for vertical layout
|
/// A text input cursor for vertical layout
|
||||||
/// corresponds to the CSS cursor value `vertical-text`
|
/// corresponds to the CSS cursor value `vertical-text`
|
||||||
IBeamCursorForVerticalLayout,
|
IBeamCursorForVerticalLayout,
|
||||||
@ -865,6 +868,7 @@ impl ClipboardItem {
|
|||||||
.and_then(|m| serde_json::from_str(m).ok())
|
.and_then(|m| serde_json::from_str(m).ok())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
pub(crate) fn text_hash(text: &str) -> u64 {
|
pub(crate) fn text_hash(text: &str) -> u64 {
|
||||||
let mut hasher = SeaHasher::new();
|
let mut hasher = SeaHasher::new();
|
||||||
text.hash(&mut hasher);
|
text.hash(&mut hasher);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::{Action, AppContext, Platform};
|
use crate::{Action, AppContext, Platform};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
@ -10,6 +12,16 @@ pub struct Menu<'a> {
|
|||||||
pub items: Vec<MenuItem<'a>>,
|
pub items: Vec<MenuItem<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> Menu<'a> {
|
||||||
|
/// Create an OwnedMenu from this Menu
|
||||||
|
pub fn owned(self) -> OwnedMenu {
|
||||||
|
OwnedMenu {
|
||||||
|
name: self.name.to_string().into(),
|
||||||
|
items: self.items.into_iter().map(|item| item.owned()).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The different kinds of items that can be in a menu
|
/// The different kinds of items that can be in a menu
|
||||||
pub enum MenuItem<'a> {
|
pub enum MenuItem<'a> {
|
||||||
/// A separator between items
|
/// A separator between items
|
||||||
@ -60,6 +72,73 @@ impl<'a> MenuItem<'a> {
|
|||||||
os_action: Some(os_action),
|
os_action: Some(os_action),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create an OwnedMenuItem from this MenuItem
|
||||||
|
pub fn owned(self) -> OwnedMenuItem {
|
||||||
|
match self {
|
||||||
|
MenuItem::Separator => OwnedMenuItem::Separator,
|
||||||
|
MenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.owned()),
|
||||||
|
MenuItem::Action {
|
||||||
|
name,
|
||||||
|
action,
|
||||||
|
os_action,
|
||||||
|
} => OwnedMenuItem::Action {
|
||||||
|
name: name.into(),
|
||||||
|
action,
|
||||||
|
os_action,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A menu of the application, either a main menu or a submenu
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct OwnedMenu {
|
||||||
|
/// The name of the menu
|
||||||
|
pub name: Cow<'static, str>,
|
||||||
|
|
||||||
|
/// The items in the menu
|
||||||
|
pub items: Vec<OwnedMenuItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The different kinds of items that can be in a menu
|
||||||
|
pub enum OwnedMenuItem {
|
||||||
|
/// A separator between items
|
||||||
|
Separator,
|
||||||
|
|
||||||
|
/// A submenu
|
||||||
|
Submenu(OwnedMenu),
|
||||||
|
|
||||||
|
/// An action that can be performed
|
||||||
|
Action {
|
||||||
|
/// The name of this menu item
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
/// the action to perform when this menu item is selected
|
||||||
|
action: Box<dyn Action>,
|
||||||
|
|
||||||
|
/// The OS Action that corresponds to this action, if any
|
||||||
|
/// See [`OsAction`] for more information
|
||||||
|
os_action: Option<OsAction>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for OwnedMenuItem {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
match self {
|
||||||
|
OwnedMenuItem::Separator => OwnedMenuItem::Separator,
|
||||||
|
OwnedMenuItem::Submenu(submenu) => OwnedMenuItem::Submenu(submenu.clone()),
|
||||||
|
OwnedMenuItem::Action {
|
||||||
|
name,
|
||||||
|
action,
|
||||||
|
os_action,
|
||||||
|
} => OwnedMenuItem::Action {
|
||||||
|
name: name.clone(),
|
||||||
|
action: action.boxed_clone(),
|
||||||
|
os_action: *os_action,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: As part of the global selections refactor, these should
|
// TODO: As part of the global selections refactor, these should
|
||||||
|
@ -20,7 +20,9 @@ use std::{mem, sync::Arc};
|
|||||||
|
|
||||||
const MAX_FRAME_TIME_MS: u32 = 1000;
|
const MAX_FRAME_TIME_MS: u32 = 1000;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
pub type Context = ();
|
pub type Context = ();
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
pub type Renderer = BladeRenderer;
|
pub type Renderer = BladeRenderer;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
// todo(linux): remove
|
|
||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
mod headless;
|
mod headless;
|
||||||
mod platform;
|
mod platform;
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
|
||||||
#![allow(non_camel_case_types)]
|
|
||||||
#![allow(non_snake_case)]
|
|
||||||
// todo(linux): remove
|
|
||||||
#![allow(unused_variables)]
|
|
||||||
|
|
||||||
use crate::{PlatformDispatcher, TaskLabel};
|
use crate::{PlatformDispatcher, TaskLabel};
|
||||||
use async_task::Runnable;
|
use async_task::Runnable;
|
||||||
use calloop::{
|
use calloop::{
|
||||||
@ -63,7 +57,7 @@ impl LinuxDispatcher {
|
|||||||
timer_handle
|
timer_handle
|
||||||
.insert_source(
|
.insert_source(
|
||||||
calloop::timer::Timer::from_duration(timer.duration),
|
calloop::timer::Timer::from_duration(timer.duration),
|
||||||
move |e, _, _| {
|
move |_, _, _| {
|
||||||
if let Some(runnable) = runnable.take() {
|
if let Some(runnable) = runnable.take() {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,16 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use calloop::{EventLoop, LoopHandle};
|
use calloop::{EventLoop, LoopHandle};
|
||||||
use collections::HashMap;
|
|
||||||
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::platform::linux::LinuxClient;
|
use crate::platform::linux::LinuxClient;
|
||||||
use crate::platform::{LinuxCommon, PlatformWindow};
|
use crate::platform::{LinuxCommon, PlatformWindow};
|
||||||
use crate::{
|
use crate::{AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, WindowParams};
|
||||||
px, AnyWindowHandle, Bounds, CursorStyle, DisplayId, Modifiers, ModifiersChangedEvent, Pixels,
|
|
||||||
PlatformDisplay, PlatformInput, Point, ScrollDelta, Size, TouchPhase, WindowParams,
|
|
||||||
};
|
|
||||||
|
|
||||||
use calloop::{
|
|
||||||
generic::{FdWrapper, Generic},
|
|
||||||
RegistrationToken,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct HeadlessClientState {
|
pub struct HeadlessClientState {
|
||||||
pub(crate) loop_handle: LoopHandle<'static, HeadlessClient>,
|
pub(crate) _loop_handle: LoopHandle<'static, HeadlessClient>,
|
||||||
pub(crate) event_loop: Option<calloop::EventLoop<'static, HeadlessClient>>,
|
pub(crate) event_loop: Option<calloop::EventLoop<'static, HeadlessClient>>,
|
||||||
pub(crate) common: LinuxCommon,
|
pub(crate) common: LinuxCommon,
|
||||||
}
|
}
|
||||||
@ -37,15 +26,17 @@ impl HeadlessClient {
|
|||||||
|
|
||||||
let handle = event_loop.handle();
|
let handle = event_loop.handle();
|
||||||
|
|
||||||
handle.insert_source(main_receiver, |event, _, _: &mut HeadlessClient| {
|
handle
|
||||||
|
.insert_source(main_receiver, |event, _, _: &mut HeadlessClient| {
|
||||||
if let calloop::channel::Event::Msg(runnable) = event {
|
if let calloop::channel::Event::Msg(runnable) = event {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
HeadlessClient(Rc::new(RefCell::new(HeadlessClientState {
|
HeadlessClient(Rc::new(RefCell::new(HeadlessClientState {
|
||||||
event_loop: Some(event_loop),
|
event_loop: Some(event_loop),
|
||||||
loop_handle: handle,
|
_loop_handle: handle,
|
||||||
common,
|
common,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
@ -64,7 +55,7 @@ impl LinuxClient for HeadlessClient {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
fn display(&self, _id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,10 +63,14 @@ impl LinuxClient for HeadlessClient {
|
|||||||
return Err(anyhow::anyhow!("neither DISPLAY, nor WAYLAND_DISPLAY found. You can still run zed for remote development with --dev-server-token."));
|
return Err(anyhow::anyhow!("neither DISPLAY, nor WAYLAND_DISPLAY found. You can still run zed for remote development with --dev-server-token."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn open_window(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
_handle: AnyWindowHandle,
|
_handle: AnyWindowHandle,
|
||||||
params: WindowParams,
|
_params: WindowParams,
|
||||||
) -> Box<dyn PlatformWindow> {
|
) -> Box<dyn PlatformWindow> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
@ -84,9 +79,9 @@ impl LinuxClient for HeadlessClient {
|
|||||||
|
|
||||||
fn open_uri(&self, _uri: &str) {}
|
fn open_uri(&self, _uri: &str) {}
|
||||||
|
|
||||||
fn write_to_primary(&self, item: crate::ClipboardItem) {}
|
fn write_to_primary(&self, _item: crate::ClipboardItem) {}
|
||||||
|
|
||||||
fn write_to_clipboard(&self, item: crate::ClipboardItem) {}
|
fn write_to_clipboard(&self, _item: crate::ClipboardItem) {}
|
||||||
|
|
||||||
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
||||||
None
|
None
|
||||||
|
@ -38,9 +38,9 @@ use crate::platform::linux::xdg_desktop_portal::{should_auto_hide_scrollbars, wi
|
|||||||
use crate::{
|
use crate::{
|
||||||
px, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CosmicTextSystem, CursorStyle,
|
px, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CosmicTextSystem, CursorStyle,
|
||||||
DisplayId, ForegroundExecutor, Keymap, Keystroke, LinuxDispatcher, Menu, MenuItem, Modifiers,
|
DisplayId, ForegroundExecutor, Keymap, Keystroke, LinuxDispatcher, Menu, MenuItem, Modifiers,
|
||||||
PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformInputHandler, PlatformTextSystem,
|
OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformInputHandler,
|
||||||
PlatformWindow, Point, PromptLevel, Result, SemanticVersion, Size, Task, WindowAppearance,
|
PlatformTextSystem, PlatformWindow, Point, PromptLevel, Result, SemanticVersion, Size, Task,
|
||||||
WindowOptions, WindowParams,
|
WindowAppearance, WindowOptions, WindowParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::x11::X11Client;
|
use super::x11::X11Client;
|
||||||
@ -72,6 +72,7 @@ pub trait LinuxClient {
|
|||||||
fn write_to_clipboard(&self, item: ClipboardItem);
|
fn write_to_clipboard(&self, item: ClipboardItem);
|
||||||
fn read_from_primary(&self) -> Option<ClipboardItem>;
|
fn read_from_primary(&self) -> Option<ClipboardItem>;
|
||||||
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
fn read_from_clipboard(&self) -> Option<ClipboardItem>;
|
||||||
|
fn active_window(&self) -> Option<AnyWindowHandle>;
|
||||||
fn run(&self);
|
fn run(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,6 +94,7 @@ pub(crate) struct LinuxCommon {
|
|||||||
pub(crate) auto_hide_scrollbars: bool,
|
pub(crate) auto_hide_scrollbars: bool,
|
||||||
pub(crate) callbacks: PlatformHandlers,
|
pub(crate) callbacks: PlatformHandlers,
|
||||||
pub(crate) signal: LoopSignal,
|
pub(crate) signal: LoopSignal,
|
||||||
|
pub(crate) menus: Vec<OwnedMenu>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinuxCommon {
|
impl LinuxCommon {
|
||||||
@ -118,6 +120,7 @@ impl LinuxCommon {
|
|||||||
auto_hide_scrollbars,
|
auto_hide_scrollbars,
|
||||||
callbacks,
|
callbacks,
|
||||||
signal,
|
signal,
|
||||||
|
menus: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
(common, main_receiver)
|
(common, main_receiver)
|
||||||
@ -210,18 +213,21 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
fn activate(&self, ignoring_other_apps: bool) {
|
||||||
fn activate(&self, ignoring_other_apps: bool) {}
|
log::info!("activate is not implemented on Linux, ignoring the call")
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
fn hide(&self) {}
|
|
||||||
|
|
||||||
fn hide_other_apps(&self) {
|
|
||||||
log::warn!("hide_other_apps is not implemented on Linux, ignoring the call")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
fn hide(&self) {
|
||||||
fn unhide_other_apps(&self) {}
|
log::info!("hide is not implemented on Linux, ignoring the call")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hide_other_apps(&self) {
|
||||||
|
log::info!("hide_other_apps is not implemented on Linux, ignoring the call")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unhide_other_apps(&self) {
|
||||||
|
log::info!("unhide_other_apps is not implemented on Linux, ignoring the call")
|
||||||
|
}
|
||||||
|
|
||||||
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
self.primary_display()
|
self.primary_display()
|
||||||
@ -231,9 +237,8 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||||||
self.displays()
|
self.displays()
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
fn active_window(&self) -> Option<AnyWindowHandle> {
|
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||||
None
|
self.active_window()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_window(
|
fn open_window(
|
||||||
@ -387,15 +392,22 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||||||
Ok(exe_path)
|
Ok(exe_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
fn set_menus(&self, menus: Vec<Menu>, _keymap: &Keymap) {
|
||||||
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {}
|
self.with_common(|common| {
|
||||||
|
common.menus = menus.into_iter().map(|menu| menu.owned()).collect();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_menus(&self) -> Option<Vec<OwnedMenu>> {
|
||||||
|
self.with_common(|common| Some(common.menus.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap) {}
|
fn set_dock_menu(&self, menu: Vec<MenuItem>, keymap: &Keymap) {}
|
||||||
|
|
||||||
fn local_timezone(&self) -> UtcOffset {
|
fn local_timezone(&self) -> UtcOffset {
|
||||||
UtcOffset::UTC
|
UtcOffset::UTC
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo(linux)
|
|
||||||
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
|
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
|
||||||
Err(anyhow::Error::msg(
|
Err(anyhow::Error::msg(
|
||||||
"Platform<LinuxPlatform>::path_for_auxiliary_executable is not implemented yet",
|
"Platform<LinuxPlatform>::path_for_auxiliary_executable is not implemented yet",
|
||||||
@ -549,7 +561,6 @@ impl CursorStyle {
|
|||||||
CursorStyle::ResizeUpDown => Shape::NsResize,
|
CursorStyle::ResizeUpDown => Shape::NsResize,
|
||||||
CursorStyle::ResizeColumn => Shape::ColResize,
|
CursorStyle::ResizeColumn => Shape::ColResize,
|
||||||
CursorStyle::ResizeRow => Shape::RowResize,
|
CursorStyle::ResizeRow => Shape::RowResize,
|
||||||
CursorStyle::DisappearingItem => Shape::Grabbing, // todo(linux) - couldn't find equivalent icon in linux
|
|
||||||
CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText,
|
CursorStyle::IBeamCursorForVerticalLayout => Shape::VerticalText,
|
||||||
CursorStyle::OperationNotAllowed => Shape::NotAllowed,
|
CursorStyle::OperationNotAllowed => Shape::NotAllowed,
|
||||||
CursorStyle::DragLink => Shape::Alias,
|
CursorStyle::DragLink => Shape::Alias,
|
||||||
@ -577,7 +588,6 @@ impl CursorStyle {
|
|||||||
CursorStyle::ResizeUpDown => "ns-resize",
|
CursorStyle::ResizeUpDown => "ns-resize",
|
||||||
CursorStyle::ResizeColumn => "col-resize",
|
CursorStyle::ResizeColumn => "col-resize",
|
||||||
CursorStyle::ResizeRow => "row-resize",
|
CursorStyle::ResizeRow => "row-resize",
|
||||||
CursorStyle::DisappearingItem => "grabbing", // todo(linux) - couldn't find equivalent icon in linux
|
|
||||||
CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
|
CursorStyle::IBeamCursorForVerticalLayout => "vertical-text",
|
||||||
CursorStyle::OperationNotAllowed => "not-allowed",
|
CursorStyle::OperationNotAllowed => "not-allowed",
|
||||||
CursorStyle::DragLink => "alias",
|
CursorStyle::DragLink => "alias",
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
use core::hash;
|
|
||||||
use std::cell::{RefCell, RefMut};
|
use std::cell::{RefCell, RefMut};
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::hash::Hash;
|
||||||
use std::os::fd::{AsRawFd, BorrowedFd};
|
use std::os::fd::{AsRawFd, BorrowedFd};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use async_task::Runnable;
|
|
||||||
use calloop::timer::{TimeoutAction, Timer};
|
use calloop::timer::{TimeoutAction, Timer};
|
||||||
use calloop::{EventLoop, LoopHandle};
|
use calloop::{EventLoop, LoopHandle};
|
||||||
use calloop_wayland_source::WaylandSource;
|
use calloop_wayland_source::WaylandSource;
|
||||||
@ -16,7 +13,7 @@ use collections::HashMap;
|
|||||||
use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
|
use copypasta::wayland_clipboard::{create_clipboards_from_external, Clipboard, Primary};
|
||||||
use copypasta::ClipboardProvider;
|
use copypasta::ClipboardProvider;
|
||||||
use filedescriptor::Pipe;
|
use filedescriptor::Pipe;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
@ -26,9 +23,8 @@ use wayland_client::globals::{registry_queue_init, GlobalList, GlobalListContent
|
|||||||
use wayland_client::protocol::wl_callback::{self, WlCallback};
|
use wayland_client::protocol::wl_callback::{self, WlCallback};
|
||||||
use wayland_client::protocol::wl_data_device_manager::DndAction;
|
use wayland_client::protocol::wl_data_device_manager::DndAction;
|
||||||
use wayland_client::protocol::wl_pointer::AxisSource;
|
use wayland_client::protocol::wl_pointer::AxisSource;
|
||||||
use wayland_client::protocol::wl_seat::WlSeat;
|
|
||||||
use wayland_client::protocol::{
|
use wayland_client::protocol::{
|
||||||
wl_data_device, wl_data_device_manager, wl_data_offer, wl_data_source, wl_output, wl_region,
|
wl_data_device, wl_data_device_manager, wl_data_offer, wl_output, wl_region,
|
||||||
};
|
};
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
delegate_noop,
|
delegate_noop,
|
||||||
@ -38,7 +34,6 @@ use wayland_client::{
|
|||||||
},
|
},
|
||||||
Connection, Dispatch, Proxy, QueueHandle,
|
Connection, Dispatch, Proxy, QueueHandle,
|
||||||
};
|
};
|
||||||
use wayland_protocols::wp::cursor_shape::v1::client::wp_cursor_shape_device_v1::Shape;
|
|
||||||
use wayland_protocols::wp::cursor_shape::v1::client::{
|
use wayland_protocols::wp::cursor_shape::v1::client::{
|
||||||
wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,
|
wp_cursor_shape_device_v1, wp_cursor_shape_manager_v1,
|
||||||
};
|
};
|
||||||
@ -62,7 +57,8 @@ use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
|
|||||||
use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
|
use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS};
|
||||||
|
|
||||||
use super::super::{open_uri_internal, read_fd, DOUBLE_CLICK_INTERVAL};
|
use super::super::{open_uri_internal, read_fd, DOUBLE_CLICK_INTERVAL};
|
||||||
use super::window::{ImeInput, WaylandWindowState, WaylandWindowStatePtr};
|
use super::display::WaylandDisplay;
|
||||||
|
use super::window::{ImeInput, WaylandWindowStatePtr};
|
||||||
use crate::platform::linux::is_within_click_distance;
|
use crate::platform::linux::is_within_click_distance;
|
||||||
use crate::platform::linux::wayland::cursor::Cursor;
|
use crate::platform::linux::wayland::cursor::Cursor;
|
||||||
use crate::platform::linux::wayland::serial::{SerialKind, SerialTracker};
|
use crate::platform::linux::wayland::serial::{SerialKind, SerialTracker};
|
||||||
@ -71,7 +67,7 @@ use crate::platform::linux::xdg_desktop_portal::{Event as XDPEvent, XDPEventSour
|
|||||||
use crate::platform::linux::LinuxClient;
|
use crate::platform::linux::LinuxClient;
|
||||||
use crate::platform::PlatformWindow;
|
use crate::platform::PlatformWindow;
|
||||||
use crate::{
|
use crate::{
|
||||||
point, px, Bounds, FileDropEvent, ForegroundExecutor, MouseExitEvent, WindowAppearance,
|
point, px, size, Bounds, DevicePixels, FileDropEvent, ForegroundExecutor, MouseExitEvent, Size,
|
||||||
SCROLL_LINES,
|
SCROLL_LINES,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -143,11 +139,49 @@ impl Globals {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct InProgressOutput {
|
||||||
|
scale: Option<i32>,
|
||||||
|
position: Option<Point<DevicePixels>>,
|
||||||
|
size: Option<Size<DevicePixels>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InProgressOutput {
|
||||||
|
fn complete(&self) -> Option<Output> {
|
||||||
|
if let Some((position, size)) = self.position.zip(self.size) {
|
||||||
|
let scale = self.scale.unwrap_or(1);
|
||||||
|
Some(Output {
|
||||||
|
scale,
|
||||||
|
bounds: Bounds::new(position, size),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct Output {
|
||||||
|
pub scale: i32,
|
||||||
|
pub bounds: Bounds<DevicePixels>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Output {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
state.write_i32(self.scale);
|
||||||
|
state.write_i32(self.bounds.origin.x.0);
|
||||||
|
state.write_i32(self.bounds.origin.y.0);
|
||||||
|
state.write_i32(self.bounds.size.width.0);
|
||||||
|
state.write_i32(self.bounds.size.height.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct WaylandClientState {
|
pub(crate) struct WaylandClientState {
|
||||||
serial_tracker: SerialTracker,
|
serial_tracker: SerialTracker,
|
||||||
globals: Globals,
|
globals: Globals,
|
||||||
wl_seat: wl_seat::WlSeat, // todo(linux): multi-seat support
|
wl_seat: wl_seat::WlSeat, // TODO: Multi seat support
|
||||||
wl_pointer: Option<wl_pointer::WlPointer>,
|
wl_pointer: Option<wl_pointer::WlPointer>,
|
||||||
|
wl_keyboard: Option<wl_keyboard::WlKeyboard>,
|
||||||
cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
|
cursor_shape_device: Option<wp_cursor_shape_device_v1::WpCursorShapeDeviceV1>,
|
||||||
data_device: Option<wl_data_device::WlDataDevice>,
|
data_device: Option<wl_data_device::WlDataDevice>,
|
||||||
text_input: Option<zwp_text_input_v3::ZwpTextInputV3>,
|
text_input: Option<zwp_text_input_v3::ZwpTextInputV3>,
|
||||||
@ -156,15 +190,16 @@ pub(crate) struct WaylandClientState {
|
|||||||
// Surface to Window mapping
|
// Surface to Window mapping
|
||||||
windows: HashMap<ObjectId, WaylandWindowStatePtr>,
|
windows: HashMap<ObjectId, WaylandWindowStatePtr>,
|
||||||
// Output to scale mapping
|
// Output to scale mapping
|
||||||
output_scales: HashMap<ObjectId, i32>,
|
outputs: HashMap<ObjectId, Output>,
|
||||||
|
in_progress_outputs: HashMap<ObjectId, InProgressOutput>,
|
||||||
keymap_state: Option<xkb::State>,
|
keymap_state: Option<xkb::State>,
|
||||||
compose_state: Option<xkb::compose::State>,
|
compose_state: Option<xkb::compose::State>,
|
||||||
drag: DragState,
|
drag: DragState,
|
||||||
click: ClickState,
|
click: ClickState,
|
||||||
repeat: KeyRepeat,
|
repeat: KeyRepeat,
|
||||||
modifiers: Modifiers,
|
pub modifiers: Modifiers,
|
||||||
axis_source: AxisSource,
|
axis_source: AxisSource,
|
||||||
mouse_location: Option<Point<Pixels>>,
|
pub mouse_location: Option<Point<Pixels>>,
|
||||||
continuous_scroll_delta: Option<Point<Pixels>>,
|
continuous_scroll_delta: Option<Point<Pixels>>,
|
||||||
discrete_scroll_delta: Option<Point<f32>>,
|
discrete_scroll_delta: Option<Point<f32>>,
|
||||||
vertical_modifier: f32,
|
vertical_modifier: f32,
|
||||||
@ -210,7 +245,7 @@ pub(crate) struct KeyRepeat {
|
|||||||
pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
|
pub struct WaylandClientStatePtr(Weak<RefCell<WaylandClientState>>);
|
||||||
|
|
||||||
impl WaylandClientStatePtr {
|
impl WaylandClientStatePtr {
|
||||||
fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
|
pub fn get_client(&self) -> Rc<RefCell<WaylandClientState>> {
|
||||||
self.0
|
self.0
|
||||||
.upgrade()
|
.upgrade()
|
||||||
.expect("The pointer should always be valid when dispatching in wayland")
|
.expect("The pointer should always be valid when dispatching in wayland")
|
||||||
@ -328,7 +363,7 @@ impl WaylandClient {
|
|||||||
let qh = event_queue.handle();
|
let qh = event_queue.handle();
|
||||||
|
|
||||||
let mut seat: Option<wl_seat::WlSeat> = None;
|
let mut seat: Option<wl_seat::WlSeat> = None;
|
||||||
let mut outputs = HashMap::default();
|
let mut in_progress_outputs = HashMap::default();
|
||||||
globals.contents().with_list(|list| {
|
globals.contents().with_list(|list| {
|
||||||
for global in list {
|
for global in list {
|
||||||
match &global.interface[..] {
|
match &global.interface[..] {
|
||||||
@ -347,7 +382,7 @@ impl WaylandClient {
|
|||||||
&qh,
|
&qh,
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
outputs.insert(output.id(), 1);
|
in_progress_outputs.insert(output.id(), InProgressOutput::default());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -361,11 +396,13 @@ impl WaylandClient {
|
|||||||
let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
|
let (common, main_receiver) = LinuxCommon::new(event_loop.get_signal());
|
||||||
|
|
||||||
let handle = event_loop.handle();
|
let handle = event_loop.handle();
|
||||||
handle.insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| {
|
handle
|
||||||
|
.insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| {
|
||||||
if let calloop::channel::Event::Msg(runnable) = event {
|
if let calloop::channel::Event::Msg(runnable) = event {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let seat = seat.unwrap();
|
let seat = seat.unwrap();
|
||||||
let globals = Globals::new(
|
let globals = Globals::new(
|
||||||
@ -384,7 +421,8 @@ impl WaylandClient {
|
|||||||
|
|
||||||
let cursor = Cursor::new(&conn, &globals, 24);
|
let cursor = Cursor::new(&conn, &globals, 24);
|
||||||
|
|
||||||
handle.insert_source(XDPEventSource::new(&common.background_executor), {
|
handle
|
||||||
|
.insert_source(XDPEventSource::new(&common.background_executor), {
|
||||||
move |event, _, client| match event {
|
move |event, _, client| match event {
|
||||||
XDPEvent::WindowAppearance(appearance) => {
|
XDPEvent::WindowAppearance(appearance) => {
|
||||||
if let Some(client) = client.0.upgrade() {
|
if let Some(client) = client.0.upgrade() {
|
||||||
@ -398,19 +436,22 @@ impl WaylandClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut state = Rc::new(RefCell::new(WaylandClientState {
|
let mut state = Rc::new(RefCell::new(WaylandClientState {
|
||||||
serial_tracker: SerialTracker::new(),
|
serial_tracker: SerialTracker::new(),
|
||||||
globals,
|
globals,
|
||||||
wl_seat: seat,
|
wl_seat: seat,
|
||||||
wl_pointer: None,
|
wl_pointer: None,
|
||||||
|
wl_keyboard: None,
|
||||||
cursor_shape_device: None,
|
cursor_shape_device: None,
|
||||||
data_device,
|
data_device,
|
||||||
text_input: None,
|
text_input: None,
|
||||||
pre_edit_text: None,
|
pre_edit_text: None,
|
||||||
composing: false,
|
composing: false,
|
||||||
output_scales: outputs,
|
outputs: HashMap::default(),
|
||||||
|
in_progress_outputs,
|
||||||
windows: HashMap::default(),
|
windows: HashMap::default(),
|
||||||
common,
|
common,
|
||||||
keymap_state: None,
|
keymap_state: None,
|
||||||
@ -459,7 +500,9 @@ impl WaylandClient {
|
|||||||
pending_open_uri: None,
|
pending_open_uri: None,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
WaylandSource::new(conn, event_queue).insert(handle);
|
WaylandSource::new(conn, event_queue)
|
||||||
|
.insert(handle)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Self(state)
|
Self(state)
|
||||||
}
|
}
|
||||||
@ -467,11 +510,32 @@ impl WaylandClient {
|
|||||||
|
|
||||||
impl LinuxClient for WaylandClient {
|
impl LinuxClient for WaylandClient {
|
||||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||||
Vec::new()
|
self.0
|
||||||
|
.borrow()
|
||||||
|
.outputs
|
||||||
|
.iter()
|
||||||
|
.map(|(id, output)| {
|
||||||
|
Rc::new(WaylandDisplay {
|
||||||
|
id: id.clone(),
|
||||||
|
bounds: output.bounds,
|
||||||
|
}) as Rc<dyn PlatformDisplay>
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
unimplemented!()
|
self.0
|
||||||
|
.borrow()
|
||||||
|
.outputs
|
||||||
|
.iter()
|
||||||
|
.find_map(|(object_id, output)| {
|
||||||
|
(object_id.protocol_id() == id.0).then(|| {
|
||||||
|
Rc::new(WaylandDisplay {
|
||||||
|
id: object_id.clone(),
|
||||||
|
bounds: output.bounds,
|
||||||
|
}) as Rc<dyn PlatformDisplay>
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
fn primary_display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
@ -486,6 +550,7 @@ impl LinuxClient for WaylandClient {
|
|||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
|
|
||||||
let (window, surface_id) = WaylandWindow::new(
|
let (window, surface_id) = WaylandWindow::new(
|
||||||
|
handle,
|
||||||
state.globals.clone(),
|
state.globals.clone(),
|
||||||
WaylandClientStatePtr(Rc::downgrade(&self.0)),
|
WaylandClientStatePtr(Rc::downgrade(&self.0)),
|
||||||
params,
|
params,
|
||||||
@ -566,7 +631,8 @@ impl LinuxClient for WaylandClient {
|
|||||||
.primary
|
.primary
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_contents(item.text);
|
.set_contents(item.text)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_clipboard(&self, item: crate::ClipboardItem) {
|
fn write_to_clipboard(&self, item: crate::ClipboardItem) {
|
||||||
@ -575,7 +641,8 @@ impl LinuxClient for WaylandClient {
|
|||||||
.clipboard
|
.clipboard
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_contents(item.text);
|
.set_contents(item.text)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
||||||
@ -605,6 +672,14 @@ impl LinuxClient for WaylandClient {
|
|||||||
metadata: None,
|
metadata: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||||
|
self.0
|
||||||
|
.borrow_mut()
|
||||||
|
.keyboard_focused_window
|
||||||
|
.as_ref()
|
||||||
|
.map(|window| window.handle())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
|
impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStatePtr {
|
||||||
@ -626,18 +701,33 @@ impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for WaylandClientStat
|
|||||||
version,
|
version,
|
||||||
} => match &interface[..] {
|
} => match &interface[..] {
|
||||||
"wl_seat" => {
|
"wl_seat" => {
|
||||||
state.wl_pointer = None;
|
if let Some(wl_pointer) = state.wl_pointer.take() {
|
||||||
registry.bind::<wl_seat::WlSeat, _, _>(name, wl_seat_version(version), qh, ());
|
wl_pointer.release();
|
||||||
|
}
|
||||||
|
if let Some(wl_keyboard) = state.wl_keyboard.take() {
|
||||||
|
wl_keyboard.release();
|
||||||
|
}
|
||||||
|
state.wl_seat.release();
|
||||||
|
state.wl_seat = registry.bind::<wl_seat::WlSeat, _, _>(
|
||||||
|
name,
|
||||||
|
wl_seat_version(version),
|
||||||
|
qh,
|
||||||
|
(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
"wl_output" => {
|
"wl_output" => {
|
||||||
let output =
|
let output =
|
||||||
registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
|
registry.bind::<wl_output::WlOutput, _, _>(name, WL_OUTPUT_VERSION, qh, ());
|
||||||
|
|
||||||
state.output_scales.insert(output.id(), 1);
|
state
|
||||||
|
.in_progress_outputs
|
||||||
|
.insert(output.id(), InProgressOutput::default());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
wl_registry::Event::GlobalRemove { name: _ } => {}
|
wl_registry::Event::GlobalRemove { name: _ } => {
|
||||||
|
// TODO: handle global removal
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -667,7 +757,7 @@ impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
|
|||||||
event: wl_callback::Event,
|
event: wl_callback::Event,
|
||||||
surface_id: &ObjectId,
|
surface_id: &ObjectId,
|
||||||
_: &Connection,
|
_: &Connection,
|
||||||
qh: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
let client = state.get_client();
|
let client = state.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
@ -677,7 +767,7 @@ impl Dispatch<WlCallback, ObjectId> for WaylandClientStatePtr {
|
|||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
wl_callback::Event::Done { callback_data } => {
|
wl_callback::Event::Done { .. } => {
|
||||||
window.frame(true);
|
window.frame(true);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -707,10 +797,10 @@ impl Dispatch<wl_surface::WlSurface, ()> for WaylandClientStatePtr {
|
|||||||
let Some(window) = get_window(&mut state, &surface.id()) else {
|
let Some(window) = get_window(&mut state, &surface.id()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let scales = state.output_scales.clone();
|
let outputs = state.outputs.clone();
|
||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
window.handle_surface_event(event, scales);
|
window.handle_surface_event(event, outputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,13 +816,25 @@ impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
|
|||||||
let mut client = this.get_client();
|
let mut client = this.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
|
|
||||||
let Some(mut output_scale) = state.output_scales.get_mut(&output.id()) else {
|
let Some(mut in_progress_output) = state.in_progress_outputs.get_mut(&output.id()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
wl_output::Event::Scale { factor } => {
|
wl_output::Event::Scale { factor } => {
|
||||||
*output_scale = factor;
|
in_progress_output.scale = Some(factor);
|
||||||
|
}
|
||||||
|
wl_output::Event::Geometry { x, y, .. } => {
|
||||||
|
in_progress_output.position = Some(point(DevicePixels(x), DevicePixels(y)))
|
||||||
|
}
|
||||||
|
wl_output::Event::Mode { width, height, .. } => {
|
||||||
|
in_progress_output.size = Some(size(DevicePixels(width), DevicePixels(height)))
|
||||||
|
}
|
||||||
|
wl_output::Event::Done => {
|
||||||
|
if let Some(complete) = in_progress_output.complete() {
|
||||||
|
state.outputs.insert(output.id(), complete);
|
||||||
|
}
|
||||||
|
state.in_progress_outputs.remove(&output.id());
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -742,7 +844,7 @@ impl Dispatch<wl_output::WlOutput, ()> for WaylandClientStatePtr {
|
|||||||
impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
|
impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
xdg_surface: &xdg_surface::XdgSurface,
|
_: &xdg_surface::XdgSurface,
|
||||||
event: xdg_surface::Event,
|
event: xdg_surface::Event,
|
||||||
surface_id: &ObjectId,
|
surface_id: &ObjectId,
|
||||||
_: &Connection,
|
_: &Connection,
|
||||||
@ -761,7 +863,7 @@ impl Dispatch<xdg_surface::XdgSurface, ObjectId> for WaylandClientStatePtr {
|
|||||||
impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
|
impl Dispatch<xdg_toplevel::XdgToplevel, ObjectId> for WaylandClientStatePtr {
|
||||||
fn event(
|
fn event(
|
||||||
this: &mut Self,
|
this: &mut Self,
|
||||||
xdg_toplevel: &xdg_toplevel::XdgToplevel,
|
_: &xdg_toplevel::XdgToplevel,
|
||||||
event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
|
event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
|
||||||
surface_id: &ObjectId,
|
surface_id: &ObjectId,
|
||||||
_: &Connection,
|
_: &Connection,
|
||||||
@ -824,8 +926,8 @@ impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
|
|||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
seat: &wl_seat::WlSeat,
|
seat: &wl_seat::WlSeat,
|
||||||
event: wl_seat::Event,
|
event: wl_seat::Event,
|
||||||
data: &(),
|
_: &(),
|
||||||
conn: &Connection,
|
_: &Connection,
|
||||||
qh: &QueueHandle<Self>,
|
qh: &QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
if let wl_seat::Event::Capabilities {
|
if let wl_seat::Event::Capabilities {
|
||||||
@ -835,12 +937,19 @@ impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
|
|||||||
let client = state.get_client();
|
let client = state.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
if capabilities.contains(wl_seat::Capability::Keyboard) {
|
if capabilities.contains(wl_seat::Capability::Keyboard) {
|
||||||
seat.get_keyboard(qh, ());
|
let keyboard = seat.get_keyboard(qh, ());
|
||||||
|
|
||||||
state.text_input = state
|
state.text_input = state
|
||||||
.globals
|
.globals
|
||||||
.text_input_manager
|
.text_input_manager
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|text_input_manager| text_input_manager.get_text_input(&seat, qh, ()));
|
.map(|text_input_manager| text_input_manager.get_text_input(&seat, qh, ()));
|
||||||
|
|
||||||
|
if let Some(wl_keyboard) = &state.wl_keyboard {
|
||||||
|
wl_keyboard.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
state.wl_keyboard = Some(keyboard);
|
||||||
}
|
}
|
||||||
if capabilities.contains(wl_seat::Capability::Pointer) {
|
if capabilities.contains(wl_seat::Capability::Pointer) {
|
||||||
let pointer = seat.get_pointer(qh, ());
|
let pointer = seat.get_pointer(qh, ());
|
||||||
@ -849,6 +958,11 @@ impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
|
|||||||
.cursor_shape_manager
|
.cursor_shape_manager
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|cursor_shape_manager| cursor_shape_manager.get_pointer(&pointer, qh, ()));
|
.map(|cursor_shape_manager| cursor_shape_manager.get_pointer(&pointer, qh, ()));
|
||||||
|
|
||||||
|
if let Some(wl_pointer) = &state.wl_pointer {
|
||||||
|
wl_pointer.release();
|
||||||
|
}
|
||||||
|
|
||||||
state.wl_pointer = Some(pointer);
|
state.wl_pointer = Some(pointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -858,11 +972,11 @@ impl Dispatch<wl_seat::WlSeat, ()> for WaylandClientStatePtr {
|
|||||||
impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
|
impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
|
||||||
fn event(
|
fn event(
|
||||||
this: &mut Self,
|
this: &mut Self,
|
||||||
keyboard: &wl_keyboard::WlKeyboard,
|
_: &wl_keyboard::WlKeyboard,
|
||||||
event: wl_keyboard::Event,
|
event: wl_keyboard::Event,
|
||||||
data: &(),
|
_: &(),
|
||||||
conn: &Connection,
|
_: &Connection,
|
||||||
qh: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
let mut client = this.get_client();
|
let mut client = this.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
@ -1018,8 +1132,8 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
|
|||||||
state.compose_state = Some(compose);
|
state.compose_state = Some(compose);
|
||||||
}
|
}
|
||||||
let input = PlatformInput::KeyDown(KeyDownEvent {
|
let input = PlatformInput::KeyDown(KeyDownEvent {
|
||||||
keystroke: keystroke,
|
keystroke: keystroke.clone(),
|
||||||
is_held: false, // todo(linux)
|
is_held: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
state.repeat.current_id += 1;
|
state.repeat.current_id += 1;
|
||||||
@ -1030,8 +1144,11 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
|
|||||||
state
|
state
|
||||||
.loop_handle
|
.loop_handle
|
||||||
.insert_source(Timer::from_duration(state.repeat.delay), {
|
.insert_source(Timer::from_duration(state.repeat.delay), {
|
||||||
let input = input.clone();
|
let input = PlatformInput::KeyDown(KeyDownEvent {
|
||||||
move |event, _metadata, this| {
|
keystroke,
|
||||||
|
is_held: true,
|
||||||
|
});
|
||||||
|
move |_event, _metadata, this| {
|
||||||
let mut client = this.get_client();
|
let mut client = this.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
let is_repeating = id == state.repeat.current_id
|
let is_repeating = id == state.repeat.current_id
|
||||||
@ -1080,18 +1197,18 @@ impl Dispatch<zwp_text_input_v3::ZwpTextInputV3, ()> for WaylandClientStatePtr {
|
|||||||
this: &mut Self,
|
this: &mut Self,
|
||||||
text_input: &zwp_text_input_v3::ZwpTextInputV3,
|
text_input: &zwp_text_input_v3::ZwpTextInputV3,
|
||||||
event: <zwp_text_input_v3::ZwpTextInputV3 as Proxy>::Event,
|
event: <zwp_text_input_v3::ZwpTextInputV3 as Proxy>::Event,
|
||||||
data: &(),
|
_: &(),
|
||||||
conn: &Connection,
|
_: &Connection,
|
||||||
qhandle: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
let client = this.get_client();
|
let client = this.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
match event {
|
match event {
|
||||||
zwp_text_input_v3::Event::Enter { surface } => {
|
zwp_text_input_v3::Event::Enter { .. } => {
|
||||||
drop(state);
|
drop(state);
|
||||||
this.enable_ime();
|
this.enable_ime();
|
||||||
}
|
}
|
||||||
zwp_text_input_v3::Event::Leave { surface } => {
|
zwp_text_input_v3::Event::Leave { .. } => {
|
||||||
drop(state);
|
drop(state);
|
||||||
this.disable_ime();
|
this.disable_ime();
|
||||||
}
|
}
|
||||||
@ -1119,11 +1236,7 @@ impl Dispatch<zwp_text_input_v3::ZwpTextInputV3, ()> for WaylandClientStatePtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
zwp_text_input_v3::Event::PreeditString {
|
zwp_text_input_v3::Event::PreeditString { text, .. } => {
|
||||||
text,
|
|
||||||
cursor_begin,
|
|
||||||
cursor_end,
|
|
||||||
} => {
|
|
||||||
state.composing = true;
|
state.composing = true;
|
||||||
state.pre_edit_text = text;
|
state.pre_edit_text = text;
|
||||||
}
|
}
|
||||||
@ -1183,9 +1296,9 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
|
|||||||
this: &mut Self,
|
this: &mut Self,
|
||||||
wl_pointer: &wl_pointer::WlPointer,
|
wl_pointer: &wl_pointer::WlPointer,
|
||||||
event: wl_pointer::Event,
|
event: wl_pointer::Event,
|
||||||
data: &(),
|
_: &(),
|
||||||
conn: &Connection,
|
_: &Connection,
|
||||||
qh: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
) {
|
) {
|
||||||
let mut client = this.get_client();
|
let mut client = this.get_client();
|
||||||
let mut state = client.borrow_mut();
|
let mut state = client.borrow_mut();
|
||||||
@ -1220,7 +1333,7 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
|
|||||||
window.set_focused(true);
|
window.set_focused(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wl_pointer::Event::Leave { surface, .. } => {
|
wl_pointer::Event::Leave { .. } => {
|
||||||
if let Some(focused_window) = state.mouse_focused_window.clone() {
|
if let Some(focused_window) = state.mouse_focused_window.clone() {
|
||||||
let input = PlatformInput::MouseExited(MouseExitEvent {
|
let input = PlatformInput::MouseExited(MouseExitEvent {
|
||||||
position: state.mouse_location.unwrap(),
|
position: state.mouse_location.unwrap(),
|
||||||
@ -1237,7 +1350,6 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
wl_pointer::Event::Motion {
|
wl_pointer::Event::Motion {
|
||||||
time,
|
|
||||||
surface_x,
|
surface_x,
|
||||||
surface_y,
|
surface_y,
|
||||||
..
|
..
|
||||||
@ -1280,7 +1392,6 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
|
|||||||
wl_pointer::ButtonState::Pressed => {
|
wl_pointer::ButtonState::Pressed => {
|
||||||
if let Some(window) = state.keyboard_focused_window.clone() {
|
if let Some(window) = state.keyboard_focused_window.clone() {
|
||||||
if state.composing && state.text_input.is_some() {
|
if state.composing && state.text_input.is_some() {
|
||||||
let text_input = state.text_input.as_ref().unwrap();
|
|
||||||
drop(state);
|
drop(state);
|
||||||
// text_input_v3 don't have something like a reset function
|
// text_input_v3 don't have something like a reset function
|
||||||
this.disable_ime();
|
this.disable_ime();
|
||||||
@ -1351,7 +1462,6 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
|
|||||||
state.axis_source = axis_source;
|
state.axis_source = axis_source;
|
||||||
}
|
}
|
||||||
wl_pointer::Event::Axis {
|
wl_pointer::Event::Axis {
|
||||||
time,
|
|
||||||
axis: WEnum::Value(axis),
|
axis: WEnum::Value(axis),
|
||||||
value,
|
value,
|
||||||
..
|
..
|
||||||
@ -1364,13 +1474,10 @@ impl Dispatch<wl_pointer::WlPointer, ()> for WaylandClientStatePtr {
|
|||||||
wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
|
wl_pointer::Axis::HorizontalScroll => state.horizontal_modifier,
|
||||||
_ => 1.0,
|
_ => 1.0,
|
||||||
};
|
};
|
||||||
let supports_relative_direction =
|
|
||||||
wl_pointer.version() >= wl_pointer::EVT_AXIS_RELATIVE_DIRECTION_SINCE;
|
|
||||||
state.scroll_event_received = true;
|
state.scroll_event_received = true;
|
||||||
let scroll_delta = state
|
let scroll_delta = state
|
||||||
.continuous_scroll_delta
|
.continuous_scroll_delta
|
||||||
.get_or_insert(point(px(0.0), px(0.0)));
|
.get_or_insert(point(px(0.0), px(0.0)));
|
||||||
// TODO: Make nice feeling kinetic scrolling that integrates with the platform's scroll settings
|
|
||||||
let modifier = 3.0;
|
let modifier = 3.0;
|
||||||
match axis {
|
match axis {
|
||||||
wl_pointer::Axis::VerticalScroll => {
|
wl_pointer::Axis::VerticalScroll => {
|
||||||
|
@ -1,31 +1,36 @@
|
|||||||
use std::fmt::Debug;
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use wayland_backend::client::ObjectId;
|
||||||
|
|
||||||
use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay, Size};
|
use crate::{Bounds, DevicePixels, DisplayId, PlatformDisplay};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct WaylandDisplay {}
|
pub(crate) struct WaylandDisplay {
|
||||||
|
/// The ID of the wl_output object
|
||||||
|
pub id: ObjectId,
|
||||||
|
pub bounds: Bounds<DevicePixels>,
|
||||||
|
}
|
||||||
|
|
||||||
impl PlatformDisplay for WaylandDisplay {
|
impl Hash for WaylandDisplay {
|
||||||
// todo(linux)
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
fn id(&self) -> DisplayId {
|
self.id.hash(state);
|
||||||
DisplayId(123) // return some fake data so it doesn't panic
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
impl PlatformDisplay for WaylandDisplay {
|
||||||
fn uuid(&self) -> anyhow::Result<Uuid> {
|
fn id(&self) -> DisplayId {
|
||||||
Ok(Uuid::from_bytes([0; 16])) // return some fake data so it doesn't panic
|
DisplayId(self.id.protocol_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
fn uuid(&self) -> anyhow::Result<Uuid> {
|
||||||
fn bounds(&self) -> Bounds<DevicePixels> {
|
Err(anyhow::anyhow!("Display UUID is not supported on Wayland"))
|
||||||
Bounds {
|
}
|
||||||
origin: Default::default(),
|
|
||||||
size: Size {
|
fn bounds(&self) -> Bounds<DevicePixels> {
|
||||||
width: DevicePixels(1000),
|
self.bounds
|
||||||
height: DevicePixels(500),
|
|
||||||
},
|
|
||||||
} // return some fake data so it doesn't panic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||||
@ -14,15 +12,11 @@ pub(crate) enum SerialKind {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SerialData {
|
struct SerialData {
|
||||||
serial: u32,
|
serial: u32,
|
||||||
time: Instant,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SerialData {
|
impl SerialData {
|
||||||
fn new(value: u32) -> Self {
|
fn new(value: u32) -> Self {
|
||||||
Self {
|
Self { serial: value }
|
||||||
serial: value,
|
|
||||||
time: Instant::now(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,41 +46,4 @@ impl SerialTracker {
|
|||||||
.map(|serial_data| serial_data.serial)
|
.map(|serial_data| serial_data.serial)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the newest serial of any of the provided [`SerialKind`]
|
|
||||||
pub fn get_newest_of(&self, kinds: &[SerialKind]) -> u32 {
|
|
||||||
kinds
|
|
||||||
.iter()
|
|
||||||
.filter_map(|kind| self.serials.get(&kind))
|
|
||||||
.max_by_key(|serial_data| serial_data.time)
|
|
||||||
.map(|serial_data| serial_data.serial)
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_serial_tracker() {
|
|
||||||
let mut tracker = SerialTracker::new();
|
|
||||||
|
|
||||||
tracker.update(SerialKind::KeyPress, 100);
|
|
||||||
tracker.update(SerialKind::MousePress, 50);
|
|
||||||
tracker.update(SerialKind::MouseEnter, 300);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
tracker.get_newest_of(&[SerialKind::KeyPress, SerialKind::MousePress]),
|
|
||||||
50
|
|
||||||
);
|
|
||||||
assert_eq!(tracker.get(SerialKind::DataDevice), 0);
|
|
||||||
|
|
||||||
tracker.update(SerialKind::KeyPress, 2000);
|
|
||||||
assert_eq!(tracker.get(SerialKind::KeyPress), 2000);
|
|
||||||
assert_eq!(
|
|
||||||
tracker.get_newest_of(&[SerialKind::KeyPress, SerialKind::MousePress]),
|
|
||||||
2000
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,24 @@
|
|||||||
use std::any::Any;
|
|
||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::ops::{Deref, Range};
|
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use blade_graphics as gpu;
|
use blade_graphics as gpu;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::HashMap;
|
||||||
use futures::channel::oneshot::Receiver;
|
use futures::channel::oneshot::Receiver;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use raw_window_handle as rwh;
|
use raw_window_handle as rwh;
|
||||||
use wayland_backend::client::ObjectId;
|
use wayland_backend::client::ObjectId;
|
||||||
use wayland_client::protocol::wl_region::WlRegion;
|
|
||||||
use wayland_client::WEnum;
|
use wayland_client::WEnum;
|
||||||
use wayland_client::{protocol::wl_surface, Proxy};
|
use wayland_client::{protocol::wl_surface, Proxy};
|
||||||
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
|
use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;
|
||||||
use wayland_protocols::wp::viewporter::client::wp_viewport;
|
use wayland_protocols::wp::viewporter::client::wp_viewport;
|
||||||
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
|
use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;
|
||||||
use wayland_protocols::xdg::shell::client::xdg_surface;
|
use wayland_protocols::xdg::shell::client::xdg_surface;
|
||||||
use wayland_protocols::xdg::shell::client::xdg_toplevel::{self, WmCapabilities};
|
use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
|
||||||
use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blur_manager};
|
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
|
||||||
|
|
||||||
use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig};
|
use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig};
|
||||||
use crate::platform::linux::wayland::display::WaylandDisplay;
|
use crate::platform::linux::wayland::display::WaylandDisplay;
|
||||||
@ -29,9 +26,9 @@ use crate::platform::linux::wayland::serial::SerialKind;
|
|||||||
use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
|
use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use crate::{
|
use crate::{
|
||||||
px, size, Bounds, DevicePixels, Globals, Modifiers, Pixels, PlatformDisplay, PlatformInput,
|
px, size, AnyWindowHandle, Bounds, DevicePixels, Globals, Modifiers, Output, Pixels,
|
||||||
Point, PromptLevel, Size, WaylandClientStatePtr, WindowAppearance, WindowBackgroundAppearance,
|
PlatformDisplay, PlatformInput, Point, PromptLevel, Size, WaylandClientStatePtr,
|
||||||
WindowBounds, WindowParams,
|
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -75,7 +72,8 @@ pub struct WaylandWindowState {
|
|||||||
blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
|
blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,
|
||||||
toplevel: xdg_toplevel::XdgToplevel,
|
toplevel: xdg_toplevel::XdgToplevel,
|
||||||
viewport: Option<wp_viewport::WpViewport>,
|
viewport: Option<wp_viewport::WpViewport>,
|
||||||
outputs: HashSet<ObjectId>,
|
outputs: HashMap<ObjectId, Output>,
|
||||||
|
display: Option<(ObjectId, Output)>,
|
||||||
globals: Globals,
|
globals: Globals,
|
||||||
renderer: BladeRenderer,
|
renderer: BladeRenderer,
|
||||||
bounds: Bounds<u32>,
|
bounds: Bounds<u32>,
|
||||||
@ -86,7 +84,8 @@ pub struct WaylandWindowState {
|
|||||||
restore_bounds: Bounds<DevicePixels>,
|
restore_bounds: Bounds<DevicePixels>,
|
||||||
maximized: bool,
|
maximized: bool,
|
||||||
client: WaylandClientStatePtr,
|
client: WaylandClientStatePtr,
|
||||||
callbacks: Callbacks,
|
handle: AnyWindowHandle,
|
||||||
|
active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -98,6 +97,7 @@ pub struct WaylandWindowStatePtr {
|
|||||||
impl WaylandWindowState {
|
impl WaylandWindowState {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
|
handle: AnyWindowHandle,
|
||||||
surface: wl_surface::WlSurface,
|
surface: wl_surface::WlSurface,
|
||||||
xdg_surface: xdg_surface::XdgSurface,
|
xdg_surface: xdg_surface::XdgSurface,
|
||||||
toplevel: xdg_toplevel::XdgToplevel,
|
toplevel: xdg_toplevel::XdgToplevel,
|
||||||
@ -150,7 +150,8 @@ impl WaylandWindowState {
|
|||||||
toplevel,
|
toplevel,
|
||||||
viewport,
|
viewport,
|
||||||
globals,
|
globals,
|
||||||
outputs: HashSet::default(),
|
outputs: HashMap::default(),
|
||||||
|
display: None,
|
||||||
renderer: BladeRenderer::new(gpu, config),
|
renderer: BladeRenderer::new(gpu, config),
|
||||||
bounds,
|
bounds,
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
@ -159,9 +160,10 @@ impl WaylandWindowState {
|
|||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
restore_bounds: Bounds::default(),
|
restore_bounds: Bounds::default(),
|
||||||
maximized: false,
|
maximized: false,
|
||||||
callbacks: Callbacks::default(),
|
|
||||||
client,
|
client,
|
||||||
appearance,
|
appearance,
|
||||||
|
handle,
|
||||||
|
active: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,6 +219,7 @@ impl WaylandWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
handle: AnyWindowHandle,
|
||||||
globals: Globals,
|
globals: Globals,
|
||||||
client: WaylandClientStatePtr,
|
client: WaylandClientStatePtr,
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
@ -253,6 +256,7 @@ impl WaylandWindow {
|
|||||||
|
|
||||||
let this = Self(WaylandWindowStatePtr {
|
let this = Self(WaylandWindowStatePtr {
|
||||||
state: Rc::new(RefCell::new(WaylandWindowState::new(
|
state: Rc::new(RefCell::new(WaylandWindowState::new(
|
||||||
|
handle,
|
||||||
surface.clone(),
|
surface.clone(),
|
||||||
xdg_surface,
|
xdg_surface,
|
||||||
toplevel,
|
toplevel,
|
||||||
@ -274,6 +278,10 @@ impl WaylandWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WaylandWindowStatePtr {
|
impl WaylandWindowStatePtr {
|
||||||
|
pub fn handle(&self) -> AnyWindowHandle {
|
||||||
|
self.state.borrow().handle
|
||||||
|
}
|
||||||
|
|
||||||
pub fn surface(&self) -> wl_surface::WlSurface {
|
pub fn surface(&self) -> wl_surface::WlSurface {
|
||||||
self.state.borrow().surface.clone()
|
self.state.borrow().surface.clone()
|
||||||
}
|
}
|
||||||
@ -381,7 +389,7 @@ impl WaylandWindowStatePtr {
|
|||||||
pub fn handle_surface_event(
|
pub fn handle_surface_event(
|
||||||
&self,
|
&self,
|
||||||
event: wl_surface::Event,
|
event: wl_surface::Event,
|
||||||
output_scales: HashMap<ObjectId, i32>,
|
outputs: HashMap<ObjectId, Output>,
|
||||||
) {
|
) {
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
|
|
||||||
@ -396,15 +404,15 @@ impl WaylandWindowStatePtr {
|
|||||||
if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
|
if state.surface.version() >= wl_surface::EVT_PREFERRED_BUFFER_SCALE_SINCE {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let id = output.id();
|
||||||
|
|
||||||
state.outputs.insert(output.id());
|
let Some(output) = outputs.get(&id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let mut scale = 1;
|
state.outputs.insert(id, *output);
|
||||||
for output in state.outputs.iter() {
|
|
||||||
if let Some(s) = output_scales.get(output) {
|
let scale = primary_output_scale(&mut state);
|
||||||
scale = scale.max(*s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.surface.set_buffer_scale(scale);
|
state.surface.set_buffer_scale(scale);
|
||||||
drop(state);
|
drop(state);
|
||||||
@ -418,12 +426,7 @@ impl WaylandWindowStatePtr {
|
|||||||
|
|
||||||
state.outputs.remove(&output.id());
|
state.outputs.remove(&output.id());
|
||||||
|
|
||||||
let mut scale = 1;
|
let scale = primary_output_scale(&mut state);
|
||||||
for output in state.outputs.iter() {
|
|
||||||
if let Some(s) = output_scales.get(output) {
|
|
||||||
scale = scale.max(*s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state.surface.set_buffer_scale(scale);
|
state.surface.set_buffer_scale(scale);
|
||||||
drop(state);
|
drop(state);
|
||||||
@ -577,6 +580,7 @@ impl WaylandWindowStatePtr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_focused(&self, focus: bool) {
|
pub fn set_focused(&self, focus: bool) {
|
||||||
|
self.state.borrow_mut().active = focus;
|
||||||
if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
|
if let Some(ref mut fun) = self.callbacks.borrow_mut().active_status_change {
|
||||||
fun(focus);
|
fun(focus);
|
||||||
}
|
}
|
||||||
@ -592,6 +596,23 @@ impl WaylandWindowStatePtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn primary_output_scale(state: &mut RefMut<WaylandWindowState>) -> i32 {
|
||||||
|
let mut scale = 1;
|
||||||
|
let mut current_output = state.display.take();
|
||||||
|
for (id, output) in state.outputs.iter() {
|
||||||
|
if let Some((_, output_data)) = ¤t_output {
|
||||||
|
if output.scale > output_data.scale {
|
||||||
|
current_output = Some((id.clone(), *output));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_output = Some((id.clone(), *output));
|
||||||
|
}
|
||||||
|
scale = scale.max(output.scale);
|
||||||
|
}
|
||||||
|
state.display = current_output;
|
||||||
|
scale
|
||||||
|
}
|
||||||
|
|
||||||
impl rwh::HasWindowHandle for WaylandWindow {
|
impl rwh::HasWindowHandle for WaylandWindow {
|
||||||
fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
|
fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
@ -612,8 +633,6 @@ impl PlatformWindow for WaylandWindow {
|
|||||||
self.borrow().maximized
|
self.borrow().maximized
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
// check if it is right
|
|
||||||
fn window_bounds(&self) -> WindowBounds {
|
fn window_bounds(&self) -> WindowBounds {
|
||||||
let state = self.borrow();
|
let state = self.borrow();
|
||||||
if state.fullscreen {
|
if state.fullscreen {
|
||||||
@ -641,19 +660,26 @@ impl PlatformWindow for WaylandWindow {
|
|||||||
self.borrow().appearance
|
self.borrow().appearance
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
fn display(&self) -> Rc<dyn PlatformDisplay> {
|
self.borrow().display.as_ref().map(|(id, display)| {
|
||||||
Rc::new(WaylandDisplay {})
|
Rc::new(WaylandDisplay {
|
||||||
|
id: id.clone(),
|
||||||
|
bounds: display.bounds,
|
||||||
|
}) as Rc<dyn PlatformDisplay>
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
fn mouse_position(&self) -> Point<Pixels> {
|
fn mouse_position(&self) -> Point<Pixels> {
|
||||||
Point::default()
|
self.borrow()
|
||||||
|
.client
|
||||||
|
.get_client()
|
||||||
|
.borrow()
|
||||||
|
.mouse_location
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
fn modifiers(&self) -> Modifiers {
|
fn modifiers(&self) -> Modifiers {
|
||||||
crate::Modifiers::default()
|
self.borrow().client.get_client().borrow().modifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
||||||
@ -666,21 +692,20 @@ impl PlatformWindow for WaylandWindow {
|
|||||||
|
|
||||||
fn prompt(
|
fn prompt(
|
||||||
&self,
|
&self,
|
||||||
level: PromptLevel,
|
_level: PromptLevel,
|
||||||
msg: &str,
|
_msg: &str,
|
||||||
detail: Option<&str>,
|
_detail: Option<&str>,
|
||||||
answers: &[&str],
|
_answers: &[&str],
|
||||||
) -> Option<Receiver<usize>> {
|
) -> Option<Receiver<usize>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate(&self) {
|
fn activate(&self) {
|
||||||
// todo(linux)
|
log::info!("Wayland does not support this API");
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
fn is_active(&self) -> bool {
|
fn is_active(&self) -> bool {
|
||||||
false
|
self.borrow().active
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_title(&mut self, title: &str) {
|
fn set_title(&mut self, title: &str) {
|
||||||
@ -712,8 +737,8 @@ impl PlatformWindow for WaylandWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref blur_manager) = state.globals.blur_manager {
|
if let Some(ref blur_manager) = state.globals.blur_manager {
|
||||||
if (background_appearance == WindowBackgroundAppearance::Blurred) {
|
if background_appearance == WindowBackgroundAppearance::Blurred {
|
||||||
if (state.blur.is_none()) {
|
if state.blur.is_none() {
|
||||||
let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
|
let blur = blur_manager.create(&state.surface, &state.globals.qh, ());
|
||||||
blur.set_region(Some(®ion));
|
blur.set_region(Some(®ion));
|
||||||
state.blur = Some(blur);
|
state.blur = Some(blur);
|
||||||
@ -731,12 +756,12 @@ impl PlatformWindow for WaylandWindow {
|
|||||||
region.destroy();
|
region.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_edited(&mut self, edited: bool) {
|
fn set_edited(&mut self, _edited: bool) {
|
||||||
// todo(linux)
|
log::info!("ignoring macOS specific set_edited");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_character_palette(&self) {
|
fn show_character_palette(&self) {
|
||||||
// todo(linux)
|
log::info!("ignoring macOS specific show_character_palette");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn minimize(&self) {
|
fn minimize(&self) {
|
||||||
|
@ -2,7 +2,6 @@ use std::cell::RefCell;
|
|||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
use std::sync::OnceLock;
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use calloop::generic::{FdWrapper, Generic};
|
use calloop::generic::{FdWrapper, Generic};
|
||||||
@ -11,30 +10,29 @@ use calloop::{channel, EventLoop, LoopHandle, RegistrationToken};
|
|||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext};
|
use copypasta::x11_clipboard::{Clipboard, Primary, X11ClipboardContext};
|
||||||
use copypasta::ClipboardProvider;
|
use copypasta::ClipboardProvider;
|
||||||
use parking_lot::Mutex;
|
|
||||||
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use x11rb::connection::{Connection, RequestConnection};
|
use x11rb::connection::{Connection, RequestConnection};
|
||||||
use x11rb::cursor;
|
use x11rb::cursor;
|
||||||
use x11rb::errors::ConnectionError;
|
use x11rb::errors::ConnectionError;
|
||||||
use x11rb::protocol::randr::ConnectionExt as _;
|
use x11rb::protocol::randr::ConnectionExt as _;
|
||||||
use x11rb::protocol::xinput::{ConnectionExt, ScrollClass};
|
use x11rb::protocol::xinput::ConnectionExt;
|
||||||
use x11rb::protocol::xkb::ConnectionExt as _;
|
use x11rb::protocol::xkb::ConnectionExt as _;
|
||||||
use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _};
|
use x11rb::protocol::xproto::{ChangeWindowAttributesAux, ConnectionExt as _};
|
||||||
use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event};
|
use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event};
|
||||||
use x11rb::resource_manager::Database;
|
use x11rb::resource_manager::Database;
|
||||||
use x11rb::xcb_ffi::XCBConnection;
|
use x11rb::xcb_ffi::XCBConnection;
|
||||||
use xim::{x11rb::X11rbClient, Client};
|
use xim::{x11rb::X11rbClient, Client};
|
||||||
use xim::{AHashMap, AttributeName, ClientHandler, InputStyle};
|
use xim::{AttributeName, InputStyle};
|
||||||
use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
|
use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION};
|
||||||
use xkbcommon::xkb as xkbc;
|
use xkbcommon::xkb as xkbc;
|
||||||
|
|
||||||
use crate::platform::linux::LinuxClient;
|
use crate::platform::linux::LinuxClient;
|
||||||
use crate::platform::{LinuxCommon, PlatformWindow, WaylandClientState};
|
use crate::platform::{LinuxCommon, PlatformWindow};
|
||||||
use crate::{
|
use crate::{
|
||||||
modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, CursorStyle, DisplayId,
|
modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, CursorStyle, DisplayId,
|
||||||
ForegroundExecutor, Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay,
|
Keystroke, Modifiers, ModifiersChangedEvent, Pixels, PlatformDisplay, PlatformInput, Point,
|
||||||
PlatformInput, Point, ScrollDelta, Size, TouchPhase, WindowAppearance, WindowParams, X11Window,
|
ScrollDelta, Size, TouchPhase, WindowParams, X11Window,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -54,6 +52,12 @@ pub(crate) struct WindowRef {
|
|||||||
refresh_event_token: RegistrationToken,
|
refresh_event_token: RegistrationToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl WindowRef {
|
||||||
|
pub fn handle(&self) -> AnyWindowHandle {
|
||||||
|
self.window.state.borrow().handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deref for WindowRef {
|
impl Deref for WindowRef {
|
||||||
type Target = X11WindowStatePtr;
|
type Target = X11WindowStatePtr;
|
||||||
|
|
||||||
@ -104,13 +108,14 @@ pub struct X11ClientState {
|
|||||||
|
|
||||||
pub(crate) xcb_connection: Rc<XCBConnection>,
|
pub(crate) xcb_connection: Rc<XCBConnection>,
|
||||||
pub(crate) x_root_index: usize,
|
pub(crate) x_root_index: usize,
|
||||||
pub(crate) resource_database: Database,
|
pub(crate) _resource_database: Database,
|
||||||
pub(crate) atoms: XcbAtoms,
|
pub(crate) atoms: XcbAtoms,
|
||||||
pub(crate) windows: HashMap<xproto::Window, WindowRef>,
|
pub(crate) windows: HashMap<xproto::Window, WindowRef>,
|
||||||
pub(crate) focused_window: Option<xproto::Window>,
|
pub(crate) focused_window: Option<xproto::Window>,
|
||||||
pub(crate) xkb: xkbc::State,
|
pub(crate) xkb: xkbc::State,
|
||||||
pub(crate) ximc: Option<X11rbClient<Rc<XCBConnection>>>,
|
pub(crate) ximc: Option<X11rbClient<Rc<XCBConnection>>>,
|
||||||
pub(crate) xim_handler: Option<XimHandler>,
|
pub(crate) xim_handler: Option<XimHandler>,
|
||||||
|
pub modifiers: Modifiers,
|
||||||
|
|
||||||
pub(crate) compose_state: xkbc::compose::State,
|
pub(crate) compose_state: xkbc::compose::State,
|
||||||
pub(crate) pre_edit_text: Option<String>,
|
pub(crate) pre_edit_text: Option<String>,
|
||||||
@ -159,11 +164,13 @@ impl X11Client {
|
|||||||
|
|
||||||
let handle = event_loop.handle();
|
let handle = event_loop.handle();
|
||||||
|
|
||||||
handle.insert_source(main_receiver, |event, _, _: &mut X11Client| {
|
handle
|
||||||
|
.insert_source(main_receiver, |event, _, _: &mut X11Client| {
|
||||||
if let calloop::channel::Event::Msg(runnable) = event {
|
if let calloop::channel::Event::Msg(runnable) = event {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap();
|
let (xcb_connection, x_root_index) = XCBConnection::connect(None).unwrap();
|
||||||
xcb_connection
|
xcb_connection
|
||||||
@ -248,23 +255,7 @@ impl X11Client {
|
|||||||
xkbc::compose::State::new(&table, xkbc::compose::STATE_NO_FLAGS)
|
xkbc::compose::State::new(&table, xkbc::compose::STATE_NO_FLAGS)
|
||||||
};
|
};
|
||||||
|
|
||||||
let screen = xcb_connection.setup().roots.get(x_root_index).unwrap();
|
let resource_database = x11rb::resource_manager::new_from_default(&xcb_connection).unwrap();
|
||||||
|
|
||||||
// Values from `Database::GET_RESOURCE_DATABASE`
|
|
||||||
let resource_manager = xcb_connection
|
|
||||||
.get_property(
|
|
||||||
false,
|
|
||||||
screen.root,
|
|
||||||
xproto::AtomEnum::RESOURCE_MANAGER,
|
|
||||||
xproto::AtomEnum::STRING,
|
|
||||||
0,
|
|
||||||
100_000_000,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let resource_manager = resource_manager.reply().unwrap();
|
|
||||||
|
|
||||||
// todo(linux): read hostname
|
|
||||||
let resource_database = Database::new_from_default(&resource_manager, "HOSTNAME".into());
|
|
||||||
|
|
||||||
let scale_factor = resource_database
|
let scale_factor = resource_database
|
||||||
.get_value("Xft.dpi", "Xft.dpi")
|
.get_value("Xft.dpi", "Xft.dpi")
|
||||||
@ -345,7 +336,7 @@ impl X11Client {
|
|||||||
.insert_source(xim_rx, {
|
.insert_source(xim_rx, {
|
||||||
move |chan_event, _, client| match chan_event {
|
move |chan_event, _, client| match chan_event {
|
||||||
channel::Event::Msg(xim_event) => {
|
channel::Event::Msg(xim_event) => {
|
||||||
match (xim_event) {
|
match xim_event {
|
||||||
XimCallbackEvent::XimXEvent(event) => {
|
XimCallbackEvent::XimXEvent(event) => {
|
||||||
client.handle_event(event);
|
client.handle_event(event);
|
||||||
}
|
}
|
||||||
@ -363,7 +354,8 @@ impl X11Client {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.expect("Failed to initialize XIM event source");
|
.expect("Failed to initialize XIM event source");
|
||||||
handle.insert_source(XDPEventSource::new(&common.background_executor), {
|
handle
|
||||||
|
.insert_source(XDPEventSource::new(&common.background_executor), {
|
||||||
move |event, _, client| match event {
|
move |event, _, client| match event {
|
||||||
XDPEvent::WindowAppearance(appearance) => {
|
XDPEvent::WindowAppearance(appearance) => {
|
||||||
client.with_common(|common| common.appearance = appearance);
|
client.with_common(|common| common.appearance = appearance);
|
||||||
@ -372,9 +364,11 @@ impl X11Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
X11Client(Rc::new(RefCell::new(X11ClientState {
|
X11Client(Rc::new(RefCell::new(X11ClientState {
|
||||||
|
modifiers: Modifiers::default(),
|
||||||
event_loop: Some(event_loop),
|
event_loop: Some(event_loop),
|
||||||
loop_handle: handle,
|
loop_handle: handle,
|
||||||
common,
|
common,
|
||||||
@ -385,7 +379,7 @@ impl X11Client {
|
|||||||
|
|
||||||
xcb_connection,
|
xcb_connection,
|
||||||
x_root_index,
|
x_root_index,
|
||||||
resource_database,
|
_resource_database: resource_database,
|
||||||
atoms,
|
atoms,
|
||||||
windows: HashMap::default(),
|
windows: HashMap::default(),
|
||||||
focused_window: None,
|
focused_window: None,
|
||||||
@ -446,7 +440,8 @@ impl X11Client {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ximc.create_ic(xim_handler.im_id, ic_attributes.build());
|
ximc.create_ic(xim_handler.im_id, ic_attributes.build())
|
||||||
|
.ok();
|
||||||
state = self.0.borrow_mut();
|
state = self.0.borrow_mut();
|
||||||
state.xim_handler = Some(xim_handler);
|
state.xim_handler = Some(xim_handler);
|
||||||
state.ximc = Some(ximc);
|
state.ximc = Some(ximc);
|
||||||
@ -457,7 +452,7 @@ impl X11Client {
|
|||||||
state.composing = false;
|
state.composing = false;
|
||||||
if let Some(mut ximc) = state.ximc.take() {
|
if let Some(mut ximc) = state.ximc.take() {
|
||||||
let xim_handler = state.xim_handler.as_ref().unwrap();
|
let xim_handler = state.xim_handler.as_ref().unwrap();
|
||||||
ximc.destroy_ic(xim_handler.im_id, xim_handler.ic_id);
|
ximc.destroy_ic(xim_handler.im_id, xim_handler.ic_id).ok();
|
||||||
state.ximc = Some(ximc);
|
state.ximc = Some(ximc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -535,6 +530,7 @@ impl X11Client {
|
|||||||
);
|
);
|
||||||
let modifiers = Modifiers::from_xkb(&state.xkb);
|
let modifiers = Modifiers::from_xkb(&state.xkb);
|
||||||
let focused_window_id = state.focused_window?;
|
let focused_window_id = state.focused_window?;
|
||||||
|
state.modifiers = modifiers;
|
||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
let focused_window = self.get_window(focused_window_id)?;
|
let focused_window = self.get_window(focused_window_id)?;
|
||||||
@ -547,6 +543,8 @@ impl X11Client {
|
|||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
|
|
||||||
let modifiers = modifiers_from_state(event.state);
|
let modifiers = modifiers_from_state(event.state);
|
||||||
|
state.modifiers = modifiers;
|
||||||
|
|
||||||
let keystroke = {
|
let keystroke = {
|
||||||
let code = event.detail.into();
|
let code = event.detail.into();
|
||||||
let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
let mut keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
||||||
@ -600,6 +598,8 @@ impl X11Client {
|
|||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
|
|
||||||
let modifiers = modifiers_from_state(event.state);
|
let modifiers = modifiers_from_state(event.state);
|
||||||
|
state.modifiers = modifiers;
|
||||||
|
|
||||||
let keystroke = {
|
let keystroke = {
|
||||||
let code = event.detail.into();
|
let code = event.detail.into();
|
||||||
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
let keystroke = crate::Keystroke::from_xkb(&state.xkb, modifiers, code);
|
||||||
@ -618,6 +618,8 @@ impl X11Client {
|
|||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
|
|
||||||
let modifiers = modifiers_from_xinput_info(event.mods);
|
let modifiers = modifiers_from_xinput_info(event.mods);
|
||||||
|
state.modifiers = modifiers;
|
||||||
|
|
||||||
let position = point(
|
let position = point(
|
||||||
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
@ -664,8 +666,10 @@ impl X11Client {
|
|||||||
}
|
}
|
||||||
Event::XinputButtonRelease(event) => {
|
Event::XinputButtonRelease(event) => {
|
||||||
let window = self.get_window(event.event)?;
|
let window = self.get_window(event.event)?;
|
||||||
let state = self.0.borrow();
|
let mut state = self.0.borrow_mut();
|
||||||
let modifiers = modifiers_from_xinput_info(event.mods);
|
let modifiers = modifiers_from_xinput_info(event.mods);
|
||||||
|
state.modifiers = modifiers;
|
||||||
|
|
||||||
let position = point(
|
let position = point(
|
||||||
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
@ -683,14 +687,15 @@ impl X11Client {
|
|||||||
}
|
}
|
||||||
Event::XinputMotion(event) => {
|
Event::XinputMotion(event) => {
|
||||||
let window = self.get_window(event.event)?;
|
let window = self.get_window(event.event)?;
|
||||||
let state = self.0.borrow();
|
let mut state = self.0.borrow_mut();
|
||||||
let pressed_button = pressed_button_from_mask(event.button_mask[0]);
|
let pressed_button = pressed_button_from_mask(event.button_mask[0]);
|
||||||
let position = point(
|
let position = point(
|
||||||
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
);
|
);
|
||||||
drop(state);
|
|
||||||
let modifiers = modifiers_from_xinput_info(event.mods);
|
let modifiers = modifiers_from_xinput_info(event.mods);
|
||||||
|
state.modifiers = modifiers;
|
||||||
|
drop(state);
|
||||||
|
|
||||||
let axisvalues = event
|
let axisvalues = event
|
||||||
.axisvalues
|
.axisvalues
|
||||||
@ -766,13 +771,14 @@ impl X11Client {
|
|||||||
self.0.borrow_mut().scroll_y = None;
|
self.0.borrow_mut().scroll_y = None;
|
||||||
|
|
||||||
let window = self.get_window(event.event)?;
|
let window = self.get_window(event.event)?;
|
||||||
let state = self.0.borrow();
|
let mut state = self.0.borrow_mut();
|
||||||
let pressed_button = pressed_button_from_mask(event.buttons[0]);
|
let pressed_button = pressed_button_from_mask(event.buttons[0]);
|
||||||
let position = point(
|
let position = point(
|
||||||
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_x as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
px(event.event_y as f32 / u16::MAX as f32 / state.scale_factor),
|
||||||
);
|
);
|
||||||
let modifiers = modifiers_from_xinput_info(event.mods);
|
let modifiers = modifiers_from_xinput_info(event.mods);
|
||||||
|
state.modifiers = modifiers;
|
||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
|
window.handle_input(PlatformInput::MouseExited(crate::MouseExitEvent {
|
||||||
@ -855,7 +861,8 @@ impl X11Client {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes);
|
ximc.set_ic_values(xim_handler.im_id, xim_handler.ic_id, ic_attributes)
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
state.ximc = Some(ximc);
|
state.ximc = Some(ximc);
|
||||||
@ -904,13 +911,14 @@ impl LinuxClient for X11Client {
|
|||||||
|
|
||||||
fn open_window(
|
fn open_window(
|
||||||
&self,
|
&self,
|
||||||
_handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
) -> Box<dyn PlatformWindow> {
|
) -> Box<dyn PlatformWindow> {
|
||||||
let mut state = self.0.borrow_mut();
|
let mut state = self.0.borrow_mut();
|
||||||
let x_window = state.xcb_connection.generate_id().unwrap();
|
let x_window = state.xcb_connection.generate_id().unwrap();
|
||||||
|
|
||||||
let window = X11Window::new(
|
let window = X11Window::new(
|
||||||
|
handle,
|
||||||
X11ClientStatePtr(Rc::downgrade(&self.0)),
|
X11ClientStatePtr(Rc::downgrade(&self.0)),
|
||||||
state.common.foreground_executor.clone(),
|
state.common.foreground_executor.clone(),
|
||||||
params,
|
params,
|
||||||
@ -1034,11 +1042,11 @@ impl LinuxClient for X11Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_primary(&self, item: crate::ClipboardItem) {
|
fn write_to_primary(&self, item: crate::ClipboardItem) {
|
||||||
self.0.borrow_mut().primary.set_contents(item.text);
|
self.0.borrow_mut().primary.set_contents(item.text).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_clipboard(&self, item: crate::ClipboardItem) {
|
fn write_to_clipboard(&self, item: crate::ClipboardItem) {
|
||||||
self.0.borrow_mut().clipboard.set_contents(item.text);
|
self.0.borrow_mut().clipboard.set_contents(item.text).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
fn read_from_primary(&self) -> Option<crate::ClipboardItem> {
|
||||||
@ -1075,6 +1083,16 @@ impl LinuxClient for X11Client {
|
|||||||
|
|
||||||
event_loop.run(None, &mut self.clone(), |_| {}).log_err();
|
event_loop.run(None, &mut self.clone(), |_| {}).log_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||||
|
let state = self.0.borrow();
|
||||||
|
state.focused_window.and_then(|focused_window| {
|
||||||
|
state
|
||||||
|
.windows
|
||||||
|
.get(&focused_window)
|
||||||
|
.map(|window| window.handle())
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adatpted from:
|
// Adatpted from:
|
||||||
|
@ -1,40 +1,29 @@
|
|||||||
// todo(linux): remove
|
|
||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
platform::blade::{BladeRenderer, BladeSurfaceConfig},
|
||||||
size, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels, Platform, PlatformAtlas,
|
size, AnyWindowHandle, Bounds, DevicePixels, ForegroundExecutor, Modifiers, Pixels,
|
||||||
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel,
|
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||||
Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowOptions,
|
PromptLevel, Scene, Size, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
|
||||||
WindowParams, X11Client, X11ClientState, X11ClientStatePtr,
|
WindowParams, X11ClientStatePtr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use blade_graphics as gpu;
|
use blade_graphics as gpu;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use raw_window_handle as rwh;
|
use raw_window_handle as rwh;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use x11rb::{
|
use x11rb::{
|
||||||
connection::{Connection as _, RequestConnection as _},
|
connection::Connection,
|
||||||
protocol::{
|
protocol::{
|
||||||
render,
|
|
||||||
xinput::{self, ConnectionExt as _},
|
xinput::{self, ConnectionExt as _},
|
||||||
xproto::{
|
xproto::{
|
||||||
self, Atom, ClientMessageEvent, ConnectionExt as _, CreateWindowAux, EventMask,
|
self, ClientMessageEvent, ConnectionExt as _, EventMask, TranslateCoordinatesReply,
|
||||||
TranslateCoordinatesReply,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
resource_manager::Database,
|
|
||||||
wrapper::ConnectionExt as _,
|
wrapper::ConnectionExt as _,
|
||||||
xcb_ffi::XCBConnection,
|
xcb_ffi::XCBConnection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::rc::Weak;
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell, RefMut},
|
cell::RefCell,
|
||||||
collections::HashMap,
|
|
||||||
ffi::c_void,
|
ffi::c_void,
|
||||||
iter::Zip,
|
|
||||||
mem,
|
mem,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
ops::Div,
|
ops::Div,
|
||||||
@ -165,29 +154,29 @@ pub struct Callbacks {
|
|||||||
appearance_changed: Option<Box<dyn FnMut()>>,
|
appearance_changed: Option<Box<dyn FnMut()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct X11WindowState {
|
pub struct X11WindowState {
|
||||||
client: X11ClientStatePtr,
|
client: X11ClientStatePtr,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
atoms: XcbAtoms,
|
atoms: XcbAtoms,
|
||||||
x_root_window: xproto::Window,
|
x_root_window: xproto::Window,
|
||||||
raw: RawWindow,
|
_raw: RawWindow,
|
||||||
bounds: Bounds<i32>,
|
bounds: Bounds<i32>,
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
renderer: BladeRenderer,
|
renderer: BladeRenderer,
|
||||||
display: Rc<dyn PlatformDisplay>,
|
display: Rc<dyn PlatformDisplay>,
|
||||||
input_handler: Option<PlatformInputHandler>,
|
input_handler: Option<PlatformInputHandler>,
|
||||||
appearance: WindowAppearance,
|
appearance: WindowAppearance,
|
||||||
|
pub handle: AnyWindowHandle,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct X11WindowStatePtr {
|
pub(crate) struct X11WindowStatePtr {
|
||||||
pub(crate) state: Rc<RefCell<X11WindowState>>,
|
pub state: Rc<RefCell<X11WindowState>>,
|
||||||
pub(crate) callbacks: Rc<RefCell<Callbacks>>,
|
pub(crate) callbacks: Rc<RefCell<Callbacks>>,
|
||||||
xcb_connection: Rc<XCBConnection>,
|
xcb_connection: Rc<XCBConnection>,
|
||||||
x_window: xproto::Window,
|
x_window: xproto::Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux): Remove other RawWindowHandle implementation
|
|
||||||
impl rwh::HasWindowHandle for RawWindow {
|
impl rwh::HasWindowHandle for RawWindow {
|
||||||
fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
|
fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
|
||||||
let non_zero = NonZeroU32::new(self.window_id).unwrap();
|
let non_zero = NonZeroU32::new(self.window_id).unwrap();
|
||||||
@ -218,6 +207,7 @@ impl rwh::HasDisplayHandle for X11Window {
|
|||||||
impl X11WindowState {
|
impl X11WindowState {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
handle: AnyWindowHandle,
|
||||||
client: X11ClientStatePtr,
|
client: X11ClientStatePtr,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
@ -372,7 +362,7 @@ impl X11WindowState {
|
|||||||
client,
|
client,
|
||||||
executor,
|
executor,
|
||||||
display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()),
|
display: Rc::new(X11Display::new(xcb_connection, x_screen_index).unwrap()),
|
||||||
raw,
|
_raw: raw,
|
||||||
x_root_window: visual_set.root,
|
x_root_window: visual_set.root,
|
||||||
bounds: params.bounds.map(|v| v.0),
|
bounds: params.bounds.map(|v| v.0),
|
||||||
scale_factor,
|
scale_factor,
|
||||||
@ -380,6 +370,7 @@ impl X11WindowState {
|
|||||||
atoms: *atoms,
|
atoms: *atoms,
|
||||||
input_handler: None,
|
input_handler: None,
|
||||||
appearance,
|
appearance,
|
||||||
|
handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,14 +411,15 @@ impl Drop for X11Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum WmHintPropertyState {
|
enum WmHintPropertyState {
|
||||||
Remove = 0,
|
// Remove = 0,
|
||||||
Add = 1,
|
// Add = 1,
|
||||||
Toggle = 2,
|
Toggle = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Window {
|
impl X11Window {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
handle: AnyWindowHandle,
|
||||||
client: X11ClientStatePtr,
|
client: X11ClientStatePtr,
|
||||||
executor: ForegroundExecutor,
|
executor: ForegroundExecutor,
|
||||||
params: WindowParams,
|
params: WindowParams,
|
||||||
@ -440,6 +432,7 @@ impl X11Window {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self(X11WindowStatePtr {
|
Self(X11WindowStatePtr {
|
||||||
state: Rc::new(RefCell::new(X11WindowState::new(
|
state: Rc::new(RefCell::new(X11WindowState::new(
|
||||||
|
handle,
|
||||||
client,
|
client,
|
||||||
executor,
|
executor,
|
||||||
params,
|
params,
|
||||||
@ -622,8 +615,6 @@ impl X11WindowStatePtr {
|
|||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
let old_bounds = mem::replace(&mut state.bounds, bounds);
|
let old_bounds = mem::replace(&mut state.bounds, bounds);
|
||||||
do_move = old_bounds.origin != bounds.origin;
|
do_move = old_bounds.origin != bounds.origin;
|
||||||
// todo(linux): use normal GPUI types here, refactor out the double
|
|
||||||
// viewport check and extra casts ( )
|
|
||||||
let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
|
let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
|
||||||
if state.renderer.viewport_size() != gpu_size {
|
if state.renderer.viewport_size() != gpu_size {
|
||||||
state
|
state
|
||||||
@ -698,8 +689,8 @@ impl PlatformWindow for X11Window {
|
|||||||
self.0.state.borrow().appearance
|
self.0.state.borrow().appearance
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self) -> Rc<dyn PlatformDisplay> {
|
fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
self.0.state.borrow().display.clone()
|
Some(self.0.state.borrow().display.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_position(&self) -> Point<Pixels> {
|
fn mouse_position(&self) -> Point<Pixels> {
|
||||||
@ -713,9 +704,15 @@ impl PlatformWindow for X11Window {
|
|||||||
Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
|
Point::new((reply.root_x as u32).into(), (reply.root_y as u32).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
fn modifiers(&self) -> Modifiers {
|
fn modifiers(&self) -> Modifiers {
|
||||||
Modifiers::default()
|
self.0
|
||||||
|
.state
|
||||||
|
.borrow()
|
||||||
|
.client
|
||||||
|
.0
|
||||||
|
.upgrade()
|
||||||
|
.map(|ref_cell| ref_cell.borrow().modifiers)
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
||||||
@ -792,8 +789,9 @@ impl PlatformWindow for X11Window {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux)
|
fn set_edited(&mut self, _edited: bool) {
|
||||||
fn set_edited(&mut self, edited: bool) {}
|
log::info!("ignoring macOS specific set_edited");
|
||||||
|
}
|
||||||
|
|
||||||
fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
|
fn set_background_appearance(&mut self, background_appearance: WindowBackgroundAppearance) {
|
||||||
let mut inner = self.0.state.borrow_mut();
|
let mut inner = self.0.state.borrow_mut();
|
||||||
@ -801,14 +799,8 @@ impl PlatformWindow for X11Window {
|
|||||||
inner.renderer.update_transparency(transparent);
|
inner.renderer.update_transparency(transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(linux), this corresponds to `orderFrontCharacterPalette` on macOS,
|
|
||||||
// but it looks like the equivalent for Linux is GTK specific:
|
|
||||||
//
|
|
||||||
// https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html
|
|
||||||
//
|
|
||||||
// This API might need to change, or we might need to build an emoji picker into GPUI
|
|
||||||
fn show_character_palette(&self) {
|
fn show_character_palette(&self) {
|
||||||
unimplemented!()
|
log::info!("ignoring macOS specific show_character_palette");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn minimize(&self) {
|
fn minimize(&self) {
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
use std::cell::RefCell;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use calloop::channel;
|
use calloop::channel;
|
||||||
|
|
||||||
use x11rb::protocol::{xproto, Event};
|
use x11rb::protocol::{xproto, Event};
|
||||||
use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle, Point};
|
use xim::{AHashMap, AttributeName, Client, ClientError, ClientHandler, InputStyle};
|
||||||
|
|
||||||
use crate::{Keystroke, PlatformInput, X11ClientState};
|
|
||||||
|
|
||||||
pub enum XimCallbackEvent {
|
pub enum XimCallbackEvent {
|
||||||
XimXEvent(x11rb::protocol::Event),
|
XimXEvent(x11rb::protocol::Event),
|
||||||
@ -84,10 +80,12 @@ impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler
|
|||||||
_input_context_id: u16,
|
_input_context_id: u16,
|
||||||
text: &str,
|
text: &str,
|
||||||
) -> Result<(), ClientError> {
|
) -> Result<(), ClientError> {
|
||||||
self.xim_tx.send(XimCallbackEvent::XimCommitEvent(
|
self.xim_tx
|
||||||
|
.send(XimCallbackEvent::XimCommitEvent(
|
||||||
self.window,
|
self.window,
|
||||||
String::from(text),
|
String::from(text),
|
||||||
));
|
))
|
||||||
|
.ok();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,14 +97,16 @@ impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler
|
|||||||
_flag: xim::ForwardEventFlag,
|
_flag: xim::ForwardEventFlag,
|
||||||
xev: C::XEvent,
|
xev: C::XEvent,
|
||||||
) -> Result<(), ClientError> {
|
) -> Result<(), ClientError> {
|
||||||
match (xev.response_type) {
|
match xev.response_type {
|
||||||
x11rb::protocol::xproto::KEY_PRESS_EVENT => {
|
x11rb::protocol::xproto::KEY_PRESS_EVENT => {
|
||||||
self.xim_tx
|
self.xim_tx
|
||||||
.send(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)));
|
.send(XimCallbackEvent::XimXEvent(Event::KeyPress(xev)))
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
|
x11rb::protocol::xproto::KEY_RELEASE_EVENT => {
|
||||||
self.xim_tx
|
self.xim_tx
|
||||||
.send(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)));
|
.send(XimCallbackEvent::XimXEvent(Event::KeyRelease(xev)))
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@ -145,10 +145,12 @@ impl<C: Client<XEvent = xproto::KeyPressEvent>> ClientHandler<C> for XimHandler
|
|||||||
// XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified,
|
// XIMPrimary, XIMHighlight, XIMSecondary, XIMTertiary are not specified,
|
||||||
// but interchangeable as above
|
// but interchangeable as above
|
||||||
// Currently there's no way to support these.
|
// Currently there's no way to support these.
|
||||||
let mark_range = self.xim_tx.send(XimCallbackEvent::XimPreeditEvent(
|
self.xim_tx
|
||||||
|
.send(XimCallbackEvent::XimPreeditEvent(
|
||||||
self.window,
|
self.window,
|
||||||
String::from(preedit_string),
|
String::from(preedit_string),
|
||||||
));
|
))
|
||||||
|
.ok();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ use std::future::Future;
|
|||||||
use ashpd::desktop::settings::{ColorScheme, Settings};
|
use ashpd::desktop::settings::{ColorScheme, Settings};
|
||||||
use calloop::channel::{Channel, Sender};
|
use calloop::channel::{Channel, Sender};
|
||||||
use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
|
use calloop::{EventSource, Poll, PostAction, Readiness, Token, TokenFactory};
|
||||||
use parking_lot::Mutex;
|
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
@ -115,6 +114,7 @@ impl WindowAppearance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(target_os = "linux", allow(dead_code))]
|
||||||
fn set_native(&mut self, cs: ColorScheme) {
|
fn set_native(&mut self, cs: ColorScheme) {
|
||||||
*self = Self::from_native(cs);
|
*self = Self::from_native(cs);
|
||||||
}
|
}
|
||||||
|
@ -826,9 +826,6 @@ impl Platform for MacPlatform {
|
|||||||
CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
|
CursorStyle::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
|
||||||
CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
|
CursorStyle::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
|
||||||
CursorStyle::ResizeRow => msg_send![class!(NSCursor), resizeUpDownCursor],
|
CursorStyle::ResizeRow => msg_send![class!(NSCursor), resizeUpDownCursor],
|
||||||
CursorStyle::DisappearingItem => {
|
|
||||||
msg_send![class!(NSCursor), disappearingItemCursor]
|
|
||||||
}
|
|
||||||
CursorStyle::IBeamCursorForVerticalLayout => {
|
CursorStyle::IBeamCursorForVerticalLayout => {
|
||||||
msg_send![class!(NSCursor), IBeamCursorForVerticalLayout]
|
msg_send![class!(NSCursor), IBeamCursorForVerticalLayout]
|
||||||
}
|
}
|
||||||
|
@ -799,7 +799,7 @@ impl PlatformWindow for MacWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self) -> Rc<dyn PlatformDisplay> {
|
fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let screen = self.0.lock().native_window.screen();
|
let screen = self.0.lock().native_window.screen();
|
||||||
let device_description: id = msg_send![screen, deviceDescription];
|
let device_description: id = msg_send![screen, deviceDescription];
|
||||||
@ -810,7 +810,7 @@ impl PlatformWindow for MacWindow {
|
|||||||
|
|
||||||
let screen_number: u32 = msg_send![screen_number, unsignedIntValue];
|
let screen_number: u32 = msg_send![screen_number, unsignedIntValue];
|
||||||
|
|
||||||
Rc::new(MacDisplay(screen_number))
|
Some(Rc::new(MacDisplay(screen_number)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
mod display;
|
mod display;
|
||||||
mod platform;
|
mod platform;
|
||||||
mod text_system;
|
|
||||||
mod window;
|
mod window;
|
||||||
|
|
||||||
pub(crate) use dispatcher::*;
|
pub(crate) use dispatcher::*;
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
Bounds, DevicePixels, Font, FontId, FontMetrics, FontRun, GlyphId, LineLayout, Pixels,
|
|
||||||
PlatformTextSystem, RenderGlyphParams, Size,
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
pub(crate) struct TestTextSystem {}
|
|
||||||
|
|
||||||
// todo(linux)
|
|
||||||
#[allow(unused)]
|
|
||||||
impl PlatformTextSystem for TestTextSystem {
|
|
||||||
fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn all_font_names(&self) -> Vec<String> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn all_font_families(&self) -> Vec<String> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn font_id(&self, descriptor: &Font) -> Result<FontId> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn font_metrics(&self, font_id: FontId) -> FontMetrics {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn rasterize_glyph(
|
|
||||||
&self,
|
|
||||||
params: &RenderGlyphParams,
|
|
||||||
raster_bounds: Bounds<DevicePixels>,
|
|
||||||
) -> Result<(Size<DevicePixels>, Vec<u8>)> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
@ -132,8 +132,8 @@ impl PlatformWindow for TestWindow {
|
|||||||
WindowAppearance::Light
|
WindowAppearance::Light
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
|
fn display(&self) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
|
||||||
self.0.lock().display.clone()
|
Some(self.0.lock().display.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_position(&self) -> Point<Pixels> {
|
fn mouse_position(&self) -> Point<Pixels> {
|
||||||
|
@ -379,8 +379,8 @@ impl PlatformWindow for WindowsWindow {
|
|||||||
WindowAppearance::Dark
|
WindowAppearance::Dark
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display(&self) -> Rc<dyn PlatformDisplay> {
|
fn display(&self) -> Option<Rc<dyn PlatformDisplay>> {
|
||||||
Rc::new(self.0.state.borrow().display)
|
Some(Rc::new(self.0.state.borrow().display))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mouse_position(&self) -> Point<Pixels> {
|
fn mouse_position(&self) -> Point<Pixels> {
|
||||||
|
@ -488,7 +488,7 @@ pub struct Window {
|
|||||||
pub(crate) handle: AnyWindowHandle,
|
pub(crate) handle: AnyWindowHandle,
|
||||||
pub(crate) removed: bool,
|
pub(crate) removed: bool,
|
||||||
pub(crate) platform_window: Box<dyn PlatformWindow>,
|
pub(crate) platform_window: Box<dyn PlatformWindow>,
|
||||||
display_id: DisplayId,
|
display_id: Option<DisplayId>,
|
||||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||||
text_system: Arc<WindowTextSystem>,
|
text_system: Arc<WindowTextSystem>,
|
||||||
rem_size: Pixels,
|
rem_size: Pixels,
|
||||||
@ -634,7 +634,7 @@ impl Window {
|
|||||||
window_background,
|
window_background,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let display_id = platform_window.display().id();
|
let display_id = platform_window.display().map(|display| display.id());
|
||||||
let sprite_atlas = platform_window.sprite_atlas();
|
let sprite_atlas = platform_window.sprite_atlas();
|
||||||
let mouse_position = platform_window.mouse_position();
|
let mouse_position = platform_window.mouse_position();
|
||||||
let modifiers = platform_window.modifiers();
|
let modifiers = platform_window.modifiers();
|
||||||
@ -1099,7 +1099,12 @@ impl<'a> WindowContext<'a> {
|
|||||||
fn bounds_changed(&mut self) {
|
fn bounds_changed(&mut self) {
|
||||||
self.window.scale_factor = self.window.platform_window.scale_factor();
|
self.window.scale_factor = self.window.platform_window.scale_factor();
|
||||||
self.window.viewport_size = self.window.platform_window.content_size();
|
self.window.viewport_size = self.window.platform_window.content_size();
|
||||||
self.window.display_id = self.window.platform_window.display().id();
|
self.window.display_id = self
|
||||||
|
.window
|
||||||
|
.platform_window
|
||||||
|
.display()
|
||||||
|
.map(|display| display.id());
|
||||||
|
|
||||||
self.refresh();
|
self.refresh();
|
||||||
|
|
||||||
self.window
|
self.window
|
||||||
@ -1191,7 +1196,7 @@ impl<'a> WindowContext<'a> {
|
|||||||
self.platform
|
self.platform
|
||||||
.displays()
|
.displays()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|display| display.id() == self.window.display_id)
|
.find(|display| Some(display.id()) == self.window.display_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the platform character palette.
|
/// Show the platform character palette.
|
||||||
|
@ -76,8 +76,11 @@ actions!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
pub fn init(cx: &mut AppContext) {
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
cx.on_action(|_: &Hide, cx| cx.hide());
|
cx.on_action(|_: &Hide, cx| cx.hide());
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
cx.on_action(|_: &HideOthers, cx| cx.hide_other_apps());
|
cx.on_action(|_: &HideOthers, cx| cx.hide_other_apps());
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
cx.on_action(|_: &ShowAll, cx| cx.unhide_other_apps());
|
cx.on_action(|_: &ShowAll, cx| cx.unhide_other_apps());
|
||||||
cx.on_action(quit);
|
cx.on_action(quit);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user