Comments (4)
I have the following diff on top of current master (commit 4d012e1) and it "should be enough". This gets enough things going that I get a working xterm
. I did not try any other X11 apps.
diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml
index 4dbfd7c1..68d417a6 100644
--- a/anvil/Cargo.toml
+++ b/anvil/Cargo.toml
@@ -15,11 +15,17 @@ slog = { version = "2.1.1" }
slog-term = "2.3"
slog-async = "2.2"
xkbcommon = "0.4.0"
+once_cell = "1.5.2"
[dependencies.smithay]
path = ".."
default-features = false
-features = [ "renderer_glium", "backend_egl", "wayland_frontend" ]
+features = [ "renderer_glium", "backend_egl", "wayland_frontend", "xwayland" ]
+
+[dependencies.x11rb]
+default-features = false
+version = "0.7.0"
+features = [ "composite" ]
[build-dependencies]
gl_generator = "0.14"
diff --git a/anvil/src/main.rs b/anvil/src/main.rs
index f6d28ccd..3340827f 100644
--- a/anvil/src/main.rs
+++ b/anvil/src/main.rs
@@ -25,6 +25,7 @@ mod udev;
mod window_map;
#[cfg(feature = "winit")]
mod winit;
+mod xwayland;
use state::AnvilState;
@@ -36,6 +37,8 @@ static POSSIBLE_BACKENDS: &[&str] = &[
];
fn main() {
+ let helper = unsafe { smithay::xwayland::LaunchHelper::fork() }.unwrap();
+
// A logger facility, here we use the terminal here
let log = slog::Logger::root(
slog_async::Async::default(slog_term::term_full().fuse()).fuse(),
@@ -50,14 +53,14 @@ fn main() {
#[cfg(feature = "winit")]
Some("--winit") => {
info!(log, "Starting anvil with winit backend");
- if let Err(()) = winit::run_winit(display, &mut event_loop, log.clone()) {
+ if let Err(()) = winit::run_winit(display, &mut event_loop, helper, log.clone()) {
crit!(log, "Failed to initialize winit backend.");
}
}
#[cfg(feature = "udev")]
Some("--tty-udev") => {
info!(log, "Starting anvil on a tty using udev");
- if let Err(()) = udev::run_udev(display, &mut event_loop, log.clone()) {
+ if let Err(()) = udev::run_udev(display, &mut event_loop, helper, log.clone()) {
crit!(log, "Failed to initialize tty backend.");
}
}
diff --git a/anvil/src/shell.rs b/anvil/src/shell.rs
index f3e9d8ff..5d241a23 100644
--- a/anvil/src/shell.rs
+++ b/anvil/src/shell.rs
@@ -36,11 +36,13 @@ use smithay::{
use crate::{
buffer_utils::BufferUtils,
window_map::{Kind as SurfaceKind, WindowMap},
+ xwayland::X11SurfaceRole,
};
define_roles!(Roles =>
[ XdgSurface, XdgSurfaceRole ]
[ ShellSurface, ShellSurfaceRole]
+ [ X11Surface, X11SurfaceRole ]
[ DnDIcon, DnDIconRole ]
[ CursorImage, CursorImageRole ]
);
@@ -219,6 +221,7 @@ impl PointerGrab for ResizeSurfaceGrab {
(self.last_window_size.0 as u32, self.last_window_size.1 as u32),
self.edges.into(),
),
+ SurfaceKind::X11(_) => eprintln!("XXX Ignoring motion on X11 surface"),
}
}
@@ -768,6 +771,8 @@ fn surface_commit(
buffer_utils: &BufferUtils,
window_map: &RefCell<MyWindowMap>,
) {
+ super::xwayland::commit_hook(surface);
+
let mut geometry = None;
let mut min_size = (0, 0);
let mut max_size = (0, 0);
diff --git a/anvil/src/state.rs b/anvil/src/state.rs
index 48bc6ce8..eac4cdf7 100644
--- a/anvil/src/state.rs
+++ b/anvil/src/state.rs
@@ -21,6 +21,7 @@ use smithay::{
seat::{CursorImageStatus, KeyboardHandle, PointerHandle, Seat, XkbConfig},
shm::init_shm_global,
},
+ xwayland::{LaunchHelper, XWayland},
};
#[cfg(feature = "udev")]
@@ -28,7 +29,7 @@ use smithay::backend::session::{auto::AutoSession, Session};
#[cfg(feature = "udev")]
use crate::udev::MyOutput;
-use crate::{buffer_utils::BufferUtils, shell::init_shell};
+use crate::{buffer_utils::BufferUtils, shell::init_shell, xwayland::XWm};
pub struct AnvilState {
pub socket_name: String,
@@ -51,6 +52,7 @@ pub struct AnvilState {
pub session: Option<AutoSession>,
// things we must keep alive
_wayland_event_source: Source<Generic<Fd>>,
+ xwayland: Option<XWayland<XWm>>,
}
impl AnvilState {
@@ -62,6 +64,7 @@ impl AnvilState {
#[cfg(not(feature = "udev"))] _session: Option<()>,
#[cfg(feature = "udev")] output_map: Option<Rc<RefCell<Vec<MyOutput>>>>,
#[cfg(not(feature = "udev"))] _output_map: Option<()>,
+ helper: LaunchHelper,
log: slog::Logger,
) -> AnvilState {
// init the wayland connection
@@ -153,14 +156,14 @@ impl AnvilState {
})
.expect("Failed to initialize the keyboard");
- AnvilState {
+ let mut state = AnvilState {
running: Arc::new(AtomicBool::new(true)),
- display,
- handle,
+ display: display.clone(),
+ handle: handle.clone(),
ctoken: shell_handles.token,
- window_map: shell_handles.window_map,
+ window_map: shell_handles.window_map.clone(),
dnd_icon,
- log,
+ log: log.clone(),
socket_name,
pointer,
keyboard,
@@ -171,7 +174,15 @@ impl AnvilState {
seat_name,
#[cfg(feature = "udev")]
session,
+ xwayland: None,
_wayland_event_source,
- }
+ };
+
+ info!(log, "Hey Uli: Starting XWayland");
+ let wm = XWm::new(handle.clone(), shell_handles.token, shell_handles.window_map);
+ let xwl = XWayland::init(wm, handle.clone(), display.clone(), &mut state, log.clone(), helper).unwrap();
+ state.xwayland = Some(xwl);
+
+ state
}
}
diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs
index e5a8b596..279938c6 100644
--- a/anvil/src/udev.rs
+++ b/anvil/src/udev.rs
@@ -60,6 +60,7 @@ use smithay::{
output::{Mode, Output, PhysicalProperties},
seat::CursorImageStatus,
},
+ xwayland::LaunchHelper,
};
use crate::buffer_utils::BufferUtils;
@@ -93,6 +94,7 @@ type RenderSurface = FallbackSurface<
pub fn run_udev(
display: Rc<RefCell<Display>>,
event_loop: &mut EventLoop<AnvilState>,
+ helper: LaunchHelper,
log: Logger,
) -> Result<(), ()> {
let name = display
@@ -129,6 +131,7 @@ pub fn run_udev(
buffer_utils.clone(),
Some(session),
Some(output_map.clone()),
+ helper,
log.clone(),
);
diff --git a/anvil/src/window_map.rs b/anvil/src/window_map.rs
index 54eb33f7..1abadd4a 100644
--- a/anvil/src/window_map.rs
+++ b/anvil/src/window_map.rs
@@ -12,11 +12,15 @@ use smithay::{
},
};
-use crate::shell::SurfaceData;
+use crate::{
+ shell::SurfaceData,
+ xwayland::X11Surface,
+};
pub enum Kind<R> {
Xdg(ToplevelSurface<R>),
Wl(ShellSurface<R>),
+ X11(X11Surface<R>),
}
// We implement Clone manually because #[derive(..)] would require R: Clone.
@@ -25,6 +29,7 @@ impl<R> Clone for Kind<R> {
match self {
Kind::Xdg(xdg) => Kind::Xdg(xdg.clone()),
Kind::Wl(wl) => Kind::Wl(wl.clone()),
+ Kind::X11(x11) => Kind::X11(x11.clone()),
}
}
}
@@ -37,12 +42,14 @@ where
match *self {
Kind::Xdg(ref t) => t.alive(),
Kind::Wl(ref t) => t.alive(),
+ Kind::X11(ref t) => t.alive(),
}
}
pub fn get_surface(&self) -> Option<&wl_surface::WlSurface> {
match *self {
Kind::Xdg(ref t) => t.get_surface(),
Kind::Wl(ref t) => t.get_surface(),
+ Kind::X11(ref t) => t.get_surface(),
}
}
@@ -51,6 +58,7 @@ where
match (self, other) {
(Kind::Xdg(a), Kind::Xdg(b)) => a.equals(b),
(Kind::Wl(a), Kind::Wl(b)) => a.equals(b),
+ (Kind::X11(a), Kind::X11(b)) => a.equals(b),
_ => false,
}
}
diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs
index eafbfcf2..9b3f4184 100644
--- a/anvil/src/winit.rs
+++ b/anvil/src/winit.rs
@@ -13,6 +13,7 @@ use smithay::{
output::{Mode, Output, PhysicalProperties},
seat::CursorImageStatus,
},
+ xwayland::LaunchHelper
};
use slog::Logger;
@@ -24,6 +25,7 @@ use crate::state::AnvilState;
pub fn run_winit(
display: Rc<RefCell<Display>>,
event_loop: &mut EventLoop<AnvilState>,
+ helper: LaunchHelper,
log: Logger,
) -> Result<(), ()> {
let (renderer, mut input) = winit::init(log.clone()).map_err(|err| {
@@ -58,6 +60,7 @@ pub fn run_winit(
buffer_utils,
None,
None,
+ helper,
log.clone(),
);
diff --git a/anvil/src/xwayland.rs b/anvil/src/xwayland.rs
new file mode 100644
index 00000000..4ca13262
--- /dev/null
+++ b/anvil/src/xwayland.rs
@@ -0,0 +1,285 @@
+use std::{
+ cell::RefCell,
+ collections::HashMap,
+ convert::TryFrom,
+ rc::Rc,
+ sync::Mutex,
+ os::unix::{net::UnixStream, io::AsRawFd},
+};
+
+use smithay::{
+ reexports::{
+ calloop::{
+ generic::{Fd, Generic},
+ Interest, LoopHandle, Mode, Source,
+ },
+ wayland_server::{protocol::wl_surface::WlSurface, Client},
+ },
+ wayland::compositor::CompositorToken,
+ xwayland::{XWindowManager},
+};
+
+use x11rb::{
+ connection::Connection as _,
+ rust_connection::{RustConnection, DefaultStream},
+ protocol::{
+ composite::{ConnectionExt as _, Redirect},
+ xproto::{
+ ConnectionExt as _,
+ EventMask,
+ ChangeWindowAttributesAux,
+ ConfigWindow,
+ ConfigureWindowAux,
+ Window,
+ WindowClass,
+ },
+ },
+};
+
+use crate::{
+ state::AnvilState,
+ shell::{MyWindowMap, Roles},
+ window_map::Kind,
+};
+
+x11rb::atom_manager! {
+ Atoms: AtomsCookie {
+ WL_SURFACE_ID,
+ }
+}
+
+struct XWmInner {
+ connection: RustConnection,
+ atoms: Atoms,
+ _source: Source<Generic<Fd>>,
+ token: CompositorToken<Roles>,
+ unpaired_surfaces: HashMap<u32, Window>,
+ paired_surfaces: Vec<(Window, WlSurface)>,
+ window_map: Rc<RefCell<MyWindowMap>>,
+}
+
+impl XWmInner {
+ fn new_window(&mut self, window: Window, surface: &WlSurface) {
+ self.paired_surfaces.push((window, surface.clone()));
+ println!("matched X11 window {:#x} and {:?}", window, surface);
+ assert!(self.token.give_role_with(surface, X11SurfaceRole { window }).is_ok());
+ let x11surface = X11Surface {
+ window,
+ wl_surface: surface.clone(),
+ token: self.token,
+ };
+ let _ = x11surface.window; // Hack to silence a compiler warning; just remove this line
+ self.token.with_role_data::<X11SurfaceRole, _, _>(surface, |r| { let _ = r.window; }).unwrap(); // Hack to silence a compiler warning; just remove this line
+ self.window_map.borrow_mut().insert(Kind::X11(x11surface), (0, 0));
+ }
+}
+
+use once_cell::sync::OnceCell;
+static XWAYLAND_CLIENT: OnceCell<Mutex<Option<Client>>> = OnceCell::new();
+
+pub struct XWm {
+ handle: LoopHandle<AnvilState>,
+ token: CompositorToken<Roles>,
+ window_map: Rc<RefCell<MyWindowMap>>,
+}
+
+impl XWm {
+ pub fn new(handle: LoopHandle<AnvilState>, token: CompositorToken<Roles>, window_map: Rc<RefCell<MyWindowMap>>) -> Self {
+ Self { handle, token, window_map }
+ }
+}
+
+impl XWindowManager for XWm {
+ fn xwayland_ready(&mut self, connection: UnixStream, client: Client) {
+ println!("Hey Uli: XWayland is ready, DISPLAY={}", std::env::var("DISPLAY").unwrap_or("NONE".to_string()));
+
+ let (connection, atoms) = start_wm(connection).unwrap();
+ let fd = connection.stream().as_raw_fd();
+ let source = self.handle.insert_source(
+ Generic::new(Fd(fd), Interest::Readable, Mode::Level),
+ |_, _, _| {
+ let guard = XWAYLAND_CLIENT.get().unwrap().lock().unwrap();
+ let client = guard.as_ref().unwrap();
+ let inner = client.data_map().get::<RefCell<XWmInner>>().unwrap();
+ read_events(client, &mut *inner.borrow_mut()).unwrap();
+ Ok(())
+ },
+ ).unwrap();
+ let inner = XWmInner {
+ connection,
+ atoms,
+ _source: source,
+ token: self.token,
+ window_map: self.window_map.clone(),
+ unpaired_surfaces: Default::default(),
+ paired_surfaces: Default::default(),
+ };
+ client.data_map().insert_if_missing(|| RefCell::new(inner));
+ let mutex = XWAYLAND_CLIENT.get_or_init(|| Mutex::new(None));
+ *mutex.lock().unwrap() = Some(client);
+
+ // The root window gets a ClientMessageEvent of type WL_SURFACE_ID with its first 32 bit
+ // member set to the wl surface id of the X11 window available in its window member. We
+ // have to use that to build a mapping between X11 windows and WL surfaces.
+ // However, there is a race: Either the ClientMessage or the surface creation arrives
+ // first, so we have to be prepared for both orders.
+ // At this point, we assign the surface some special role and make it visible.
+
+ // Everyone seems to keep a list of unpaired windows and use that for the above.
+ }
+
+ fn xwayland_exited(&mut self) {
+ println!("Hey Uli: XWayland exited");
+ }
+}
+
+pub(crate) fn commit_hook(surface: &WlSurface) {
+ if let Some(client) = surface.as_ref().client() {
+ if let Some(inner) = client.data_map().get::<RefCell<XWmInner>>() {
+ let mut inner = inner.borrow_mut();
+ if let Some(window) = inner.unpaired_surfaces.remove(&surface.as_ref().id()) {
+ inner.new_window(window, surface);
+ }
+ }
+ }
+}
+
+fn start_wm(connection: UnixStream) -> Result<(RustConnection, Atoms), Box<dyn std::error::Error>> {
+ // It's always screen 0 with Xwayland
+ let screen = 0;
+ let stream = DefaultStream::from_unix_stream(connection)?;
+ let connection = RustConnection::connect_to_stream(stream, screen)?;
+ let atoms = Atoms::new(&connection)?;
+
+ let screen = &connection.setup().roots[0];
+
+ // This is how we actually become the WM.
+ connection.change_window_attributes(
+ screen.root,
+ &ChangeWindowAttributesAux::default().event_mask(EventMask::SubstructureRedirect),
+ )?;
+
+ // Xwayland waits until the WM_S0 selection is acquired before it starts working
+ let win = connection.generate_id()?;
+ connection.create_window(
+ screen.root_depth,
+ win,
+ screen.root,
+ // x, y, width, height, border width
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ WindowClass::InputOutput,
+ x11rb::COPY_FROM_PARENT,
+ &Default::default(),
+ )?;
+ let wm_s0 = connection.intern_atom(false, b"WM_S0")?.reply()?;
+
+ // Screw ICCCM, we know that we are alone and do not have to do this properly
+ connection.set_selection_owner(win, wm_s0.atom, x11rb::CURRENT_TIME)?;
+
+ // No idea why I am doing this, but Xwayland seems to require it
+ connection.composite_redirect_subwindows(screen.root, Redirect::Manual)?;
+
+ let atoms = atoms.reply()?;
+
+ // Ensure all our requests are sent
+ connection.flush()?;
+
+ Ok((connection, atoms))
+}
+
+fn read_events(client: &Client, inner: &mut XWmInner) -> Result<(), Box<dyn std::error::Error>> {
+ use x11rb::protocol::Event;
+
+ while let Some(event) = inner.connection.poll_for_event()? {
+ eprintln!("X11: Got event {:?}", event);
+ match event {
+ Event::ConfigureRequest(r) => {
+ // Just grant the wish
+ let mut aux = ConfigureWindowAux::default();
+ if r.value_mask & u16::from(ConfigWindow::StackMode) != 0 {
+ aux = aux.stack_mode(r.stack_mode);
+ }
+ if r.value_mask & u16::from(ConfigWindow::Sibling) != 0 {
+ aux = aux.sibling(r.sibling);
+ }
+ if r.value_mask & u16::from(ConfigWindow::X) != 0 {
+ aux = aux.x(i32::try_from(r.x).unwrap());
+ }
+ if r.value_mask & u16::from(ConfigWindow::Y) != 0 {
+ aux = aux.y(i32::try_from(r.y).unwrap());
+ }
+ if r.value_mask & u16::from(ConfigWindow::Width) != 0 {
+ aux = aux.width(u32::try_from(r.width).unwrap());
+ }
+ if r.value_mask & u16::from(ConfigWindow::Height) != 0 {
+ aux = aux.height(u32::try_from(r.height).unwrap());
+ }
+ if r.value_mask & u16::from(ConfigWindow::BorderWidth) != 0 {
+ aux = aux.border_width(u32::try_from(r.border_width).unwrap());
+ }
+ inner.connection.configure_window(r.window, &aux)?;
+ }
+ Event::MapRequest(r) => {
+ // Just grant the wish
+ inner.connection.map_window(r.window)?;
+ }
+ Event::ClientMessage(msg) => {
+ if msg.type_ == inner.atoms.WL_SURFACE_ID {
+ let id = msg.data.as_data32()[0];
+ let surf = client.get_resource::<WlSurface>(id);
+ match surf {
+ None => {
+ inner.unpaired_surfaces.insert(id, msg.window);
+ }
+ Some(surf) => inner.new_window(msg.window, &surf),
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ inner.connection.flush()?;
+ Ok(())
+}
+
+pub struct X11SurfaceRole {
+ window: Window,
+}
+
+pub struct X11Surface<R> {
+ window: Window,
+ wl_surface: WlSurface,
+ token: CompositorToken<R>
+}
+
+impl<R> Clone for X11Surface<R> {
+ fn clone(&self) -> Self {
+ Self {
+ window: self.window,
+ wl_surface: self.wl_surface.clone(),
+ token: self.token,
+ }
+ }
+}
+
+impl<R> X11Surface<R> {
+ pub fn alive(&self) -> bool {
+ self.wl_surface.as_ref().is_alive()
+ }
+
+ pub fn equals(&self, other: &Self) -> bool {
+ self.alive() && other.alive() && self.wl_surface.as_ref().equals(&other.wl_surface.as_ref())
+ }
+
+ pub fn get_surface(&self) -> Option<&WlSurface> {
+ if self.alive() {
+ Some(&self.wl_surface)
+ } else {
+ None
+ }
+ }
+}
diff --git a/src/xwayland/xserver.rs b/src/xwayland/xserver.rs
index 42186965..dce9aedf 100644
--- a/src/xwayland/xserver.rs
+++ b/src/xwayland/xserver.rs
@@ -330,6 +330,7 @@ pub(crate) fn exec_xwayland(
}
// the WAYLAND_SOCKET var tells XWayland where to connect as a wayland client
env::set_var("WAYLAND_SOCKET", format!("{}", wayland_socket.as_raw_fd()));
+ env::set_var("WAYLAND_DEBUG", "client");
// ignore SIGUSR1, this will make the XWayland server send us this
// signal when it is ready apparently
from smithay.
We now have the core mechanism handling the XWayland server iteself. "Only" remains writing some WM utilities / integration.
from smithay.
Is this now fixed?
from smithay.
Mostly yes, I'm using smithay's xwayland utils and I haven't had any problems so far, I think that there are some helpers still planed, but all of the groundwork is already laid out.
from smithay.
Related Issues (20)
- `renderer_formats` argument of `DrmCompositor::new()` doesn't make much sense
- subcompositor: cached state isn't applied when a subsurface becomes effectively desynchronized
- No cursor in anvil HOT 16
- anvil: Newly placed windows are activated but don't receive keyboard focus HOT 1
- Write introduction on logging
- wayland-rs 0.30 TODO doc comment
- Monitors not lighting up DRM problems HOT 7
- MemoryRenderBuffer rounds size to scale HOT 1
- Fails to build on 32-bit architectures HOT 2
- Smithay doesn't reset the VRR CRTC property
- Apps run through nested wlroots compositors in "direct scan out" mode are not updated properly HOT 5
- `set_buffer_scale(0)` divides by zero instead of signaling a protocol error
- New release HOT 1
- With multiple subsurface, only last created (topmost) subsurface and parent get pointer input HOT 1
- Missing key release events on winit backend results in permanently pressed modifiers
- wvkbd crashes when touching a key HOT 9
- Possibly wrong window geometry size with wp-viewporter HOT 4
- aa8d87a214 broke pointer input in foot HOT 1
- Stuck in listener.accept
- Add blurred Element support to `OutputDamageTracker`
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from smithay.