1
1
mirror of https://github.com/wez/wezterm.git synced 2024-08-17 10:10:23 +03:00

Nuke Wayland

This commit is contained in:
Timmy Xiao 2024-01-03 20:22:07 -05:00 committed by Wez Furlong
parent 9df250f1d2
commit 3eaba4e3d6
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
4 changed files with 83 additions and 627 deletions

104
Cargo.lock generated
View File

@ -3520,18 +3520,6 @@ dependencies = [
"memoffset 0.6.5",
]
[[package]]
name = "nix"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.6.5",
]
[[package]]
name = "nix"
version = "0.25.1"
@ -5039,20 +5027,23 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]]
name = "smithay-client-toolkit"
version = "0.16.1"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9"
checksum = "e1476c3d89bb67079264b88aaf4f14358353318397e083b7c4e8c14517f55de7"
dependencies = [
"bitflags 1.3.2",
"dlib",
"lazy_static",
"log",
"memmap2 0.5.10",
"nix 0.24.3",
"pkg-config",
"nix 0.26.4",
"thiserror",
"wayland-backend",
"wayland-client",
"wayland-cursor",
"wayland-protocols",
"wayland-protocols-wlr",
"wayland-scanner",
]
[[package]]
@ -6092,84 +6083,97 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
[[package]]
name = "wayland-client"
version = "0.29.5"
name = "wayland-backend"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
checksum = "41b48e27457e8da3b2260ac60d0a94512f5cba36448679f3747c0865b7893ed8"
dependencies = [
"bitflags 1.3.2",
"cc",
"downcast-rs",
"libc",
"nix 0.24.3",
"io-lifetimes",
"nix 0.26.4",
"scoped-tls",
"wayland-commons",
"wayland-scanner",
"wayland-sys",
]
[[package]]
name = "wayland-commons"
version = "0.29.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
dependencies = [
"nix 0.24.3",
"once_cell",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-cursor"
version = "0.29.5"
name = "wayland-client"
version = "0.30.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8"
dependencies = [
"nix 0.24.3",
"bitflags 1.3.2",
"nix 0.26.4",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-cursor"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d0c3a0d5b4b688b07b0442362d3ed6bf04724fcc16cd69ab6285b90dbc487aa"
dependencies = [
"nix 0.26.4",
"wayland-client",
"xcursor",
]
[[package]]
name = "wayland-egl"
version = "0.29.5"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d"
checksum = "1187695fe81c3153c3163f9d2953149f638c5d7dbc6fe988914ca3f4961e28ed"
dependencies = [
"wayland-client",
"wayland-backend",
"wayland-sys",
]
[[package]]
name = "wayland-protocols"
version = "0.29.5"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
checksum = "3b28101e5ca94f70461a6c2d610f76d85ad223d042dd76585ab23d3422dd9b4d"
dependencies = [
"bitflags 1.3.2",
"wayland-backend",
"wayland-client",
"wayland-commons",
"wayland-scanner",
]
[[package]]
name = "wayland-protocols-wlr"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a"
dependencies = [
"bitflags 1.3.2",
"wayland-backend",
"wayland-client",
"wayland-protocols",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.29.5"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
checksum = "b9b873b257fbc32ec909c0eb80dea312076a67014e65e245f5eb69a6b8ab330e"
dependencies = [
"proc-macro2",
"quick-xml 0.28.2",
"quote",
"xml-rs",
]
[[package]]
name = "wayland-sys"
version = "0.29.5"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06"
dependencies = [
"dlib",
"log",
"pkg-config",
]

View File

@ -70,20 +70,22 @@ winreg = "0.10"
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
dirs-next = "2.0"
filedescriptor = { version="0.8", path = "../filedescriptor" }
futures-util = "0.3"
futures-lite = "1.12"
x11 = {version ="2.21", features = ["xlib_xcb", "xlib"]}
xcb = {version="1.3", features=["render", "randr", "dri2", "xkb", "xlib_xcb", "present", "as-raw-xcb-connection"]}
xkbcommon = { version = "0.7.0", features = ["x11", "wayland"] }
mio = {version="0.8", features=["os-ext"]}
libc = "0.2"
smithay-client-toolkit = {version = "0.16.1", default-features=false, optional=true}
wayland-protocols = {version="0.29", optional=true}
wayland-client = {version="0.29", optional=true}
wayland-egl = {version="0.29", optional=true}
xcb-imdkit = { version="0.3", git="https://github.com/wez/xcb-imdkit-rs.git", rev="215ce4b08ac9c4822e541efd4f4ffb1062806051"}
zbus = "3.14"
zvariant = "3.15"
futures-util = "0.3"
futures-lite = "1.12"
smithay-client-toolkit = {version = "0.17.0", default-features=false, optional=true}
wayland-protocols = {version="0.30", optional=true}
wayland-client = {version="0.30", optional=true}
wayland-egl = {version="0.30", optional=true}
xcb-imdkit = { version="0.2", git="https://github.com/wez/xcb-imdkit-rs.git", branch="hangfix"}
[target.'cfg(target_os="macos")'.dependencies]
cocoa = "0.25"

View File

@ -1,552 +1 @@
#![allow(dead_code)]
use super::pointer::*;
use super::window::*;
use crate::connection::ConnectionOps;
use crate::os::wayland::inputhandler::InputHandler;
use crate::os::wayland::output::OutputHandler;
use crate::os::x11::keyboard::KeyboardWithFallback;
use crate::screen::{ScreenInfo, Screens};
use crate::spawn::*;
use crate::{Appearance, Connection, ScreenRect, WindowEvent};
use anyhow::{bail, Context};
use mio::unix::SourceFd;
use mio::{Events, Interest, Poll, Token};
use smithay_client_toolkit as toolkit;
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::Read;
use std::os::unix::fs::FileExt;
use std::os::unix::io::FromRawFd;
use std::rc::Rc;
use std::sync::atomic::AtomicUsize;
use toolkit::environment::Environment;
use toolkit::reexports::client::Display;
use toolkit::seat::SeatListener;
use toolkit::shm::AutoMemPool;
use wayland_client::protocol::wl_keyboard::{Event as WlKeyboardEvent, KeymapFormat, WlKeyboard};
use wayland_client::{EventQueue, Main};
toolkit::default_environment!(MyEnvironment, desktop,
fields=[
output_handler: OutputHandler,
input_handler: InputHandler,
],
singles=[
wayland_protocols::wlr::unstable::output_management::v1::client::zwlr_output_manager_v1::ZwlrOutputManagerV1 => output_handler,
wayland_protocols::unstable::text_input::v3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3 => input_handler,
]);
impl MyEnvironment {
pub fn input_handler(&mut self) -> &mut InputHandler {
&mut self.input_handler
}
}
pub struct WaylandConnection {
should_terminate: RefCell<bool>,
pub(crate) next_window_id: AtomicUsize,
pub(crate) windows: RefCell<HashMap<usize, Rc<RefCell<WaylandWindowInner>>>>,
// Take care with the destruction order: the underlying wayland
// libraries are not safe and require destruction in reverse
// creation order. This list of fields must reflect that otherwise
// we'll segfault on shutdown.
// Rust guarantees that struct fields are dropped in the order
// they appear in the struct, so the Display must be at the
// bottom of this list, and opengl, which depends on everything
// must be ahead of the rest.
pub(crate) gl_connection: RefCell<Option<Rc<crate::egl::GlConnection>>>,
pub(crate) pointer: RefCell<PointerDispatcher>,
pub(crate) keyboard_mapper: RefCell<Option<KeyboardWithFallback>>,
pub(crate) keyboard_window_id: RefCell<Option<usize>>,
pub(crate) surface_to_window_id: RefCell<HashMap<u32, usize>>,
pub(crate) active_surface_id: RefCell<u32>,
/// Repeats per second
pub(crate) key_repeat_rate: RefCell<i32>,
pub(crate) mem_pool: RefCell<AutoMemPool>,
/// Delay before repeating, in milliseconds
pub(crate) key_repeat_delay: RefCell<i32>,
pub(crate) last_serial: RefCell<u32>,
seat_listener: SeatListener,
pub(crate) environment: Environment<MyEnvironment>,
event_q: RefCell<EventQueue>,
pub(crate) display: RefCell<Display>,
}
impl Drop for WaylandConnection {
fn drop(&mut self) {
self.environment
.with_inner(|env| env.input_handler.shutdown());
}
}
impl WaylandConnection {
pub fn create_new() -> anyhow::Result<Self> {
let (environment, display, event_q) = toolkit::new_default_environment!(
MyEnvironment,
desktop,
fields = [
output_handler: OutputHandler::new(),
input_handler: InputHandler::new(),
]
)?;
let mut pointer = None;
let mut seat_keyboards = HashMap::new();
for seat in environment.get_all_seats() {
if let Some((has_kbd, has_ptr, name)) =
toolkit::seat::with_seat_data(&seat, |seat_data| {
(
seat_data.has_keyboard && !seat_data.defunct,
seat_data.has_pointer && !seat_data.defunct,
seat_data.name.clone(),
)
})
{
if has_kbd {
let keyboard = seat.get_keyboard();
keyboard.quick_assign(|keyboard, event, _| {
let conn = Connection::get().unwrap().wayland();
if let Err(err) = conn.keyboard_event(keyboard, event) {
log::error!("keyboard_event: {:#}", err);
}
});
environment.with_inner(|env| env.input_handler.advise_seat(&seat, &keyboard));
seat_keyboards.insert(name, keyboard);
}
if has_ptr {
pointer.replace(PointerDispatcher::register(
&seat,
environment.require_global(),
environment.require_global(),
environment.require_global(),
environment.get_primary_selection_manager(),
)?);
}
}
}
let pointer =
pointer.ok_or_else(|| anyhow::anyhow!("no seats have an available pointer"))?;
let seat_listener;
{
let env = environment.clone();
seat_listener = environment.listen_for_seats(move |seat, seat_data, _| {
if seat_data.has_keyboard {
if !seat_data.defunct {
// We only want to assign a new keyboard object if we don't already have
// one for this seat. When a seat is being created or updated, the listener
// can receive the same seat multiple times: for example, when switching
// back from another virtual console, the same seat is usually seen four
// times with different data flags:
//
// has_pointer: true; has_keyboard: false
// has_pointer: false; has_keyboard: false
// has_pointer: false; has_keyboard: true
// has_pointer: true; has_keyboard: true
//
// This is essentially telling the client to re-assign its keyboard and
// pointer, but that means that this listener will fire twice with
// has_keyboard set to true. If we assign a handler both times, then we end
// up handling key events twice.
if !seat_keyboards.contains_key(&seat_data.name) {
let keyboard = seat.get_keyboard();
keyboard.quick_assign(|keyboard, event, _| {
let conn = Connection::get().unwrap().wayland();
if let Err(err) = conn.keyboard_event(keyboard, event) {
log::error!("keyboard_event: {:#}", err);
}
});
env.with_inner(|env| env.input_handler.advise_seat(&seat, &keyboard));
seat_keyboards.insert(seat_data.name.clone(), keyboard);
}
} else {
env.with_inner(|env| env.input_handler.seat_defunct(&seat));
}
} else {
// If we previously had a keyboard object on this seat, it's no longer valid if
// has_keyboard is false, so we remove the keyboard object we knew about and
// thereby ensure that we assign a new keyboard object next time the listener
// fires for this seat with has_keyboard = true.
seat_keyboards.remove(&seat_data.name);
}
if seat_data.has_pointer && !seat_data.defunct {
let conn = Connection::get().unwrap().wayland();
conn.pointer.borrow_mut().seat_changed(&seat);
}
});
}
let mem_pool = environment.create_auto_pool()?;
Ok(Self {
display: RefCell::new(display),
environment,
should_terminate: RefCell::new(false),
next_window_id: AtomicUsize::new(1),
windows: RefCell::new(HashMap::new()),
event_q: RefCell::new(event_q),
pointer: RefCell::new(pointer),
seat_listener,
mem_pool: RefCell::new(mem_pool),
gl_connection: RefCell::new(None),
keyboard_mapper: RefCell::new(None),
key_repeat_rate: RefCell::new(25),
key_repeat_delay: RefCell::new(400),
keyboard_window_id: RefCell::new(None),
last_serial: RefCell::new(0),
surface_to_window_id: RefCell::new(HashMap::new()),
active_surface_id: RefCell::new(0),
})
}
fn keyboard_event(
&self,
keyboard: Main<WlKeyboard>,
event: WlKeyboardEvent,
) -> anyhow::Result<()> {
match &event {
WlKeyboardEvent::Enter {
serial, surface, ..
} => {
// update global active surface id
*self.active_surface_id.borrow_mut() = surface.as_ref().id();
*self.last_serial.borrow_mut() = *serial;
if let Some(&window_id) = self
.surface_to_window_id
.borrow()
.get(&surface.as_ref().id())
{
self.keyboard_window_id.borrow_mut().replace(window_id);
self.environment.with_inner(|env| {
if let Some(input) =
env.input_handler.get_text_input_for_keyboard(&keyboard)
{
input.enable();
input.commit();
}
env.input_handler.advise_surface(&surface, &keyboard);
});
} else {
log::warn!("{:?}, no known surface", event);
}
}
WlKeyboardEvent::Leave { serial, .. } => {
if let Some(input) = self
.environment
.with_inner(|env| env.input_handler.get_text_input_for_keyboard(&keyboard))
{
input.disable();
input.commit();
}
*self.last_serial.borrow_mut() = *serial;
}
WlKeyboardEvent::Key { serial, .. } | WlKeyboardEvent::Modifiers { serial, .. } => {
*self.last_serial.borrow_mut() = *serial;
}
WlKeyboardEvent::RepeatInfo { rate, delay } => {
*self.key_repeat_rate.borrow_mut() = *rate;
*self.key_repeat_delay.borrow_mut() = *delay;
}
WlKeyboardEvent::Keymap { format, fd, size } => {
let mut file = unsafe { std::fs::File::from_raw_fd(*fd) };
match format {
KeymapFormat::XkbV1 => {
let mut data = vec![0u8; *size as usize];
// If we weren't passed a pipe, be sure to explicitly
// read from the start of the file
match file.read_exact_at(&mut data, 0) {
Ok(_) => {}
Err(err) => {
// ideally: we check for:
// err.kind() == std::io::ErrorKind::NotSeekable
// but that is not yet in stable rust
if err.raw_os_error() == Some(libc::ESPIPE) {
// It's a pipe, which cannot be seeked, so we
// just try reading from the current pipe position
file.read(&mut data).context("read from Keymap fd/pipe")?;
} else {
return Err(err).context("read_exact_at from Keymap fd");
}
}
}
// Dance around CString panicing on the NUL terminator
// in the xkbcommon crate
while let Some(0) = data.last() {
data.pop();
}
let s = String::from_utf8(data)?;
match KeyboardWithFallback::new_from_string(s) {
Ok(k) => {
self.keyboard_mapper.replace(Some(k));
}
Err(err) => {
log::error!("Error processing keymap change: {:#}", err);
}
}
}
_ => {}
}
}
_ => {}
}
if let Some(&window_id) = self.keyboard_window_id.borrow().as_ref() {
if let Some(win) = self.window_by_id(window_id) {
let mut inner = win.borrow_mut();
inner.keyboard_event(event);
}
}
Ok(())
}
pub(crate) fn dispatch_to_focused_window(&self, event: WindowEvent) {
if let Some(&window_id) = self.keyboard_window_id.borrow().as_ref() {
if let Some(win) = self.window_by_id(window_id) {
let mut inner = win.borrow_mut();
inner.events.dispatch(event);
}
}
}
pub(crate) fn next_window_id(&self) -> usize {
self.next_window_id
.fetch_add(1, ::std::sync::atomic::Ordering::Relaxed)
}
fn flush(&self) -> anyhow::Result<()> {
if let Err(e) = self.display.borrow_mut().flush() {
if e.kind() != ::std::io::ErrorKind::WouldBlock {
bail!("Error while flushing display: {}", e);
}
}
Ok(())
}
pub(crate) fn window_by_id(&self, window_id: usize) -> Option<Rc<RefCell<WaylandWindowInner>>> {
self.windows.borrow().get(&window_id).map(Rc::clone)
}
pub(crate) fn with_window_inner<
R,
F: FnOnce(&mut WaylandWindowInner) -> anyhow::Result<R> + Send + 'static,
>(
window: usize,
f: F,
) -> promise::Future<R>
where
R: Send + 'static,
{
let mut prom = promise::Promise::new();
let future = prom.get_future().unwrap();
promise::spawn::spawn_into_main_thread(async move {
if let Some(handle) = Connection::get().unwrap().wayland().window_by_id(window) {
let mut inner = handle.borrow_mut();
prom.result(f(&mut inner));
}
})
.detach();
future
}
fn run_message_loop_impl(&self) -> anyhow::Result<()> {
const TOK_WL: usize = 0xffff_fffc;
const TOK_SPAWN: usize = 0xffff_fffd;
let tok_wl = Token(TOK_WL);
let tok_spawn = Token(TOK_SPAWN);
let mut poll = Poll::new()?;
let mut events = Events::with_capacity(8);
poll.registry().register(
&mut SourceFd(&self.display.borrow().get_connection_fd()),
tok_wl,
Interest::READABLE,
)?;
poll.registry().register(
&mut SourceFd(&SPAWN_QUEUE.raw_fd()),
tok_spawn,
Interest::READABLE,
)?;
while !*self.should_terminate.borrow() {
// Check the spawn queue before we try to sleep; there may
// be work pending and we don't guarantee that there is a
// 1:1 wakeup to queued function, so we need to be assertive
// in order to avoid missing wakeups
let timeout = if SPAWN_QUEUE.run() {
// if we processed one, we don't want to sleep because
// there may be others to deal with
Some(std::time::Duration::from_secs(0))
} else {
None
};
{
let mut event_q = self.event_q.borrow_mut();
if let Err(err) = event_q.dispatch_pending(&mut (), |_, _, _| {}) {
return Err(err).with_context(|| {
format!(
"error during event_q.dispatch protocol_error={:?}",
self.display.borrow().protocol_error()
)
});
}
}
self.flush()?;
if let Err(err) = poll.poll(&mut events, timeout) {
if err.kind() == std::io::ErrorKind::Interrupted {
continue;
}
bail!("polling for events: {:?}", err);
}
for event in &events {
if event.token() == tok_wl {
let event_q = self.event_q.borrow();
if let Some(guard) = event_q.prepare_read() {
if let Err(err) = guard.read_events() {
if err.kind() != std::io::ErrorKind::WouldBlock
&& err.kind() != std::io::ErrorKind::Interrupted
{
return Err(err).with_context(|| {
format!(
"error during event_q.read_events protocol_error={:?}",
self.display.borrow().protocol_error()
)
});
}
}
}
}
}
}
Ok(())
}
pub(crate) fn advise_of_appearance_change(&self, appearance: crate::Appearance) {
for win in self.windows.borrow().values() {
win.borrow_mut().appearance_changed(appearance);
}
}
}
impl ConnectionOps for WaylandConnection {
fn name(&self) -> String {
"Wayland".to_string()
}
fn terminate_message_loop(&self) {
*self.should_terminate.borrow_mut() = true;
}
fn get_appearance(&self) -> Appearance {
match promise::spawn::block_on(crate::os::xdg_desktop_portal::get_appearance()) {
Ok(Some(appearance)) => return appearance,
Ok(None) => {}
Err(err) => {
log::debug!("Unable to resolve appearance using xdg-desktop-portal: {err:#}");
}
}
// fallback
Appearance::Light
}
fn run_message_loop(&self) -> anyhow::Result<()> {
let res = self.run_message_loop_impl();
// Ensure that we drop these eagerly, to avoid
// noisy errors wrt. global destructors unwinding
// in unexpected places
self.windows.borrow_mut().clear();
res
}
fn screens(&self) -> anyhow::Result<Screens> {
if let Some(screens) = self
.environment
.with_inner(|env| env.output_handler.screens())
{
return Ok(screens);
}
let mut by_name = HashMap::new();
let mut virtual_rect: ScreenRect = euclid::rect(0, 0, 0, 0);
let config = config::configuration();
for output in self.environment.get_all_outputs() {
toolkit::output::with_output_info(&output, |info| {
let name = if info.name.is_empty() {
format!("{} {}", info.model, info.make)
} else {
info.name.clone()
};
let (width, height) = info
.modes
.iter()
.find(|mode| mode.is_current)
.map(|mode| mode.dimensions)
.unwrap_or((info.physical_size.0, info.physical_size.1));
let rect = euclid::rect(
info.location.0 as isize,
info.location.1 as isize,
width as isize,
height as isize,
);
let scale = info.scale_factor as f64;
// FIXME: teach this how to resolve dpi_by_screen once
// dispatch_pending_event knows how to do the same
let effective_dpi = Some(config.dpi.unwrap_or(scale * crate::DEFAULT_DPI));
virtual_rect = virtual_rect.union(&rect);
by_name.insert(
name.clone(),
ScreenInfo {
name,
rect,
scale,
max_fps: None,
effective_dpi,
},
);
});
}
// The main screen is the one either at the origin of
// the virtual area, or if that doesn't exist for some weird
// reason, the screen closest to the origin.
let main = by_name
.values()
.min_by_key(|screen| {
screen
.rect
.origin
.to_f32()
.distance_to(euclid::Point2D::origin())
.abs() as isize
})
.ok_or_else(|| anyhow::anyhow!("no screens were found"))?
.clone();
// We don't yet know how to determine the active screen,
// so assume the main screen.
let active = main.clone();
Ok(Screens {
main,
active,
by_name,
virtual_rect,
})
}
}
pub struct WaylandConnection {}

View File

@ -1,26 +1,27 @@
#![cfg(all(unix, not(target_os = "macos")))]
pub mod connection;
pub mod inputhandler;
pub mod output;
pub mod window;
pub use self::window::*;
// pub mod inputhandler;
// pub mod output;
// pub mod window;
// pub use self::window::*;
pub use connection::*;
pub use output::*;
mod copy_and_paste;
mod drag_and_drop;
mod frame;
mod pointer;
// pub use output::*;
// mod copy_and_paste;
// mod drag_and_drop;
// mod frame;
// mod pointer;
/// Returns the id of a wayland proxy object, suitable for using
/// a key into hash maps
pub fn wl_id<I, T>(obj: T) -> u32
where
I: wayland_client::Interface,
T: AsRef<wayland_client::Proxy<I>>,
I: AsRef<wayland_client::Proxy<I>>,
I: From<wayland_client::Proxy<I>>,
{
let proxy: &wayland_client::Proxy<I> = obj.as_ref();
proxy.id()
}
pub fn todo() {}
// pub fn wl_id<I, T>(obj: T) -> u32
// where
// I: wayland_client::Interface,
// T: AsRef<wayland_client::Proxy<I>>,
// I: AsRef<wayland_client::Proxy<I>>,
// I: From<wayland_client::Proxy<I>>,
// {
// let proxy: &wayland_client::Proxy<I> = obj.as_ref();
// proxy.id()
// }