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:
Mikayla Maki 2024-05-31 18:36:15 -07:00 committed by GitHub
parent a6e0c8aca1
commit 94c3101fb0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 581 additions and 441 deletions

View File

@ -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"]

View File

@ -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"
} }
}, },

View File

@ -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

View File

@ -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());

View File

@ -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);

View File

@ -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

View File

@ -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")]

View File

@ -1,6 +1,3 @@
// todo(linux): remove
#![allow(unused)]
mod dispatcher; mod dispatcher;
mod headless; mod headless;
mod platform; mod platform;

View File

@ -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();
} }

View File

@ -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
if let calloop::channel::Event::Msg(runnable) = event { .insert_source(main_receiver, |event, _, _: &mut HeadlessClient| {
runnable.run(); if let calloop::channel::Event::Msg(runnable) = event {
} 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

View File

@ -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",

View File

@ -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
if let calloop::channel::Event::Msg(runnable) = event { .insert_source(main_receiver, |event, _, _: &mut WaylandClientStatePtr| {
runnable.run(); if let calloop::channel::Event::Msg(runnable) = event {
} runnable.run();
}); }
})
.unwrap();
let seat = seat.unwrap(); let seat = seat.unwrap();
let globals = Globals::new( let globals = Globals::new(
@ -384,33 +421,37 @@ 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
move |event, _, client| match event { .insert_source(XDPEventSource::new(&common.background_executor), {
XDPEvent::WindowAppearance(appearance) => { move |event, _, client| match event {
if let Some(client) = client.0.upgrade() { XDPEvent::WindowAppearance(appearance) => {
let mut client = client.borrow_mut(); if let Some(client) = client.0.upgrade() {
let mut client = client.borrow_mut();
client.common.appearance = appearance; client.common.appearance = appearance;
for (_, window) in &mut client.windows { for (_, window) in &mut client.windows {
window.set_appearance(appearance); window.set_appearance(appearance);
}
} }
} }
} }
} })
}); .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 => {

View File

@ -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
} }
} }

View File

@ -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
);
}
} }

View File

@ -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)) = &current_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(&region)); blur.set_region(Some(&region));
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) {

View File

@ -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
if let calloop::channel::Event::Msg(runnable) = event { .insert_source(main_receiver, |event, _, _: &mut X11Client| {
runnable.run(); if let calloop::channel::Event::Msg(runnable) = event {
} 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,18 +354,21 @@ 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
move |event, _, client| match event { .insert_source(XDPEventSource::new(&common.background_executor), {
XDPEvent::WindowAppearance(appearance) => { move |event, _, client| match event {
client.with_common(|common| common.appearance = appearance); XDPEvent::WindowAppearance(appearance) => {
for (_, window) in &mut client.0.borrow_mut().windows { client.with_common(|common| common.appearance = appearance);
window.window.set_appearance(appearance); for (_, window) in &mut client.0.borrow_mut().windows {
window.window.set_appearance(appearance);
}
} }
} }
} })
}); .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:

View File

@ -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) {

View File

@ -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
self.window, .send(XimCallbackEvent::XimCommitEvent(
String::from(text), self.window,
)); 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
self.window, .send(XimCallbackEvent::XimPreeditEvent(
String::from(preedit_string), self.window,
)); String::from(preedit_string),
))
.ok();
Ok(()) Ok(())
} }
} }

View File

@ -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);
} }

View File

@ -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]
} }

View File

@ -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)))
} }
} }

View File

@ -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::*;

View File

@ -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!()
}
}

View File

@ -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> {

View File

@ -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> {

View File

@ -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.

View File

@ -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);
} }