Detailed changes
@@ -2489,6 +2489,12 @@ version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
+[[package]]
+name = "downcast-rs"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
+
[[package]]
name = "dwrote"
version = "0.11.0"
@@ -3518,6 +3524,9 @@ dependencies = [
"util",
"uuid 1.4.1",
"waker-fn",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
"xcb",
]
@@ -5922,7 +5931,7 @@ dependencies = [
"base64 0.21.4",
"indexmap 1.9.3",
"line-wrap",
- "quick-xml",
+ "quick-xml 0.30.0",
"serde",
"time",
]
@@ -6391,6 +6400,15 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "quick-xml"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "quick_action_bar"
version = "0.1.0"
@@ -7245,6 +7263,12 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
@@ -10320,6 +10344,66 @@ name = "wasmtime-wmemcheck"
version = "16.0.0"
source = "git+https://github.com/bytecodealliance/wasmtime?rev=v16.0.0#6613acd1e4817957a4a7745125ef063b43c273a7"
+[[package]]
+name = "wayland-backend"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 0.38.30",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f"
+dependencies = [
+ "bitflags 2.4.1",
+ "rustix 0.38.30",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
+dependencies = [
+ "bitflags 2.4.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
+dependencies = [
+ "proc-macro2",
+ "quick-xml 0.31.0",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af"
+dependencies = [
+ "dlib",
+ "log",
+ "pkg-config",
+]
+
[[package]]
name = "web-sys"
version = "0.3.64"
@@ -10753,7 +10837,7 @@ dependencies = [
"as-raw-xcb-connection",
"bitflags 1.3.2",
"libc",
- "quick-xml",
+ "quick-xml 0.30.0",
]
[[package]]
@@ -97,6 +97,9 @@ objc = "0.2"
[target.'cfg(target_os = "linux")'.dependencies]
flume = "0.11"
xcb = { version = "1.3", features = ["as-raw-xcb-connection", "present", "randr"] }
+wayland-client= { version = "0.31.2" }
+wayland-protocols = { version = "0.31.2", features = ["client"] }
+wayland-backend = { version = "0.3.3", features = ["client_system"] }
as-raw-xcb-connection = "1"
#TODO: use these on all platforms
blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
@@ -1,18 +1,32 @@
-mod async_context;
-mod entity_map;
-mod model_context;
-#[cfg(any(test, feature = "test-support"))]
-mod test_context;
+use std::{
+ any::{type_name, TypeId},
+ cell::{Ref, RefCell, RefMut},
+ marker::PhantomData,
+ ops::{Deref, DerefMut},
+ path::{Path, PathBuf},
+ rc::{Rc, Weak},
+ sync::{atomic::Ordering::SeqCst, Arc},
+ time::Duration,
+};
-pub use async_context::*;
+use anyhow::{anyhow, Result};
use derive_more::{Deref, DerefMut};
+use futures::{channel::oneshot, future::LocalBoxFuture, Future};
+use slotmap::SlotMap;
+use smol::future::FutureExt;
+use time::UtcOffset;
+
+pub use async_context::*;
+use collections::{FxHashMap, FxHashSet, VecDeque};
pub use entity_map::*;
pub use model_context::*;
use refineable::Refineable;
-use smol::future::FutureExt;
#[cfg(any(test, feature = "test-support"))]
pub use test_context::*;
-use time::UtcOffset;
+use util::{
+ http::{self, HttpClient},
+ ResultExt,
+};
use crate::WindowAppearance;
use crate::{
@@ -23,25 +37,12 @@ use crate::{
SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement,
TextSystem, View, ViewContext, Window, WindowContext, WindowHandle, WindowId,
};
-use anyhow::{anyhow, Result};
-use collections::{FxHashMap, FxHashSet, VecDeque};
-use futures::{channel::oneshot, future::LocalBoxFuture, Future};
-use slotmap::SlotMap;
-use std::{
- any::{type_name, TypeId},
- cell::{Ref, RefCell, RefMut},
- marker::PhantomData,
- ops::{Deref, DerefMut},
- path::{Path, PathBuf},
- rc::{Rc, Weak},
- sync::{atomic::Ordering::SeqCst, Arc},
- time::Duration,
-};
-use util::{
- http::{self, HttpClient},
- ResultExt,
-};
+mod async_context;
+mod entity_map;
+mod model_context;
+#[cfg(any(test, feature = "test-support"))]
+mod test_context;
/// The duration for which futures returned from [AppContext::on_app_context] or [ModelContext::on_app_quit] can run before the application fully quits.
pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(100);
@@ -1,3 +1,6 @@
+//todo!(linux): remove this
+#![allow(unused_variables)]
+
mod blade_atlas;
mod blade_belt;
mod blade_renderer;
@@ -6,6 +9,7 @@ mod client_dispatcher;
mod dispatcher;
mod platform;
mod text_system;
+mod wayland;
mod x11;
pub(crate) use blade_atlas::*;
@@ -1,5 +1,6 @@
#![allow(unused)]
+use std::env;
use std::{
path::{Path, PathBuf},
rc::Rc,
@@ -8,21 +9,21 @@ use std::{
};
use async_task::Runnable;
+use flume::{Receiver, Sender};
use futures::channel::oneshot;
use parking_lot::Mutex;
use time::UtcOffset;
-
-use collections::{HashMap, HashSet};
+use wayland_client::Connection;
use crate::platform::linux::client::Client;
use crate::platform::linux::client_dispatcher::ClientDispatcher;
+use crate::platform::linux::wayland::{WaylandClient, WaylandClientDispatcher};
use crate::platform::{X11Client, X11ClientDispatcher, XcbAtoms};
use crate::{
- Action, AnyWindowHandle, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DisplayId,
+ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
ForegroundExecutor, Keymap, LinuxDispatcher, LinuxTextSystem, Menu, PathPromptOptions,
- Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Point, Result,
- SemanticVersion, Size, Task, WindowAppearance, WindowOptions, X11Display, X11Window,
- X11WindowState,
+ Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result,
+ SemanticVersion, Task, WindowOptions,
};
#[derive(Default)]
@@ -64,35 +65,78 @@ impl Default for LinuxPlatform {
impl LinuxPlatform {
pub(crate) fn new() -> Self {
- let (xcb_connection, x_root_index) =
- xcb::Connection::connect_with_extensions(None, &[xcb::Extension::Present], &[])
- .unwrap();
- let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap();
+ let wayland_display = env::var_os("WAYLAND_DISPLAY");
+ let use_wayland = wayland_display.is_some() && !wayland_display.unwrap().is_empty();
- let xcb_connection = Arc::new(xcb_connection);
let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
- let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
- Arc::new(X11ClientDispatcher::new(&xcb_connection, x_root_index));
- let dispatcher = LinuxDispatcher::new(main_sender, &client_dispatcher);
- let dispatcher = Arc::new(dispatcher);
+ let text_system = Arc::new(LinuxTextSystem::new());
+ let callbacks = Mutex::new(Callbacks::default());
+ let state = Mutex::new(LinuxPlatformState {
+ quit_requested: false,
+ });
+
+ if use_wayland {
+ Self::new_wayland(main_sender, main_receiver, text_system, callbacks, state)
+ } else {
+ Self::new_x11(main_sender, main_receiver, text_system, callbacks, state)
+ }
+ }
- let inner = LinuxPlatformInner {
+ fn new_wayland(
+ main_sender: Sender<Runnable>,
+ main_receiver: Receiver<Runnable>,
+ text_system: Arc<LinuxTextSystem>,
+ callbacks: Mutex<Callbacks>,
+ state: Mutex<LinuxPlatformState>,
+ ) -> Self {
+ let conn = Arc::new(Connection::connect_to_env().unwrap());
+ let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
+ Arc::new(WaylandClientDispatcher::new(&conn));
+ let dispatcher = Arc::new(LinuxDispatcher::new(main_sender, &client_dispatcher));
+ let inner = Arc::new(LinuxPlatformInner {
background_executor: BackgroundExecutor::new(dispatcher.clone()),
foreground_executor: ForegroundExecutor::new(dispatcher.clone()),
main_receiver,
- text_system: Arc::new(LinuxTextSystem::new()),
- callbacks: Mutex::new(Callbacks::default()),
- state: Mutex::new(LinuxPlatformState {
- quit_requested: false,
- }),
- };
- let inner = Arc::new(inner);
-
- let x11client = X11Client::new(Arc::clone(&inner), xcb_connection, x_root_index, atoms);
- let x11client = Arc::new(x11client);
+ text_system,
+ callbacks,
+ state,
+ });
+ let client = Arc::new(WaylandClient::new(Arc::clone(&inner), Arc::clone(&conn)));
+ Self {
+ client,
+ inner: Arc::clone(&inner),
+ }
+ }
+ fn new_x11(
+ main_sender: Sender<Runnable>,
+ main_receiver: Receiver<Runnable>,
+ text_system: Arc<LinuxTextSystem>,
+ callbacks: Mutex<Callbacks>,
+ state: Mutex<LinuxPlatformState>,
+ ) -> Self {
+ let (xcb_connection, x_root_index) = xcb::Connection::connect(None).unwrap();
+ let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap();
+ let xcb_connection = Arc::new(xcb_connection);
+ let client_dispatcher: Arc<dyn ClientDispatcher + Send + Sync> =
+ Arc::new(X11ClientDispatcher::new(&xcb_connection, x_root_index));
+ let dispatcher = Arc::new(LinuxDispatcher::new(main_sender, &client_dispatcher));
+ let inner = Arc::new(LinuxPlatformInner {
+ background_executor: BackgroundExecutor::new(dispatcher.clone()),
+ foreground_executor: ForegroundExecutor::new(dispatcher.clone()),
+ main_receiver,
+ text_system,
+ callbacks,
+ state,
+ });
+ let client = Arc::new(X11Client::new(
+ Arc::clone(&inner),
+ xcb_connection,
+ x_root_index,
+ atoms,
+ ));
Self {
- client: x11client,
+ client,
inner: Arc::clone(&inner),
}
}
@@ -0,0 +1,7 @@
+pub(crate) use client::*;
+pub(crate) use client_dispatcher::*;
+
+mod client;
+mod client_dispatcher;
+mod display;
+mod window;
@@ -0,0 +1,256 @@
+use std::rc::Rc;
+use std::sync::Arc;
+
+use parking_lot::Mutex;
+use wayland_client::protocol::wl_callback::WlCallback;
+use wayland_client::{
+ delegate_noop,
+ protocol::{
+ wl_buffer, wl_callback, wl_compositor, wl_keyboard, wl_registry, wl_seat, wl_shm,
+ wl_shm_pool,
+ wl_surface::{self, WlSurface},
+ },
+ Connection, Dispatch, EventQueue, Proxy, QueueHandle,
+};
+use wayland_protocols::xdg::shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
+
+use crate::platform::linux::client::Client;
+use crate::platform::linux::wayland::window::WaylandWindow;
+use crate::platform::{LinuxPlatformInner, PlatformWindow};
+use crate::{
+ platform::linux::wayland::window::WaylandWindowState, AnyWindowHandle, DisplayId,
+ PlatformDisplay, WindowOptions,
+};
+
+pub(crate) struct WaylandClientState {
+ compositor: Option<wl_compositor::WlCompositor>,
+ buffer: Option<wl_buffer::WlBuffer>,
+ wm_base: Option<xdg_wm_base::XdgWmBase>,
+ windows: Vec<(xdg_surface::XdgSurface, Arc<WaylandWindowState>)>,
+ platform_inner: Arc<LinuxPlatformInner>,
+}
+
+pub(crate) struct WaylandClient {
+ platform_inner: Arc<LinuxPlatformInner>,
+ conn: Arc<Connection>,
+ state: Mutex<WaylandClientState>,
+ event_queue: Mutex<EventQueue<WaylandClientState>>,
+ qh: Arc<QueueHandle<WaylandClientState>>,
+}
+
+impl WaylandClient {
+ pub(crate) fn new(
+ linux_platform_inner: Arc<LinuxPlatformInner>,
+ conn: Arc<Connection>,
+ ) -> Self {
+ let state = WaylandClientState {
+ compositor: None,
+ buffer: None,
+ wm_base: None,
+ windows: Vec::new(),
+ platform_inner: Arc::clone(&linux_platform_inner),
+ };
+ let event_queue: EventQueue<WaylandClientState> = conn.new_event_queue();
+ let qh = event_queue.handle();
+ Self {
+ platform_inner: linux_platform_inner,
+ conn,
+ state: Mutex::new(state),
+ event_queue: Mutex::new(event_queue),
+ qh: Arc::new(qh),
+ }
+ }
+}
+
+impl Client for WaylandClient {
+ fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
+ let display = self.conn.display();
+ let mut eq = self.event_queue.lock();
+ let _registry = display.get_registry(&self.qh, ());
+
+ eq.roundtrip(&mut self.state.lock()).unwrap();
+
+ on_finish_launching();
+ while !self.platform_inner.state.lock().quit_requested {
+ eq.flush().unwrap();
+ eq.dispatch_pending(&mut self.state.lock()).unwrap();
+ if let Some(guard) = self.conn.prepare_read() {
+ guard.read().unwrap();
+ eq.dispatch_pending(&mut self.state.lock()).unwrap();
+ }
+ if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
+ runnable.run();
+ }
+ }
+ }
+
+ fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
+ Vec::new()
+ }
+
+ fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
+ unimplemented!()
+ }
+
+ fn open_window(
+ &self,
+ handle: AnyWindowHandle,
+ options: WindowOptions,
+ ) -> Box<dyn PlatformWindow> {
+ let mut state = self.state.lock();
+
+ let wm_base = state.wm_base.as_ref().unwrap();
+ let compositor = state.compositor.as_ref().unwrap();
+ let wl_surface = compositor.create_surface(&self.qh, ());
+ let xdg_surface = wm_base.get_xdg_surface(&wl_surface, &self.qh, ());
+ let toplevel = xdg_surface.get_toplevel(&self.qh, ());
+ let wl_surface = Arc::new(wl_surface);
+
+ wl_surface.frame(&self.qh, wl_surface.clone());
+ wl_surface.commit();
+
+ let window_state: Arc<WaylandWindowState> = Arc::new(WaylandWindowState::new(
+ &self.conn,
+ wl_surface.clone(),
+ Arc::new(toplevel),
+ options,
+ ));
+
+ state.windows.push((xdg_surface, Arc::clone(&window_state)));
+ Box::new(WaylandWindow(window_state))
+ }
+}
+
+impl Dispatch<wl_registry::WlRegistry, ()> for WaylandClientState {
+ fn event(
+ state: &mut Self,
+ registry: &wl_registry::WlRegistry,
+ event: wl_registry::Event,
+ _: &(),
+ _: &Connection,
+ qh: &QueueHandle<Self>,
+ ) {
+ if let wl_registry::Event::Global {
+ name, interface, ..
+ } = event
+ {
+ match &interface[..] {
+ "wl_compositor" => {
+ let compositor =
+ registry.bind::<wl_compositor::WlCompositor, _, _>(name, 1, qh, ());
+ state.compositor = Some(compositor);
+ }
+ "xdg_wm_base" => {
+ let wm_base = registry.bind::<xdg_wm_base::XdgWmBase, _, _>(name, 1, qh, ());
+ state.wm_base = Some(wm_base);
+ }
+ _ => {}
+ };
+ }
+ }
+}
+
+delegate_noop!(WaylandClientState: ignore wl_compositor::WlCompositor);
+delegate_noop!(WaylandClientState: ignore wl_surface::WlSurface);
+delegate_noop!(WaylandClientState: ignore wl_shm::WlShm);
+delegate_noop!(WaylandClientState: ignore wl_shm_pool::WlShmPool);
+delegate_noop!(WaylandClientState: ignore wl_buffer::WlBuffer);
+delegate_noop!(WaylandClientState: ignore wl_seat::WlSeat);
+delegate_noop!(WaylandClientState: ignore wl_keyboard::WlKeyboard);
+
+impl Dispatch<WlCallback, Arc<WlSurface>> for WaylandClientState {
+ fn event(
+ state: &mut Self,
+ _: &WlCallback,
+ event: wl_callback::Event,
+ surf: &Arc<WlSurface>,
+ _: &Connection,
+ qh: &QueueHandle<Self>,
+ ) {
+ if let wl_callback::Event::Done { .. } = event {
+ for window in &state.windows {
+ if window.1.surface.id() == surf.id() {
+ window.1.surface.frame(qh, surf.clone());
+ window.1.update();
+ window.1.surface.commit();
+ }
+ }
+ }
+ }
+}
+
+impl Dispatch<xdg_surface::XdgSurface, ()> for WaylandClientState {
+ fn event(
+ state: &mut Self,
+ xdg_surface: &xdg_surface::XdgSurface,
+ event: xdg_surface::Event,
+ _: &(),
+ _: &Connection,
+ _: &QueueHandle<Self>,
+ ) {
+ if let xdg_surface::Event::Configure { serial, .. } = event {
+ xdg_surface.ack_configure(serial);
+ for window in &state.windows {
+ if &window.0 == xdg_surface {
+ window.1.update();
+ window.1.surface.commit();
+ return;
+ }
+ }
+ }
+ }
+}
+
+impl Dispatch<xdg_toplevel::XdgToplevel, ()> for WaylandClientState {
+ fn event(
+ state: &mut Self,
+ xdg_toplevel: &xdg_toplevel::XdgToplevel,
+ event: <xdg_toplevel::XdgToplevel as Proxy>::Event,
+ _: &(),
+ _: &Connection,
+ _: &QueueHandle<Self>,
+ ) {
+ if let xdg_toplevel::Event::Configure {
+ width,
+ height,
+ states: _states,
+ } = event
+ {
+ if width == 0 || height == 0 {
+ return;
+ }
+ for window in &state.windows {
+ if window.1.toplevel.id() == xdg_toplevel.id() {
+ window.1.resize(width, height);
+ window.1.surface.commit();
+ return;
+ }
+ }
+ } else if let xdg_toplevel::Event::Close = event {
+ state.windows.retain(|(_, window)| {
+ if window.toplevel.id() == xdg_toplevel.id() {
+ window.toplevel.destroy();
+ false
+ } else {
+ true
+ }
+ });
+ state.platform_inner.state.lock().quit_requested |= state.windows.is_empty();
+ }
+ }
+}
+
+impl Dispatch<xdg_wm_base::XdgWmBase, ()> for WaylandClientState {
+ fn event(
+ _: &mut Self,
+ wm_base: &xdg_wm_base::XdgWmBase,
+ event: <xdg_wm_base::XdgWmBase as Proxy>::Event,
+ _: &(),
+ _: &Connection,
+ _: &QueueHandle<Self>,
+ ) {
+ if let xdg_wm_base::Event::Ping { serial } = event {
+ wm_base.pong(serial);
+ }
+ }
+}
@@ -0,0 +1,30 @@
+use std::sync::Arc;
+
+use wayland_client::{Connection, EventQueue};
+
+use crate::platform::linux::client_dispatcher::ClientDispatcher;
+
+pub(crate) struct WaylandClientDispatcher {
+ conn: Arc<Connection>,
+ event_queue: Arc<EventQueue<Connection>>,
+}
+
+impl WaylandClientDispatcher {
+ pub(crate) fn new(conn: &Arc<Connection>) -> Self {
+ let event_queue = conn.new_event_queue();
+ Self {
+ conn: Arc::clone(conn),
+ event_queue: Arc::new(event_queue),
+ }
+ }
+}
+
+impl Drop for WaylandClientDispatcher {
+ fn drop(&mut self) {
+ //todo!(linux)
+ }
+}
+
+impl ClientDispatcher for WaylandClientDispatcher {
+ fn dispatch_on_main_thread(&self) {}
+}
@@ -0,0 +1,31 @@
+use std::fmt::Debug;
+
+use uuid::Uuid;
+
+use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size};
+
+#[derive(Debug)]
+pub(crate) struct WaylandDisplay {}
+
+impl PlatformDisplay for WaylandDisplay {
+ // todo!(linux)
+ fn id(&self) -> DisplayId {
+ return DisplayId(123); // return some fake data so it doesn't panic
+ }
+
+ // todo!(linux)
+ fn uuid(&self) -> anyhow::Result<Uuid> {
+ return Ok(Uuid::from_bytes([0; 16])); // return some fake data so it doesn't panic
+ }
+
+ // todo!(linux)
+ fn bounds(&self) -> Bounds<GlobalPixels> {
+ Bounds {
+ origin: Default::default(),
+ size: Size {
+ width: GlobalPixels(1000f32),
+ height: GlobalPixels(500f32),
+ },
+ } // return some fake data so it doesn't panic
+ }
+}
@@ -0,0 +1,350 @@
+use std::any::Any;
+use std::ffi::c_void;
+use std::rc::Rc;
+use std::sync::Arc;
+
+use blade_graphics as gpu;
+use blade_rwh::{HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle};
+use futures::channel::oneshot::Receiver;
+use parking_lot::Mutex;
+use raw_window_handle::{
+ DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, WindowHandle,
+};
+use wayland_client::{protocol::wl_surface, Proxy};
+use wayland_protocols::xdg::shell::client::xdg_toplevel;
+
+use crate::platform::linux::blade_renderer::BladeRenderer;
+use crate::platform::linux::wayland::display::WaylandDisplay;
+use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
+use crate::scene::Scene;
+use crate::{
+ px, Bounds, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, PromptLevel, Size,
+ WindowAppearance, WindowBounds, WindowOptions,
+};
+
+#[derive(Default)]
+pub(crate) struct Callbacks {
+ request_frame: Option<Box<dyn FnMut()>>,
+ input: Option<Box<dyn FnMut(crate::PlatformInput) -> bool>>,
+ active_status_change: Option<Box<dyn FnMut(bool)>>,
+ resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
+ fullscreen: Option<Box<dyn FnMut(bool)>>,
+ moved: Option<Box<dyn FnMut()>>,
+ should_close: Option<Box<dyn FnMut() -> bool>>,
+ close: Option<Box<dyn FnOnce()>>,
+ appearance_changed: Option<Box<dyn FnMut()>>,
+}
+
+struct WaylandWindowInner {
+ renderer: BladeRenderer,
+ bounds: Bounds<i32>,
+}
+
+struct RawWindow {
+ window: *mut c_void,
+ display: *mut c_void,
+}
+
+unsafe impl HasRawWindowHandle for RawWindow {
+ fn raw_window_handle(&self) -> RawWindowHandle {
+ let mut wh = blade_rwh::WaylandWindowHandle::empty();
+ wh.surface = self.window;
+ wh.into()
+ }
+}
+
+unsafe impl HasRawDisplayHandle for RawWindow {
+ fn raw_display_handle(&self) -> RawDisplayHandle {
+ let mut dh = blade_rwh::WaylandDisplayHandle::empty();
+ dh.display = self.display;
+ dh.into()
+ }
+}
+
+impl WaylandWindowInner {
+ fn new(
+ conn: &Arc<wayland_client::Connection>,
+ wl_surf: &Arc<wl_surface::WlSurface>,
+ bounds: Bounds<i32>,
+ ) -> Self {
+ let raw = RawWindow {
+ window: wl_surf.id().as_ptr() as *mut _,
+ display: conn.backend().display_ptr() as *mut _,
+ };
+ let gpu = Arc::new(
+ unsafe {
+ gpu::Context::init_windowed(
+ &raw,
+ gpu::ContextDesc {
+ validation: false,
+ capture: false,
+ },
+ )
+ }
+ .unwrap(),
+ );
+ let extent = gpu::Extent {
+ width: bounds.size.width as u32,
+ height: bounds.size.height as u32,
+ depth: 1,
+ };
+ Self {
+ renderer: BladeRenderer::new(gpu, extent),
+ bounds,
+ }
+ }
+}
+
+pub(crate) struct WaylandWindowState {
+ conn: Arc<wayland_client::Connection>,
+ inner: Mutex<WaylandWindowInner>,
+ pub(crate) callbacks: Mutex<Callbacks>,
+ pub(crate) surface: Arc<wl_surface::WlSurface>,
+ pub(crate) toplevel: Arc<xdg_toplevel::XdgToplevel>,
+}
+
+impl WaylandWindowState {
+ pub(crate) fn new(
+ conn: &Arc<wayland_client::Connection>,
+ wl_surf: Arc<wl_surface::WlSurface>,
+ toplevel: Arc<xdg_toplevel::XdgToplevel>,
+ options: WindowOptions,
+ ) -> Self {
+ if options.bounds == WindowBounds::Maximized {
+ toplevel.set_maximized();
+ } else if options.bounds == WindowBounds::Fullscreen {
+ toplevel.set_fullscreen(None);
+ }
+
+ let bounds: Bounds<i32> = match options.bounds {
+ WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds {
+ origin: Point::default(),
+ size: Size {
+ width: 500,
+ height: 500,
+ }, //todo!(implement)
+ },
+ WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32),
+ };
+
+ Self {
+ conn: Arc::clone(conn),
+ surface: Arc::clone(&wl_surf),
+ inner: Mutex::new(WaylandWindowInner::new(&Arc::clone(conn), &wl_surf, bounds)),
+ callbacks: Mutex::new(Callbacks::default()),
+ toplevel,
+ }
+ }
+
+ pub fn update(&self) {
+ let mut cb = self.callbacks.lock();
+ if let Some(mut fun) = cb.request_frame.take() {
+ drop(cb);
+ fun();
+ self.callbacks.lock().request_frame = Some(fun);
+ }
+ }
+
+ pub fn resize(&self, width: i32, height: i32) {
+ {
+ let mut inner = self.inner.lock();
+ inner.bounds.size.width = width;
+ inner.bounds.size.height = height;
+ inner.renderer.resize(gpu::Extent {
+ width: width as u32,
+ height: height as u32,
+ depth: 1,
+ });
+ }
+ let mut callbacks = self.callbacks.lock();
+ if let Some(ref mut fun) = callbacks.resize {
+ fun(
+ Size {
+ width: px(width as f32),
+ height: px(height as f32),
+ },
+ 1.0,
+ );
+ }
+ if let Some(ref mut fun) = callbacks.moved {
+ fun()
+ }
+ }
+
+ pub fn close(&self) {
+ let mut callbacks = self.callbacks.lock();
+ if let Some(fun) = callbacks.close.take() {
+ fun()
+ }
+ self.toplevel.destroy();
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct WaylandWindow(pub(crate) Arc<WaylandWindowState>);
+
+impl HasWindowHandle for WaylandWindow {
+ fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {
+ unimplemented!()
+ }
+}
+
+impl HasDisplayHandle for WaylandWindow {
+ fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {
+ unimplemented!()
+ }
+}
+
+impl PlatformWindow for WaylandWindow {
+ //todo!(linux)
+ fn bounds(&self) -> WindowBounds {
+ WindowBounds::Maximized
+ }
+
+ // todo!(linux)
+ fn content_size(&self) -> Size<Pixels> {
+ let inner = self.0.inner.lock();
+ Size {
+ width: Pixels(inner.bounds.size.width as f32),
+ height: Pixels(inner.bounds.size.height as f32),
+ }
+ }
+
+ // todo!(linux)
+ fn scale_factor(&self) -> f32 {
+ return 1f32;
+ }
+
+ //todo!(linux)
+ fn titlebar_height(&self) -> Pixels {
+ unimplemented!()
+ }
+
+ // todo!(linux)
+ fn appearance(&self) -> WindowAppearance {
+ WindowAppearance::Light
+ }
+
+ // todo!(linux)
+ fn display(&self) -> Rc<dyn PlatformDisplay> {
+ Rc::new(WaylandDisplay {})
+ }
+
+ // todo!(linux)
+ fn mouse_position(&self) -> Point<Pixels> {
+ Point::default()
+ }
+
+ //todo!(linux)
+ fn modifiers(&self) -> Modifiers {
+ crate::Modifiers::default()
+ }
+
+ //todo!(linux)
+ fn as_any_mut(&mut self) -> &mut dyn Any {
+ unimplemented!()
+ }
+
+ fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
+ //todo!(linux)
+ }
+
+ //todo!(linux)
+ fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
+ None
+ }
+
+ //todo!(linux)
+ fn prompt(
+ &self,
+ level: PromptLevel,
+ msg: &str,
+ detail: Option<&str>,
+ answers: &[&str],
+ ) -> Receiver<usize> {
+ unimplemented!()
+ }
+
+ fn activate(&self) {
+ //todo!(linux)
+ }
+
+ fn set_title(&mut self, title: &str) {
+ self.0.toplevel.set_title(title.to_string());
+ }
+
+ fn set_edited(&mut self, edited: bool) {
+ //todo!(linux)
+ }
+
+ fn show_character_palette(&self) {
+ //todo!(linux)
+ }
+
+ fn minimize(&self) {
+ //todo!(linux)
+ }
+
+ fn zoom(&self) {
+ //todo!(linux)
+ }
+
+ fn toggle_full_screen(&self) {
+ //todo!(linux)
+ }
+
+ fn on_request_frame(&self, callback: Box<dyn FnMut()>) {
+ self.0.callbacks.lock().request_frame = Some(callback);
+ }
+
+ fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
+ //todo!(linux)
+ }
+
+ fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
+ //todo!(linux)
+ }
+
+ fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
+ self.0.callbacks.lock().resize = Some(callback);
+ }
+
+ fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
+ //todo!(linux)
+ }
+
+ fn on_moved(&self, callback: Box<dyn FnMut()>) {
+ self.0.callbacks.lock().moved = Some(callback);
+ }
+
+ fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
+ self.0.callbacks.lock().should_close = Some(callback);
+ }
+
+ fn on_close(&self, callback: Box<dyn FnOnce()>) {
+ self.0.callbacks.lock().close = Some(callback);
+ }
+
+ fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
+ //todo!(linux)
+ }
+
+ // todo!(linux)
+ fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
+ false
+ }
+
+ fn draw(&self, scene: &Scene) {
+ let mut inner = self.0.inner.lock();
+ inner.renderer.draw(scene);
+ }
+
+ fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
+ let inner = self.0.inner.lock();
+ inner.renderer.atlas().clone()
+ }
+
+ fn set_graphics_profiler_enabled(&self, enabled: bool) {
+ //todo!(linux)
+ }
+}
@@ -11,6 +11,7 @@ if [[ -n $apt ]]; then
libasound2-dev
libfontconfig-dev
vulkan-validationlayers*
+ libwayland-dev
)
$maysudo "$apt" install -y "${deps[@]}"
exit 0
@@ -24,6 +25,7 @@ if [[ -n $dnf ]]; then
alsa-lib-devel
fontconfig-devel
vulkan-validation-layers
+ wayland-devel
)
$maysudo "$dnf" install -y "${deps[@]}"
exit 0
@@ -37,6 +39,7 @@ if [[ -n $pacman ]]; then
alsa-lib
fontconfig
vulkan-validation-layers
+ wayland
)
$maysudo "$pacman" -S --needed --noconfirm "${deps[@]}"
exit 0
@@ -21,7 +21,6 @@ extend-ignore-re = [
'"ba"',
":ba\\|z",
# :/ crates/collab/migrations/20231009181554_add_release_channel_to_rooms.sql
- "ADD COLUMN enviroment TEXT",
- "ALTER TABLE rooms DROP COLUMN enviroment;",
+ "COLUMN enviroment",
]
check-filename = true