diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b7ba7f..7bd868a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,8 @@ jobs: os: [ubuntu-latest, windows-latest, macOS-latest] runs-on: ${{ matrix.os }} steps: + - if: matrix.os == 'ubuntu-latest' + run: sudo apt install libwayland-cursor0 libxkbcommon-dev libwayland-dev - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: diff --git a/Cargo.toml b/Cargo.toml index 0a9a8eb..016ddd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,65 +39,19 @@ features = [ "errhandlingapi" ] -[target.i686-unknown-linux-gnu.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" +[features] +default = ["wayland", "x11"] +x11 = ["x11-dl"] +wayland = ["wayland-client", "wayland-protocols", "wayland-cursor", "tempfile", "xkb", "xkbcommon-sys"] -[target.x86_64-unknown-linux-gnu.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.arm-unknown-linux-gnueabihf.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.armv7-unknown-linux-gnueabihf.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.aarch64-unknown-linux-gnu.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.x86_64-unknown-dragonfly.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.armv6-unknown-freebsd.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.armv7-unknown-freebsd.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.aarch64-unknown-freebsd.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" - -[target.x86_64-unknown-freebsd.dependencies] -x11-dl = "2.18.3" -wayland-client = "0.25" -wayland-protocols = { version = "0.25", features = ["client"] } -tempfile = "3.1.0" +[target.'cfg(not(any(target_os = "macos", target_os = "redox", windows)))'.dependencies] +wayland-client = {version = "0.25", optional = true} +wayland-protocols = { version = "0.25", features = ["client", "unstable_protocols"], optional = true } +wayland-cursor = {version = "0.25", optional = true} +tempfile = {version = "3.1.0", optional = true} +xkb = {version = "0.2.1", optional = true} +xkbcommon-sys = {version = "0.7", optional = true} +x11-dl = {version = "2.18.3", optional = true} [target.x86_64-unknown-redox.dependencies] orbclient = "0.3.20" diff --git a/build.rs b/build.rs index 4e849ea..07b0092 100644 --- a/build.rs +++ b/build.rs @@ -2,6 +2,15 @@ use std::env; extern crate cc; fn main() { + if cfg!(not(any( + target_os = "macos", + target_os = "windows", + target_os = "redox" + ))) && cfg!(not(any(feature = "wayland", feature = "x11"))) + { + panic!("At least one of the x11 or wayland features must be enabled"); + } + let env = env::var("TARGET").unwrap(); if env.contains("darwin") { cc::Build::new() diff --git a/src/os/unix/common.rs b/src/os/unix/common.rs new file mode 100644 index 0000000..be9b524 --- /dev/null +++ b/src/os/unix/common.rs @@ -0,0 +1,58 @@ +use crate::Result; +use crate::{Key, MenuHandle, MenuItem, MenuItemHandle, UnixMenu, UnixMenuItem}; + +pub struct Menu { + pub internal: UnixMenu, +} + +impl Menu { + pub fn new(name: &str) -> Result { + Ok(Menu { + internal: UnixMenu { + handle: MenuHandle(0), + item_counter: MenuItemHandle(0), + name: name.to_owned(), + items: Vec::new(), + }, + }) + } + + pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) { + let handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + label: name.to_owned(), + handle, + sub_menu: Some(Box::new(sub_menu.internal.clone())), + id: 0, + enabled: true, + key: Key::Unknown, + modifier: 0, + }); + } + + fn next_item_handle(&mut self) -> MenuItemHandle { + let handle = self.internal.item_counter; + self.internal.item_counter.0 += 1; + handle + } + + pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { + let item_handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + sub_menu: None, + handle: self.internal.item_counter, + id: item.id, + label: item.label.clone(), + enabled: item.enabled, + key: item.key, + modifier: item.modifier, + }); + item_handle + } + + pub fn remove_item(&mut self, handle: &MenuItemHandle) { + self.internal + .items + .retain(|ref item| item.handle.0 != handle.0); + } +} diff --git a/src/os/unix/mod.rs b/src/os/unix/mod.rs index 87ca428..7e111bf 100644 --- a/src/os/unix/mod.rs +++ b/src/os/unix/mod.rs @@ -8,37 +8,61 @@ // turn off a gazillion warnings about X keysym names #![allow(non_upper_case_globals)] -//mod wayland; +mod common; mod key_mapping; +#[cfg(feature = "wayland")] +mod wayland; +#[cfg(feature = "x11")] mod x11; use crate::Result; use crate::{CursorStyle, MenuHandle, UnixMenu}; use crate::{InputCallback, Key, KeyRepeat, MouseButton, MouseMode, WindowOptions}; -pub use x11::Menu; +pub use common::Menu; use std::os::raw; -//Differentiate between Wayland and X11 at run-time +// Differentiate between Wayland and X11 at run-time pub enum Window { + #[cfg(feature = "x11")] X11(x11::Window), - Wayland(()), //WlWindow + #[cfg(feature = "wayland")] + Wayland(wayland::Window), } impl Window { + #[cfg(all(feature = "x11", feature = "wayland"))] pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result { - //TODO: Try to create Wayland display first + // Try to create Wayland display first + let wl_window = wayland::Window::new(name, width, height, opts); + match wl_window { + Ok(w) => Ok(Window::Wayland(w)), + Err(_) => { + // Create X11 Window when Wayland fails + let window = Window::X11(x11::Window::new(name, width, height, opts)?); + Ok(window) + } + } + } - //Create X11 Window when Wayland fails + #[cfg(all(feature = "wayland", not(feature = "x11")))] + pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result { + let wl_window = wayland::Window::new(name, width, height, opts)?; + Ok(Window::Wayland(wl_window)) + } + + #[cfg(all(feature = "x11", not(feature = "wayland")))] + pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result { let window = Window::X11(x11::Window::new(name, width, height, opts)?); - Ok(window) } pub fn set_title(&mut self, title: &str) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_title(title), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_title(title), } } @@ -50,199 +74,257 @@ impl Window { buf_stride: usize, ) -> Result<()> { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => { w.update_with_buffer_stride(buffer, buf_width, buf_height, buf_stride) } - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => { + w.update_with_buffer_stride(buffer, buf_width, buf_height, buf_stride) + } } } pub fn update(&mut self) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.update(), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.update(), } } pub fn get_window_handle(&self) -> *mut raw::c_void { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_window_handle(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_window_handle(), } } pub fn set_background_color(&mut self, bg_color: u32) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_background_color(bg_color), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_background_color(bg_color), } } pub fn set_position(&mut self, x: isize, y: isize) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_position(x, y), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_position(x, y), } } pub fn get_size(&self) -> (usize, usize) { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_size(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_size(), } } pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_mouse_pos(mode), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_mouse_pos(mode), } } pub fn get_unscaled_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_unscaled_mouse_pos(mode), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_unscaled_mouse_pos(mode), } } pub fn get_mouse_down(&self, button: MouseButton) -> bool { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_mouse_down(button), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_mouse_down(button), } } pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_scroll_wheel(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_scroll_wheel(), } } pub fn set_cursor_style(&mut self, cursor: CursorStyle) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_cursor_style(cursor), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_cursor_style(cursor), } } pub fn set_rate(&mut self, rate: Option) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_rate(rate), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_rate(rate), } } pub fn update_rate(&mut self) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.update_rate(), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.update_rate(), } } pub fn get_keys(&self) -> Option> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_keys(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_keys(), } } pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_keys_pressed(repeat), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_keys_pressed(repeat), } } pub fn get_keys_released(&self) -> Option> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_keys_released(), + #[cfg(feature = "wayland")] Window::Wayland(ref _w) => unimplemented!(), } } pub fn is_key_down(&self, key: Key) -> bool { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.is_key_down(key), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.is_key_down(key), } } pub fn set_key_repeat_delay(&mut self, delay: f32) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_key_repeat_delay(delay), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_key_repeat_delay(delay), } } pub fn set_key_repeat_rate(&mut self, rate: f32) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_key_repeat_rate(rate), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_key_repeat_rate(rate), } } pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.is_key_pressed(key, repeat), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.is_key_pressed(key, repeat), } } pub fn is_key_released(&self, key: Key) -> bool { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.is_key_released(key), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.is_key_released(key), } } pub fn set_input_callback(&mut self, callback: Box) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.set_input_callback(callback), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.set_input_callback(callback), } } pub fn is_open(&self) -> bool { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.is_open(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.is_open(), } } pub fn is_active(&mut self) -> bool { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.is_active(), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.is_active(), } } pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.add_menu(menu), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.add_menu(menu), } } pub fn get_unix_menus(&self) -> Option<&Vec> { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.get_unix_menus(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.get_unix_menus(), } } pub fn remove_menu(&mut self, handle: MenuHandle) { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.remove_menu(handle), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.remove_menu(handle), } } pub fn is_menu_pressed(&mut self) -> Option { match *self { + #[cfg(feature = "x11")] Window::X11(ref mut w) => w.is_menu_pressed(), - Window::Wayland(ref mut _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref mut w) => w.is_menu_pressed(), } } } @@ -250,8 +332,10 @@ impl Window { unsafe impl raw_window_handle::HasRawWindowHandle for Window { fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { match *self { + #[cfg(feature = "x11")] Window::X11(ref w) => w.raw_window_handle(), - Window::Wayland(ref _w) => unimplemented!(), + #[cfg(feature = "wayland")] + Window::Wayland(ref w) => w.raw_window_handle(), } } } diff --git a/src/os/unix/wayland.rs b/src/os/unix/wayland.rs new file mode 100644 index 0000000..c4a61b1 --- /dev/null +++ b/src/os/unix/wayland.rs @@ -0,0 +1,1129 @@ +use crate::buffer_helper; +use crate::key_handler::KeyHandler; +use crate::mouse_handler; +use crate::rate::UpdateRate; +use crate::{CursorStyle, MenuHandle, UnixMenu}; +use crate::{Error, Result}; +use crate::{ + InputCallback, Key, KeyRepeat, MouseButton, MouseMode, Scale, ScaleMode, WindowOptions, +}; + +use super::common::Menu; + +use wayland_client::protocol::wl_buffer::WlBuffer; +use wayland_client::protocol::wl_compositor::WlCompositor; +use wayland_client::protocol::wl_display::WlDisplay; +use wayland_client::protocol::wl_keyboard::{KeymapFormat, WlKeyboard}; +use wayland_client::protocol::wl_pointer::WlPointer; +use wayland_client::protocol::wl_seat::WlSeat; +use wayland_client::protocol::wl_shm::{Format, WlShm}; +use wayland_client::protocol::wl_shm_pool::WlShmPool; +use wayland_client::protocol::wl_surface::WlSurface; +use wayland_client::protocol::{wl_keyboard, wl_pointer}; +use wayland_client::{Attached, Display, EventQueue, GlobalManager, Main}; +use wayland_protocols::unstable::xdg_decoration::v1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1; +use wayland_protocols::xdg_shell::client::xdg_surface::XdgSurface; +use wayland_protocols::xdg_shell::client::xdg_toplevel::XdgToplevel; +use wayland_protocols::xdg_shell::client::xdg_wm_base::XdgWmBase; +use xkb::keymap::Keymap; + +use std::cell::RefCell; +use std::ffi::c_void; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::mem; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::rc::Rc; +use std::slice; +use std::sync::mpsc; +use std::time::Duration; + +const KEY_XKB_OFFSET: u32 = 8; +const KEY_MOUSE_BTN1: u32 = 272; +const KEY_MOUSE_BTN2: u32 = 273; +const KEY_MOUSE_BTN3: u32 = 274; + +type ToplevelResolution = Rc>>; +type ToplevelClosed = Rc>; + +// These functions are implemented in C in order to always have +// optimizations on (`-O3`), allowing debug builds to run fast as well. +extern "C" { + fn Image_upper_left( + target: *mut u32, + source: *const u32, + source_w: u32, + source_h: u32, + source_stride: u32, + dest_width: u32, + dest_height: u32, + bg_color: u32, + ); + + fn Image_center( + target: *mut u32, + source: *const u32, + source_w: u32, + source_h: u32, + source_stride: u32, + dest_width: u32, + dest_height: u32, + bg_color: u32, + ); + + fn Image_resize_linear_aspect_fill_c( + target: *mut u32, + source: *const u32, + source_w: u32, + source_h: u32, + source_stride: u32, + dest_width: u32, + dest_height: u32, + bg_color: u32, + ); + + fn Image_resize_linear_c( + target: *mut u32, + source: *const u32, + source_w: u32, + source_h: u32, + source_stride: u32, + dest_width: u32, + dest_height: u32, + ); +} + +struct Buffer { + fd: File, + pool: Main, + pool_size: i32, + buffer: Main, + buffer_state: Rc>, + fb_size: (i32, i32), +} + +struct BufferPool { + pool: Vec, + shm: Main, + format: Format, +} + +impl BufferPool { + fn new(shm: Main, format: Format) -> Self { + Self { + pool: Vec::new(), + shm, + format, + } + } + + fn create_shm_buffer( + shm_pool: &Main, + size: (i32, i32), + format: Format, + ) -> (Main, Rc>) { + let buf = shm_pool.create_buffer( + 0, + size.0, + size.1, + size.0 * mem::size_of::() as i32, + format, + ); + + // Whether or not the buffer has been released by the compositor + let buf_released = Rc::new(RefCell::new(false)); + let buf_released_clone = buf_released.clone(); + + buf.quick_assign(move |_, event, _| { + use wayland_client::protocol::wl_buffer::Event; + + if let Event::Release = event { + *buf_released_clone.borrow_mut() = true; + } + }); + + (buf, buf_released) + } + + fn get_buffer(&mut self, size: (i32, i32)) -> std::io::Result<(File, &Main)> { + let pos = self.pool.iter().rposition(|e| *e.buffer_state.borrow()); + let size_bytes = size.0 * size.1 * mem::size_of::() as i32; + + // If possible, take an older shm_pool and create a new buffer in it + if let Some(idx) = pos { + // Shm_pool not allowed to be truncated + if size_bytes > self.pool[idx].pool_size { + self.pool[idx].pool.resize(size_bytes); + self.pool[idx].pool_size = size_bytes; + } + + // Different buffer size + if self.pool[idx].fb_size != size { + let new_buffer = Self::create_shm_buffer(&self.pool[idx].pool, size, self.format); + let old_buffer = mem::replace(&mut self.pool[idx].buffer, new_buffer.0); + old_buffer.destroy(); + self.pool[idx].fb_size = size; + } + + Ok((self.pool[idx].fd.try_clone()?, &self.pool[idx].buffer)) + } else { + let tempfile = tempfile::tempfile()?; + let shm_pool = self.shm.create_pool( + tempfile.as_raw_fd(), + size.0 * size.1 * mem::size_of::() as i32, + ); + let buffer = Self::create_shm_buffer(&shm_pool, size, self.format); + + self.pool.push(Buffer { + fd: tempfile, + pool: shm_pool, + pool_size: size_bytes, + buffer: buffer.0, + buffer_state: buffer.1, + fb_size: size, + }); + + Ok(( + self.pool[self.pool.len() - 1].fd.try_clone()?, + &self.pool[self.pool.len() - 1].buffer, + )) + } + } +} + +struct DisplayInfo { + attached_display: Attached, + surface: Main, + xdg_surface: Main, + toplevel: Main, + event_queue: EventQueue, + xdg_config: Rc>>, + cursor: wayland_cursor::CursorTheme, + cursor_surface: Main, + _display: Display, + buf_pool: BufferPool, +} + +impl DisplayInfo { + /// Accepts the size of the surface to be created, whether or not the alpha channel will be + /// rendered, and whether or not server-side decorations will be used. + fn new(size: (i32, i32), alpha: bool, decorate: bool) -> Result<(Self, WaylandInput)> { + // Get the wayland display + let display = Display::connect_to_env().map_err(|e| { + Error::WindowCreate(format!("Failed to connect to the Wayland display: {:?}", e)) + })?; + let mut event_queue = display.create_event_queue(); + + // Access internal WlDisplay with a token + let attached_display = (*display).clone().attach(event_queue.token()); + let globals = GlobalManager::new(&attached_display); + + // Wait for the Wayland server to process all events + event_queue + .sync_roundtrip(&mut (), |_, _, _| unreachable!()) + .map_err(|e| Error::WindowCreate(format!("Roundtrip failed: {:?}", e)))?; + + // Version 5 is required for scroll events + let seat = globals + .instantiate_exact::(5) + .map_err(|e| Error::WindowCreate(format!("Failed to retrieve the WlSeat: {:?}", e)))?; + + let input_devices = WaylandInput::new(&seat); + let compositor = globals.instantiate_exact::(4).map_err(|e| { + Error::WindowCreate(format!("Failed to retrieve the compositor: {:?}", e)) + })?; + let shm = globals + .instantiate_exact::(1) + .map_err(|e| Error::WindowCreate(format!("Failed to create shared memory: {:?}", e)))?; + + let surface = compositor.create_surface(); + + // Specify format + let format = if alpha { + Format::Argb8888 + } else { + Format::Xrgb8888 + }; + + // Retrive shm buffer for writing + let mut buf_pool = BufferPool::new(shm.clone(), format); + let (mut tempfile, buffer) = buf_pool + .get_buffer(size) + .map_err(|e| Error::WindowCreate(format!("Failed to retrieve Buffer: {:?}", e)))?; + + // Add a black canvas into the framebuffer + let frame: Vec = vec![0xFF00_0000; (size.0 * size.1) as usize]; + let slice = unsafe { + slice::from_raw_parts( + frame[..].as_ptr() as *const u8, + frame.len() * mem::size_of::(), + ) + }; + tempfile + .write_all(&slice[..]) + .map_err(|e| Error::WindowCreate(format!("Io Error: {:?}", e)))?; + tempfile + .flush() + .map_err(|e| Error::WindowCreate(format!("Io Error: {:?}", e)))?; + + let xdg_wm_base = globals.instantiate_exact::(1).map_err(|e| { + Error::WindowCreate(format!("Failed to retrieve the XdgWmBase: {:?}", e)) + })?; + + // Reply to ping event + xdg_wm_base.quick_assign(|xdg_wm_base, event, _| { + use wayland_protocols::xdg_shell::client::xdg_wm_base::Event; + + if let Event::Ping { serial } = event { + xdg_wm_base.pong(serial); + } + }); + + let xdg_surface = xdg_wm_base.get_xdg_surface(&surface); + let surface_clone = surface.clone(); + + // Handle configure event + xdg_surface.quick_assign(move |xdg_surface, event, _| { + use wayland_protocols::xdg_shell::client::xdg_surface::Event; + + if let Event::Configure { serial } = event { + xdg_surface.ack_configure(serial); + surface_clone.commit(); + } + }); + + // Assign the toplevel role and commit + let xdg_toplevel = xdg_surface.get_toplevel(); + + if decorate { + if let Ok(decorations) = globals + .instantiate_exact::(1) + .map_err(|e| println!("Failed to create server-side surface decoration: {:?}", e)) + { + decorations.get_toplevel_decoration(&xdg_toplevel); + decorations.destroy(); + } + } + + surface.commit(); + event_queue + .sync_roundtrip(&mut (), |_, _, _| {}) + .map_err(|e| Error::WindowCreate(format!("Roundtrip failed: {:?}", e)))?; + + // Give the buffer to the surface and commit + surface.attach(Some(&buffer), 0, 0); + surface.damage(0, 0, i32::max_value(), i32::max_value()); + surface.commit(); + + let xdg_config = Rc::new(RefCell::new(None)); + let xdg_config_clone = xdg_config.clone(); + + xdg_surface.quick_assign(move |_xdg_surface, event, _| { + use wayland_protocols::xdg_shell::client::xdg_surface::Event; + + // Acknowledge only the last configure + if let Event::Configure { serial } = event { + *xdg_config_clone.borrow_mut() = Some(serial); + } + }); + + let cursor = wayland_cursor::load_theme(None, 16, &shm); + let cursor_surface = compositor.create_surface(); + + Ok(( + Self { + _display: display, + attached_display, + surface, + xdg_surface, + toplevel: xdg_toplevel, + event_queue, + xdg_config, + cursor, + cursor_surface, + buf_pool, + }, + input_devices, + )) + } + + fn set_geometry(&self, pos: (i32, i32), size: (i32, i32)) { + self.xdg_surface + .set_window_geometry(pos.0, pos.1, size.0, size.1); + } + + fn set_title(&self, title: &str) { + self.toplevel.set_title(title.to_owned()); + } + + fn set_no_resize(&self, size: (i32, i32)) { + self.toplevel.set_max_size(size.0, size.1); + self.toplevel.set_min_size(size.0, size.1); + } + + // Sets a specific cursor style + fn update_cursor(&mut self, cursor: &str) -> std::result::Result<(), ()> { + let cursor = self.cursor.get_cursor(cursor); + if let Some(cursor) = cursor { + let img = cursor.frame_buffer(0); + if let Some(img) = img { + self.cursor_surface.attach(Some(&*img), 0, 0); + self.cursor_surface.damage(0, 0, 32, 32); + self.cursor_surface.commit(); + return Ok(()); + } + } + Err(()) + } + + // Resizes when buffer is bigger or less + fn update_framebuffer(&mut self, buffer: &[u32], size: (i32, i32)) -> std::io::Result<()> { + let (mut fd, buf) = self.buf_pool.get_buffer(size)?; + + fd.seek(SeekFrom::Start(0))?; + + let slice = unsafe { + slice::from_raw_parts( + buffer[..].as_ptr() as *const u8, + buffer.len() * mem::size_of::(), + ) + }; + + fd.write_all(&slice[..])?; + fd.flush()?; + + // Acknowledge the last configure event + if let Some(serial) = (*self.xdg_config.borrow_mut()).take() { + self.xdg_surface.ack_configure(serial); + } + + self.surface.attach(Some(buf), 0, 0); + self.surface + .damage(0, 0, i32::max_value(), i32::max_value()); + self.surface.commit(); + + Ok(()) + } + + fn get_toplevel_info(&self) -> (ToplevelResolution, ToplevelClosed) { + let resolution = Rc::new(RefCell::new(None)); + let closed = Rc::new(RefCell::new(false)); + + let resolution_clone = resolution.clone(); + let closed_clone = closed.clone(); + + self.toplevel.quick_assign(move |_, event, _| { + use wayland_protocols::xdg_shell::client::xdg_toplevel::Event; + + if let Event::Configure { width, height, .. } = event { + *resolution_clone.borrow_mut() = Some((width, height)); + } else if let Event::Close = event { + *closed_clone.borrow_mut() = true; + } + }); + + (resolution, closed) + } +} + +struct WaylandInput { + kb_events: mpsc::Receiver, + pt_events: mpsc::Receiver, + _keyboard: Main, + pointer: Main, +} + +impl WaylandInput { + fn new(seat: &Main) -> Self { + let (keyboard, pointer) = (seat.get_keyboard(), seat.get_pointer()); + let (kb_sender, kb_receiver) = mpsc::sync_channel(1024); + + keyboard.quick_assign(move |_, event, _| { + kb_sender.send(event).unwrap(); + }); + + let (pt_sender, pt_receiver) = mpsc::sync_channel(1024); + + pointer.quick_assign(move |_, event, _| { + pt_sender.send(event).unwrap(); + }); + + Self { + kb_events: kb_receiver, + pt_events: pt_receiver, + _keyboard: keyboard, + pointer, + } + } + + fn get_pointer(&self) -> &Main { + &self.pointer + } + + fn iter_keyboard_events(&self) -> mpsc::TryIter { + self.kb_events.try_iter() + } + + fn iter_pointer_events(&self) -> mpsc::TryIter { + self.pt_events.try_iter() + } +} + +pub struct Window { + display: DisplayInfo, + + width: i32, + height: i32, + + scale: i32, + bg_color: u32, + scale_mode: ScaleMode, + + mouse_x: f32, + mouse_y: f32, + scroll_x: f32, + scroll_y: f32, + buttons: [bool; 8], // Linux kernel defines 8 mouse buttons + prev_cursor: CursorStyle, + + should_close: bool, + active: bool, + + key_handler: KeyHandler, + // Option because MaybeUninit's get_ref() is nightly-only + keymap: Option, + update_rate: UpdateRate, + menu_counter: MenuHandle, + menus: Vec, + input: WaylandInput, + resizable: bool, + // Temporary buffer + buffer: Vec, + // Resolution, closed + toplevel_info: (ToplevelResolution, ToplevelClosed), +} + +impl Window { + pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result { + let scale: i32 = match opts.scale { + // Relies on the fact that this is done by the server + // https://docs.rs/winit/0.22.0/winit/dpi/index.html#how-is-the-scale-factor-calculated + Scale::FitScreen => 1, + + Scale::X1 => 1, + Scale::X2 => 2, + Scale::X4 => 4, + Scale::X8 => 8, + Scale::X16 => 16, + Scale::X32 => 32, + }; + + let (display, input) = DisplayInfo::new( + (width as i32 * scale, height as i32 * scale), + false, + !opts.borderless, + )?; + + if opts.title { + display.set_title(name); + } + if !opts.resize { + display.set_no_resize((width as i32 * scale, height as i32 * scale)); + } + + let (resolution, closed) = display.get_toplevel_info(); + + Ok(Self { + display, + + width: width as i32 * scale, + height: height as i32 * scale, + + scale, + bg_color: 0, + scale_mode: opts.scale_mode, + + mouse_x: 0., + mouse_y: 0., + scroll_x: 0., + scroll_y: 0., + buttons: [false; 8], + prev_cursor: CursorStyle::Arrow, + + should_close: false, + active: false, + + key_handler: KeyHandler::new(), + keymap: None, + update_rate: UpdateRate::new(), + menu_counter: MenuHandle(0), + menus: Vec::new(), + input, + resizable: opts.resize, + buffer: Vec::with_capacity(width * height * scale as usize * scale as usize), + toplevel_info: (resolution, closed), + }) + } + + pub fn set_title(&mut self, title: &str) { + self.display.set_title(title); + } + + pub fn set_background_color(&mut self, bg_color: u32) { + self.bg_color = bg_color; + } + + pub fn is_open(&self) -> bool { + !self.should_close + } + + pub fn get_window_handle(&self) -> *mut c_void { + self.display.surface.as_ref().c_ptr() as *mut c_void + } + + pub fn get_size(&self) -> (usize, usize) { + (self.width as usize, self.height as usize) + } + + pub fn get_keys(&self) -> Option> { + self.key_handler.get_keys() + } + + pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option> { + self.key_handler.get_keys_pressed(repeat) + } + + pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + mouse_handler::get_pos( + mode, + self.mouse_x, + self.mouse_y, + self.scale as f32, + self.width as f32, + self.height as f32, + ) + } + + pub fn get_mouse_down(&self, button: MouseButton) -> bool { + match button { + MouseButton::Left => self.buttons[0], + MouseButton::Right => self.buttons[1], + MouseButton::Middle => self.buttons[2], + } + } + + pub fn get_unscaled_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + mouse_handler::get_pos( + mode, + self.mouse_x, + self.mouse_y, + 1.0, + self.width as f32, + self.height as f32, + ) + } + + pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { + if self.scroll_x.abs() > 0.0 || self.scroll_y.abs() > 0.0 { + Some((self.scroll_x, self.scroll_y)) + } else { + None + } + } + + pub fn is_key_down(&self, key: Key) -> bool { + self.key_handler.is_key_down(key) + } + + pub fn set_position(&mut self, x: isize, y: isize) { + self.display + .set_geometry((x as i32, y as i32), (self.width, self.height)); + } + + pub fn set_rate(&mut self, rate: Option) { + self.update_rate.set_rate(rate); + } + + pub fn set_key_repeat_rate(&mut self, rate: f32) { + self.key_handler.set_key_repeat_delay(rate); + } + + pub fn set_key_repeat_delay(&mut self, delay: f32) { + self.key_handler.set_key_repeat_delay(delay); + } + + pub fn set_input_callback(&mut self, callback: Box) { + self.key_handler.set_input_callback(callback); + } + + pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool { + self.key_handler.is_key_pressed(key, repeat) + } + + pub fn is_key_released(&self, key: Key) -> bool { + !self.key_handler.is_key_released(key) + } + + pub fn update_rate(&mut self) { + self.update_rate.update(); + } + + pub fn is_active(&self) -> bool { + self.active + } + + fn next_menu_handle(&mut self) -> MenuHandle { + let handle = self.menu_counter; + self.menu_counter.0 += 1; + + handle + } + + pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { + let handle = self.next_menu_handle(); + let mut menu = menu.internal.clone(); + menu.handle = handle; + self.menus.push(menu); + + handle + } + + pub fn get_unix_menus(&self) -> Option<&Vec> { + //FIXME + unimplemented!() + } + + pub fn remove_menu(&mut self, handle: MenuHandle) { + self.menus.retain(|ref menu| menu.handle != handle); + } + + pub fn is_menu_pressed(&mut self) -> Option { + //FIXME + unimplemented!() + } + + pub fn update(&mut self) { + self.display + .event_queue + .dispatch(&mut (), |_, _, _| {}) + .map_err(|e| Error::WindowCreate(format!("Event dispatch failed: {:?}", e))) + .unwrap(); + + if let Some(resize) = (*self.toplevel_info.0.borrow_mut()).take() { + // Don't try to resize to 0x0 + if self.resizable && resize != (0, 0) { + self.width = resize.0; + self.height = resize.1; + } + } + if *self.toplevel_info.1.borrow() { + self.should_close = true; + } + + for event in self.input.iter_keyboard_events() { + use wayland_client::protocol::wl_keyboard::Event; + + match event { + Event::Keymap { format, fd, size } => { + self.keymap = Some(Self::handle_keymap(format, fd, size).unwrap()); + } + Event::Enter { .. } => { + self.active = true; + } + Event::Leave { .. } => { + self.active = false; + } + Event::Key { key, state, .. } => { + if let Some(ref keymap) = self.keymap { + Self::handle_key( + keymap, + key + KEY_XKB_OFFSET, + state, + &mut self.key_handler, + ); + } + } + Event::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } => { + if let Some(ref keymap) = self.keymap { + let mut state = keymap.state(); + let mut update = state.update(); + update.mask(mods_depressed, mods_latched, mods_locked, 0, 0, group); + } + } + _ => {} + } + } + + self.scroll_x = 0.; + self.scroll_y = 0.; + + for event in self.input.iter_pointer_events() { + use wayland_client::protocol::wl_pointer::Event; + + match event { + Event::Enter { + serial, + surface_x, + surface_y, + .. + } => { + self.mouse_x = surface_x as f32; + self.mouse_y = surface_y as f32; + + self.input.get_pointer().set_cursor( + serial, + Some(&self.display.cursor_surface), + 0, + 0, + ); + self.display + .update_cursor(Self::decode_cursor(self.prev_cursor)) + .unwrap(); + } + Event::Motion { + surface_x, + surface_y, + .. + } => { + self.mouse_x = surface_x as f32; + self.mouse_y = surface_y as f32; + } + Event::Button { button, state, .. } => { + use wayland_client::protocol::wl_pointer::ButtonState; + + let pressed = state == ButtonState::Pressed; + + match button { + // Left mouse button + KEY_MOUSE_BTN1 => self.buttons[0] = pressed, + // Right mouse button + KEY_MOUSE_BTN2 => self.buttons[1] = pressed, + // Middle mouse button + KEY_MOUSE_BTN3 => self.buttons[2] = pressed, + _ => { + // TODO: handle more mouse buttons (see: linux/input-event-codes.h from + // the Linux kernel) + } + } + } + Event::Axis { axis, value, .. } => { + use wayland_client::protocol::wl_pointer::Axis; + + match axis { + Axis::VerticalScroll => self.scroll_y = value as f32, + Axis::HorizontalScroll => self.scroll_x = value as f32, + _ => {} + } + } + Event::Frame {} => { + // TODO + } + Event::AxisSource { axis_source } => { + let _ = axis_source; + // TODO + } + Event::AxisStop { axis, .. } => { + use wayland_client::protocol::wl_pointer::Axis; + + match axis { + Axis::VerticalScroll => self.scroll_y = 0., + Axis::HorizontalScroll => self.scroll_x = 0., + _ => {} + } + } + Event::AxisDiscrete { axis, discrete } => { + let _ = (axis, discrete); + // TODO + } + _ => {} + } + } + + self.key_handler.update(); + } + + fn handle_key( + keymap: &Keymap, + key: u32, + state: wl_keyboard::KeyState, + key_handler: &mut KeyHandler, + ) { + let is_down = state == wl_keyboard::KeyState::Pressed; + let state = keymap.state(); + let key_xkb = state.key(key); + + if let Some(keysym) = key_xkb.sym() { + use xkb::key; + + let key_i = match keysym { + key::_0 => Key::Key0, + key::_1 => Key::Key1, + key::_2 => Key::Key2, + key::_3 => Key::Key3, + key::_4 => Key::Key4, + key::_5 => Key::Key5, + key::_6 => Key::Key6, + key::_7 => Key::Key7, + key::_8 => Key::Key8, + key::_9 => Key::Key9, + + key::a => Key::A, + key::b => Key::B, + key::c => Key::C, + key::d => Key::D, + key::e => Key::E, + key::f => Key::F, + key::g => Key::G, + key::h => Key::H, + key::i => Key::I, + key::j => Key::J, + key::k => Key::K, + key::l => Key::L, + key::m => Key::M, + key::n => Key::N, + key::o => Key::O, + key::p => Key::P, + key::q => Key::Q, + key::r => Key::R, + key::s => Key::S, + key::t => Key::T, + key::u => Key::U, + key::v => Key::V, + key::w => Key::W, + key::x => Key::X, + key::y => Key::Y, + key::z => Key::Z, + + key::apostrophe => Key::Apostrophe, + key::grave => Key::Backquote, + key::backslash => Key::Backslash, + key::comma => Key::Comma, + key::equal => Key::Equal, + key::bracketleft => Key::LeftBracket, + key::bracketright => Key::RightBracket, + key::minus => Key::Minus, + key::period => Key::Period, + key::semicolon => Key::Semicolon, + key::slash => Key::Slash, + key::space => Key::Space, + + key::F1 => Key::F1, + key::F2 => Key::F2, + key::F3 => Key::F3, + key::F4 => Key::F4, + key::F5 => Key::F5, + key::F6 => Key::F6, + key::F7 => Key::F7, + key::F8 => Key::F8, + key::F9 => Key::F9, + key::F10 => Key::F10, + key::F11 => Key::F11, + key::F12 => Key::F12, + + key::Down => Key::Down, + key::Left => Key::Left, + key::Right => Key::Right, + key::Up => Key::Up, + key::Escape => Key::Escape, + key::BackSpace => Key::Backspace, + key::Delete => Key::Delete, + key::End => Key::End, + key::Return => Key::Enter, + key::Home => Key::Home, + key::Insert => Key::Insert, + key::Menu => Key::Menu, + key::Page_Down => Key::PageDown, + key::Page_Up => Key::PageUp, + key::Pause => Key::Pause, + key::Tab => Key::Tab, + key::Num_Lock => Key::NumLock, + key::Caps_Lock => Key::CapsLock, + key::Scroll_Lock => Key::ScrollLock, + key::Shift_L => Key::LeftShift, + key::Shift_R => Key::RightShift, + key::Alt_L => Key::LeftAlt, + key::Alt_R => Key::RightAlt, + key::Control_L => Key::LeftCtrl, + key::Control_R => Key::RightCtrl, + key::Super_L => Key::LeftSuper, + key::Super_R => Key::RightSuper, + + key::KP_Insert => Key::NumPad0, + key::KP_End => Key::NumPad1, + key::KP_Down => Key::NumPad2, + key::KP_Next => Key::NumPad3, + key::KP_Left => Key::NumPad4, + key::KP_Begin => Key::NumPad5, + key::KP_Right => Key::NumPad6, + key::KP_Home => Key::NumPad7, + key::KP_Up => Key::NumPad8, + key::KP_Prior => Key::NumPad9, + key::KP_Decimal => Key::NumPadDot, + key::KP_Divide => Key::NumPadSlash, + key::KP_Multiply => Key::NumPadAsterisk, + key::KP_Subtract => Key::NumPadMinus, + key::KP_Add => Key::NumPadPlus, + key::KP_Enter => Key::NumPadEnter, + + _ => { + // Ignore other keys + return; + } + }; + + key_handler.set_key_state(key_i, is_down); + } + } + + fn handle_keymap(keymap: KeymapFormat, fd: RawFd, len: u32) -> std::io::Result { + match keymap { + KeymapFormat::XkbV1 => { + unsafe { + // Read fd content into Vec + let mut file = File::from_raw_fd(fd); + let mut v = Vec::with_capacity(len as usize); + v.set_len(len as usize); + file.read_exact(&mut v)?; + + let ctx = xkbcommon_sys::xkb_context_new(0); + let kb_map_ptr = xkbcommon_sys::xkb_keymap_new_from_string( + ctx, + v.as_ptr() as *const _ as *const std::os::raw::c_char, + xkbcommon_sys::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_v1, + 0, + ); + + // Wrap keymap + Ok(Keymap::from_ptr(kb_map_ptr as *mut _ as *mut c_void)) + } + } + _ => unimplemented!("Only XKB keymaps are supported"), + } + } + + fn decode_cursor(cursor: CursorStyle) -> &'static str { + match cursor { + CursorStyle::Arrow => "arrow", + CursorStyle::Ibeam => "xterm", + CursorStyle::Crosshair => "crosshair", + CursorStyle::ClosedHand => "hand2", + CursorStyle::OpenHand => "hand2", + CursorStyle::ResizeLeftRight => "sb_h_double_arrow", + CursorStyle::ResizeUpDown => "sb_v_double_arrow", + CursorStyle::ResizeAll => "diamond_cross", + } + } + + pub fn set_cursor_style(&mut self, cursor: CursorStyle) { + if self.prev_cursor != cursor { + self.display + .update_cursor(Self::decode_cursor(cursor)) + .unwrap(); + self.prev_cursor = cursor; + } + } + + pub fn update_with_buffer_stride( + &mut self, + buffer: &[u32], + buf_width: usize, + buf_height: usize, + buf_stride: usize, + ) -> Result<()> { + buffer_helper::check_buffer_size(buf_width, buf_height, buf_width, buffer)?; + + unsafe { self.scale_buffer(buffer, buf_width, buf_height, buf_stride) }; + + self.display + .update_framebuffer(&self.buffer[..], (self.width as i32, self.height as i32)) + .map_err(|e| Error::UpdateFailed(format!("Error updating framebuffer: {:?}", e)))?; + self.update(); + + Ok(()) + } + + unsafe fn scale_buffer( + &mut self, + buffer: &[u32], + buf_width: usize, + buf_height: usize, + buf_stride: usize, + ) { + self.buffer.resize((self.width * self.height) as usize, 0); + + match self.scale_mode { + ScaleMode::Stretch => { + Image_resize_linear_c( + self.buffer.as_mut_ptr(), + buffer.as_ptr(), + buf_width as u32, + buf_height as u32, + buf_stride as u32, + self.width as u32, + self.height as u32, + ); + } + + ScaleMode::AspectRatioStretch => { + Image_resize_linear_aspect_fill_c( + self.buffer.as_mut_ptr(), + buffer.as_ptr(), + buf_width as u32, + buf_height as u32, + buf_stride as u32, + self.width as u32, + self.height as u32, + self.bg_color, + ); + } + + ScaleMode::Center => { + Image_center( + self.buffer.as_mut_ptr(), + buffer.as_ptr(), + buf_width as u32, + buf_height as u32, + buf_stride as u32, + self.width as u32, + self.height as u32, + self.bg_color, + ); + } + + ScaleMode::UpperLeft => { + Image_upper_left( + self.buffer.as_mut_ptr(), + buffer.as_ptr(), + buf_width as u32, + buf_height as u32, + buf_stride as u32, + self.width as u32, + self.height as u32, + self.bg_color, + ); + } + } + } +} + +unsafe impl raw_window_handle::HasRawWindowHandle for Window { + fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + let mut handle = raw_window_handle::unix::WaylandHandle::empty(); + handle.surface = self.display.surface.as_ref().c_ptr() as *mut _ as *mut c_void; + handle.display = self + .display + .attached_display + .clone() + .detach() + .as_ref() + .c_ptr() as *mut _ as *mut c_void; + + raw_window_handle::RawWindowHandle::Wayland(handle) + } +} diff --git a/src/os/unix/x11.rs b/src/os/unix/x11.rs index 492d7df..d20f693 100644 --- a/src/os/unix/x11.rs +++ b/src/os/unix/x11.rs @@ -9,7 +9,7 @@ use x11_dl::xlib; use crate::error::Error; use crate::Result; -use crate::{CursorStyle, MenuHandle, MenuItem, MenuItemHandle, UnixMenu, UnixMenuItem}; +use crate::{CursorStyle, MenuHandle, UnixMenu}; use std::ffi::CString; use std::mem; @@ -20,13 +20,14 @@ use std::ptr; use crate::buffer_helper; use crate::mouse_handler; +use super::common::Menu; + // NOTE: the x11-dl crate does not define Button6 or Button7 const Button6: c_uint = xlib::Button5 + 1; const Button7: c_uint = xlib::Button5 + 2; -// We have these in C so we can always have optimize on (-O3) so they -// run fast in debug build as well. These functions should be seen as -// "system" functions that just doesn't exist in X11 +// These functions are implemented in C in order to always have +// optimizations on (`-O3`), allowing debug builds to run fast as well. extern "C" { fn Image_upper_left( target: *mut u32, @@ -1103,59 +1104,3 @@ impl Drop for Window { } } } - -pub struct Menu { - pub internal: UnixMenu, -} - -impl Menu { - pub fn new(name: &str) -> Result { - Ok(Menu { - internal: UnixMenu { - handle: MenuHandle(0), - item_counter: MenuItemHandle(0), - name: name.to_owned(), - items: Vec::new(), - }, - }) - } - - pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) { - let handle = self.next_item_handle(); - self.internal.items.push(UnixMenuItem { - label: name.to_owned(), - handle, - sub_menu: Some(Box::new(sub_menu.internal.clone())), - id: 0, - enabled: true, - key: Key::Unknown, - modifier: 0, - }); - } - - fn next_item_handle(&mut self) -> MenuItemHandle { - let handle = self.internal.item_counter; - self.internal.item_counter.0 += 1; - handle - } - - pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { - let item_handle = self.next_item_handle(); - self.internal.items.push(UnixMenuItem { - sub_menu: None, - handle: self.internal.item_counter, - id: item.id, - label: item.label.clone(), - enabled: item.enabled, - key: item.key, - modifier: item.modifier, - }); - item_handle - } - - pub fn remove_item(&mut self, handle: &MenuItemHandle) { - self.internal - .items - .retain(|ref item| item.handle.0 != handle.0); - } -}