@@ -3088,6 +3088,16 @@ dependencies = [
"version_check",
]
+[[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets 0.48.5",
+]
+
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -3277,6 +3287,7 @@ dependencies = [
"util",
"uuid 1.4.1",
"waker-fn",
+ "x11rb",
]
[[package]]
@@ -10273,6 +10284,23 @@ dependencies = [
"tap",
]
+[[package]]
+name = "x11rb"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
+dependencies = [
+ "gethostname",
+ "rustix 0.38.30",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"
+
[[package]]
name = "xattr"
version = "0.2.3"
@@ -1,23 +1,42 @@
-use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay};
+use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size};
use anyhow::Result;
use uuid::Uuid;
+use x11rb::{connection::Connection as _, rust_connection::RustConnection};
#[derive(Debug)]
-pub(crate) struct LinuxDisplay;
+pub(crate) struct LinuxDisplay {
+ x11_screen_index: usize,
+ bounds: Bounds<GlobalPixels>,
+ uuid: Uuid,
+}
+
+impl LinuxDisplay {
+ pub(crate) fn new(xc: &RustConnection, x11_screen_index: usize) -> Self {
+ let screen = &xc.setup().roots[x11_screen_index];
+ Self {
+ x11_screen_index,
+ bounds: Bounds {
+ origin: Default::default(),
+ size: Size {
+ width: GlobalPixels(screen.width_in_pixels as f32),
+ height: GlobalPixels(screen.height_in_pixels as f32),
+ },
+ },
+ uuid: Uuid::from_bytes([0; 16]),
+ }
+ }
+}
impl PlatformDisplay for LinuxDisplay {
fn id(&self) -> DisplayId {
- DisplayId(0)
+ DisplayId(self.x11_screen_index as u32)
}
fn uuid(&self) -> Result<Uuid> {
- Ok(Uuid::from_bytes([0; 16]))
+ Ok(self.uuid)
}
fn bounds(&self) -> Bounds<GlobalPixels> {
- Bounds {
- origin: point(GlobalPixels(0.0), GlobalPixels(0.0)),
- size: size(GlobalPixels(100.0), GlobalPixels(100.0)),
- }
+ self.bounds
}
}
@@ -17,10 +17,13 @@ use std::{
time::Duration,
};
use time::UtcOffset;
+use x11rb::{connection::Connection as _, rust_connection::RustConnection};
pub(crate) struct LinuxPlatform(Mutex<LinuxPlatformState>);
pub(crate) struct LinuxPlatformState {
+ x11_connection: RustConnection,
+ x11_root_index: usize,
gpu: Arc<blade::Context>,
background_executor: BackgroundExecutor,
foreground_executor: ForegroundExecutor,
@@ -35,17 +38,22 @@ impl Default for LinuxPlatform {
impl LinuxPlatform {
pub(crate) fn new() -> Self {
+ let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap();
+
let dispatcher = Arc::new(LinuxDispatcher::new());
let gpu = Arc::new(
unsafe {
blade::Context::init(blade::ContextDesc {
- validation: true, //FIXME
+ validation: cfg!(debug_assertions),
capture: false,
})
}
.unwrap(),
);
+
Self(Mutex::new(LinuxPlatformState {
+ x11_connection,
+ x11_root_index,
gpu,
background_executor: BackgroundExecutor::new(dispatcher.clone()),
foreground_executor: ForegroundExecutor::new(dispatcher),
@@ -84,11 +92,21 @@ impl Platform for LinuxPlatform {
fn unhide_other_apps(&self) {}
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
- Vec::new()
+ let lock = self.0.lock();
+ let setup = lock.x11_connection.setup();
+ (0..setup.roots.len())
+ .map(|id| {
+ Rc::new(LinuxDisplay::new(&lock.x11_connection, id)) as Rc<dyn PlatformDisplay>
+ })
+ .collect()
}
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
- None
+ let lock = self.0.lock();
+ Some(Rc::new(LinuxDisplay::new(
+ &lock.x11_connection,
+ id.0 as usize,
+ )))
}
fn active_window(&self) -> Option<AnyWindowHandle> {
@@ -104,7 +122,8 @@ impl Platform for LinuxPlatform {
Box::new(LinuxWindow::new(
options,
handle,
- Rc::new(LinuxDisplay),
+ &lock.x11_connection,
+ lock.x11_root_index,
&lock.gpu,
))
}
@@ -1,7 +1,8 @@
use crate::{
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent,
- Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
- PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, WindowOptions,
+ Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
+ PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds,
+ WindowOptions,
};
use collections::HashMap;
use parking_lot::Mutex;
@@ -9,9 +10,18 @@ use std::{
rc::{Rc, Weak},
sync::{self, Arc},
};
+use x11rb::{
+ connection::Connection as _,
+ protocol::xproto::{
+ AtomEnum, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, WindowClass,
+ },
+ rust_connection::RustConnection,
+ wrapper::ConnectionExt as _,
+};
pub(crate) struct LinuxWindowState {
- display: Rc<dyn crate::PlatformDisplay>,
+ display: Rc<dyn PlatformDisplay>,
+ win_id: u32,
sprite_atlas: Arc<BladeAtlas>,
}
@@ -22,11 +32,90 @@ impl LinuxWindow {
pub fn new(
options: WindowOptions,
handle: AnyWindowHandle,
- display: Rc<dyn PlatformDisplay>,
+ x11_connection: &RustConnection,
+ x11_main_screen_index: usize,
gpu: &Arc<blade::Context>,
) -> Self {
+ let x11_screen_index = options
+ .display_id
+ .map_or(x11_main_screen_index, |did| did.0 as usize);
+ let screen = &x11_connection.setup().roots[x11_screen_index];
+
+ let win_id = x11_connection.generate_id().unwrap();
+ let win_aux = CreateWindowAux::new()
+ .event_mask(
+ EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION,
+ )
+ .background_pixel(screen.white_pixel);
+
+ let wm_protocols = x11_connection
+ .intern_atom(false, b"WM_PROTOCOLS")
+ .unwrap()
+ .reply()
+ .unwrap()
+ .atom;
+ let wm_delete_window = x11_connection
+ .intern_atom(false, b"WM_DELETE_WINDOW")
+ .unwrap()
+ .reply()
+ .unwrap()
+ .atom;
+ let (bound_x, bound_y, bound_width, bound_height) = match options.bounds {
+ WindowBounds::Fullscreen | WindowBounds::Maximized => {
+ (0, 0, screen.width_in_pixels, screen.height_in_pixels)
+ }
+ WindowBounds::Fixed(bounds) => (
+ bounds.origin.x.0 as i16,
+ bounds.origin.y.0 as i16,
+ bounds.size.width.0 as u16,
+ bounds.size.height.0 as u16,
+ ),
+ };
+
+ x11_connection
+ .create_window(
+ x11rb::COPY_DEPTH_FROM_PARENT,
+ win_id,
+ screen.root,
+ bound_x,
+ bound_y,
+ bound_width,
+ bound_height,
+ 0,
+ WindowClass::INPUT_OUTPUT,
+ 0,
+ &win_aux,
+ )
+ .unwrap();
+
+ if let Some(titlebar) = options.titlebar {
+ if let Some(title) = titlebar.title {
+ x11_connection
+ .change_property8(
+ PropMode::REPLACE,
+ win_id,
+ AtomEnum::WM_NAME,
+ AtomEnum::STRING,
+ title.as_bytes(),
+ )
+ .unwrap();
+ }
+ }
+ x11_connection
+ .change_property32(
+ PropMode::REPLACE,
+ win_id,
+ wm_protocols,
+ AtomEnum::ATOM,
+ &[wm_delete_window],
+ )
+ .unwrap();
+
+ x11_connection.map_window(win_id).unwrap();
+
Self(Arc::new(Mutex::new(LinuxWindowState {
- display,
+ display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)),
+ win_id,
sprite_atlas: Arc::new(BladeAtlas::new(gpu)),
})))
}
@@ -53,7 +142,7 @@ impl PlatformWindow for LinuxWindow {
unimplemented!()
}
- fn display(&self) -> Rc<dyn crate::PlatformDisplay> {
+ fn display(&self) -> Rc<dyn PlatformDisplay> {
Rc::clone(&self.0.lock().display)
}