1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-10 15:04:32 +03:00

basic xcb windows too

This commit is contained in:
Wez Furlong 2019-08-08 17:44:57 -07:00
parent ddd323464b
commit dc6ef6c55d
10 changed files with 540 additions and 29 deletions

View File

@ -26,3 +26,12 @@ winapi = { version = "0.3", features = [
"winuser",
]}
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
x11 = {version ="2.18", features = ["xlib_xcb"]}
xcb = "0.8"
xcb-util = { features = [ "icccm", "ewmh", "keysyms", ], version = "0.2" }
xkbcommon = { version = "0.4", features = ["x11"] }
[target.'cfg(unix)'.dependencies]
mio = "0.6"
mio-extras = "2.0"

View File

@ -1,5 +1,5 @@
use ::window::*;
use failure::Fallible;
use window::*;
struct MyWindow {
allow_close: bool,
@ -9,7 +9,7 @@ impl WindowCallbacks for MyWindow {
fn can_close(&mut self) -> bool {
eprintln!("can I close?");
if self.allow_close {
terminate_message_loop();
Connection::get().unwrap().terminate_message_loop();
true
} else {
self.allow_close = true;
@ -19,6 +19,8 @@ impl WindowCallbacks for MyWindow {
}
fn main() -> Fallible<()> {
let conn = Connection::init()?;
let win = Window::new_window(
"myclass",
"the title",
@ -29,5 +31,5 @@ fn main() -> Fallible<()> {
win.show();
run_message_loop()
conn.run_message_loop()
}

View File

@ -5,8 +5,7 @@ pub mod os;
pub use bitmaps::BitmapImage;
pub use color::Color;
#[cfg(windows)]
pub use os::windows::window::*;
pub use os::*;
/// Compositing operator.
/// We implement a small subset of possible compositing operators.

View File

@ -1,2 +1,9 @@
#[cfg(windows)]
pub mod windows;
#[cfg(windows)]
pub use windows::*;
#[cfg(all(unix, not(target_os = "macos")))]
pub mod x11;
#[cfg(all(unix, not(target_os = "macos")))]
pub use self::x11::*;

View File

@ -0,0 +1,55 @@
//! The connection to the GUI subsystem
use failure::Fallible;
use std::cell::RefCell;
use std::io::Error as IoError;
use std::ptr::null_mut;
use std::sync::Arc;
use winapi::um::winuser::*;
pub struct Connection {}
thread_local! {
static CONN: RefCell<Option<Arc<Connection>>> = RefCell::new(None);
}
impl Connection {
pub fn get() -> Option<Arc<Self>> {
let mut res = None;
CONN.with(|m| {
if let Some(mux) = &*m.borrow() {
res = Some(Arc::clone(mux));
}
});
res
}
pub fn init() -> Fallible<Arc<Self>> {
let conn = Arc::new(Self {});
CONN.with(|m| *m.borrow_mut() = Some(Arc::clone(&conn)));
Ok(conn)
}
pub fn terminate_message_loop(&self) {
unsafe {
PostQuitMessage(0);
}
}
pub fn run_message_loop(&self) -> Fallible<()> {
let mut msg: MSG = unsafe { std::mem::zeroed() };
loop {
let res = unsafe { GetMessageW(&mut msg, null_mut(), 0, 0) };
if res == -1 {
return Err(IoError::last_os_error().into());
}
if res == 0 {
return Ok(());
}
unsafe {
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
}
}
}
}

View File

@ -1,6 +1,11 @@
pub mod connection;
pub mod gdi;
pub mod window;
pub use connection::*;
pub use gdi::*;
pub use window::*;
/// Convert a rust string to a windows wide string
fn wide_string(s: &str) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;

View File

@ -369,27 +369,3 @@ unsafe extern "system" fn wnd_proc(
Err(_) => std::process::exit(1),
}
}
pub fn run_message_loop() -> Fallible<()> {
let mut msg: MSG = unsafe { std::mem::zeroed() };
loop {
let res = unsafe { GetMessageW(&mut msg, null_mut(), 0, 0) };
if res == -1 {
return Err(IoError::last_os_error().into());
}
if res == 0 {
return Ok(());
}
unsafe {
TranslateMessage(&mut msg);
DispatchMessageW(&mut msg);
}
}
}
pub fn terminate_message_loop() {
unsafe {
PostQuitMessage(0);
}
}

View File

@ -0,0 +1,229 @@
use crate::Window;
use failure::Fallible;
use mio::unix::EventedFd;
use mio::{Evented, Poll, PollOpt, Ready, Token};
use std::cell::RefCell;
use std::collections::HashMap;
use std::os::unix::io::AsRawFd;
use std::sync::Arc;
use xcb_util::ffi::keysyms::{xcb_key_symbols_alloc, xcb_key_symbols_free, xcb_key_symbols_t};
pub struct Connection {
pub display: *mut x11::xlib::Display,
conn: xcb::Connection,
screen_num: i32,
pub atom_protocols: xcb::Atom,
pub atom_delete: xcb::Atom,
pub atom_utf8_string: xcb::Atom,
pub atom_xsel_data: xcb::Atom,
pub atom_targets: xcb::Atom,
pub atom_clipboard: xcb::Atom,
keysyms: *mut xcb_key_symbols_t,
pub(crate) windows: RefCell<HashMap<xcb::xproto::Window, Window>>,
should_terminate: RefCell<bool>,
}
impl std::ops::Deref for Connection {
type Target = xcb::Connection;
fn deref(&self) -> &xcb::Connection {
&self.conn
}
}
impl Evented for Connection {
fn register(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> std::io::Result<()> {
EventedFd(&self.conn.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(
&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt,
) -> std::io::Result<()> {
EventedFd(&self.conn.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> std::io::Result<()> {
EventedFd(&self.conn.as_raw_fd()).deregister(poll)
}
}
#[link(name = "X11-xcb")]
extern "C" {
fn XGetXCBConnection(display: *mut x11::xlib::Display) -> *mut xcb::ffi::xcb_connection_t;
fn XSetEventQueueOwner(display: *mut x11::xlib::Display, owner: i32);
}
thread_local! {
static CONN: RefCell<Option<Arc<Connection>>> = RefCell::new(None);
}
fn window_id_from_event(event: &xcb::GenericEvent) -> Option<xcb::xproto::Window> {
match event.response_type() & 0x7f {
xcb::EXPOSE => {
let expose: &xcb::ExposeEvent = unsafe { xcb::cast_event(event) };
Some(expose.window())
}
xcb::CONFIGURE_NOTIFY => {
let cfg: &xcb::ConfigureNotifyEvent = unsafe { xcb::cast_event(event) };
Some(cfg.window())
}
xcb::KEY_PRESS | xcb::KEY_RELEASE => {
let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(event) };
Some(key_press.event())
}
xcb::MOTION_NOTIFY => {
let motion: &xcb::MotionNotifyEvent = unsafe { xcb::cast_event(event) };
Some(motion.event())
}
xcb::BUTTON_PRESS | xcb::BUTTON_RELEASE => {
let button_press: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(event) };
Some(button_press.event())
}
xcb::CLIENT_MESSAGE => {
let msg: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(event) };
Some(msg.window())
}
_ => None,
}
}
impl Connection {
pub fn get() -> Option<Arc<Self>> {
let mut res = None;
CONN.with(|m| {
if let Some(mux) = &*m.borrow() {
res = Some(Arc::clone(mux));
}
});
res
}
pub fn terminate_message_loop(&self) {
*self.should_terminate.borrow_mut() = true;
}
pub fn run_message_loop(&self) -> Fallible<()> {
self.conn.flush();
while let Some(event) = self.conn.wait_for_event() {
self.process_xcb_event(&event)?;
self.conn.flush();
if *self.should_terminate.borrow() {
break;
}
}
Ok(())
}
fn process_xcb_event(&self, event: &xcb::GenericEvent) -> Fallible<()> {
if let Some(window_id) = window_id_from_event(event) {
self.process_window_event(window_id, event)?;
} else {
/*
let r = event.response_type() & 0x7f;
if r == self.conn.kbd_ev {
// key press/release are not processed here.
// xkbcommon depends on those events in order to:
// - update modifiers state
// - update keymap/state on keyboard changes
self.conn.keyboard.process_xkb_event(&self.conn, event)?;
}
*/
}
Ok(())
}
fn window_by_id(&self, window_id: xcb::xproto::Window) -> Option<Window> {
self.windows.borrow().get(&window_id).cloned()
}
fn process_window_event(
&self,
window_id: xcb::xproto::Window,
event: &xcb::GenericEvent,
) -> Fallible<()> {
if let Some(window) = self.window_by_id(window_id) {
window.dispatch_event(event)?;
}
Ok(())
}
pub fn init() -> Fallible<Arc<Connection>> {
let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
if display.is_null() {
failure::bail!("failed to open display");
}
let screen_num = unsafe { x11::xlib::XDefaultScreen(display) };
let conn = unsafe { xcb::Connection::from_raw_conn(XGetXCBConnection(display)) };
unsafe { XSetEventQueueOwner(display, 1) };
let atom_protocols = xcb::intern_atom(&conn, false, "WM_PROTOCOLS")
.get_reply()?
.atom();
let atom_delete = xcb::intern_atom(&conn, false, "WM_DELETE_WINDOW")
.get_reply()?
.atom();
let atom_utf8_string = xcb::intern_atom(&conn, false, "UTF8_STRING")
.get_reply()?
.atom();
let atom_xsel_data = xcb::intern_atom(&conn, false, "XSEL_DATA")
.get_reply()?
.atom();
let atom_targets = xcb::intern_atom(&conn, false, "TARGETS")
.get_reply()?
.atom();
let atom_clipboard = xcb::intern_atom(&conn, false, "CLIPBOARD")
.get_reply()?
.atom();
let keysyms = unsafe { xcb_key_symbols_alloc(conn.get_raw_conn()) };
let conn = Arc::new(Connection {
display,
conn,
screen_num,
atom_protocols,
atom_clipboard,
atom_delete,
keysyms,
atom_utf8_string,
atom_xsel_data,
atom_targets,
windows: RefCell::new(HashMap::new()),
should_terminate: RefCell::new(false),
});
CONN.with(|m| *m.borrow_mut() = Some(Arc::clone(&conn)));
Ok(conn)
}
pub fn conn(&self) -> &xcb::Connection {
&self.conn
}
pub fn screen_num(&self) -> i32 {
self.screen_num
}
pub fn atom_delete(&self) -> xcb::Atom {
self.atom_delete
}
}
impl Drop for Connection {
fn drop(&mut self) {
unsafe {
xcb_key_symbols_free(self.keysyms);
}
}
}

4
window/src/os/x11/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod connection;
pub mod window;
pub use connection::*;
pub use window::*;

225
window/src/os/x11/window.rs Normal file
View File

@ -0,0 +1,225 @@
use super::connection::*;
use crate::WindowCallbacks;
use failure::Fallible;
use std::convert::TryInto;
use std::sync::{Arc, Mutex};
struct WindowHolder {
window_id: xcb::xproto::Window,
conn: Arc<Connection>,
callbacks: Mutex<Box<WindowCallbacks>>,
}
impl Drop for WindowHolder {
fn drop(&mut self) {
xcb::destroy_window(self.conn.conn(), self.window_id);
}
}
/// A Window!
#[derive(Clone)]
pub struct Window {
window: Arc<WindowHolder>,
}
impl Window {
/// Create a new window on the specified screen with the specified
/// dimensions
pub fn new_window(
_class_name: &str,
name: &str,
width: usize,
height: usize,
callbacks: Box<WindowCallbacks>,
) -> Fallible<Window> {
let conn = Connection::get().ok_or_else(|| {
failure::err_msg(
"new_window must be called on the gui thread after Connection::init has succeeded",
)
})?;
let window = {
let setup = conn.conn().get_setup();
let screen = setup
.roots()
.nth(conn.screen_num() as usize)
.ok_or_else(|| failure::err_msg("no screen?"))?;
let window_id = conn.conn().generate_id();
xcb::create_window_checked(
conn.conn(),
xcb::COPY_FROM_PARENT as u8,
window_id,
screen.root(),
// x, y
0,
0,
// width, height
width.try_into()?,
height.try_into()?,
// border width
0,
xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
screen.root_visual(),
&[(
xcb::CW_EVENT_MASK,
xcb::EVENT_MASK_EXPOSURE
| xcb::EVENT_MASK_KEY_PRESS
| xcb::EVENT_MASK_BUTTON_PRESS
| xcb::EVENT_MASK_BUTTON_RELEASE
| xcb::EVENT_MASK_POINTER_MOTION
| xcb::EVENT_MASK_BUTTON_MOTION
| xcb::EVENT_MASK_KEY_RELEASE
| xcb::EVENT_MASK_STRUCTURE_NOTIFY,
)],
)
.request_check()?;
Arc::new(WindowHolder {
window_id,
conn: Arc::clone(&conn),
callbacks: Mutex::new(callbacks),
})
};
xcb::change_property(
&*conn,
xcb::PROP_MODE_REPLACE as u8,
window.window_id,
conn.atom_protocols,
4,
32,
&[conn.atom_delete],
);
let window = Window { window };
conn.windows
.borrow_mut()
.insert(window.window.window_id, window.clone());
window.set_title(name);
window.show();
Ok(window)
}
/// Change the title for the window manager
pub fn set_title(&self, title: &str) {
xcb_util::icccm::set_wm_name(self.window.conn.conn(), self.window.window_id, title);
}
/// Display the window
pub fn show(&self) {
xcb::map_window(self.window.conn.conn(), self.window.window_id);
}
pub fn dispatch_event(&self, event: &xcb::GenericEvent) -> Fallible<()> {
let r = event.response_type() & 0x7f;
match r {
xcb::EXPOSE => {
let expose: &xcb::ExposeEvent = unsafe { xcb::cast_event(event) };
eprintln!("EXPOSE");
//self.expose(expose.x(), expose.y(), expose.width(), expose.height())?;
}
xcb::CONFIGURE_NOTIFY => {
let cfg: &xcb::ConfigureNotifyEvent = unsafe { xcb::cast_event(event) };
eprintln!("CONFIGURE_NOTIFY");
/*
let schedule = self.have_pending_resize.is_none();
self.have_pending_resize = Some((cfg.width(), cfg.height()));
if schedule {
self.host.with_window(|win| win.check_for_resize());
}
*/
}
xcb::KEY_PRESS => {
let key_press: &xcb::KeyPressEvent = unsafe { xcb::cast_event(event) };
eprintln!("KEY_PRESS");
/*
let mux = Mux::get().unwrap();
let tab = match mux.get_active_tab_for_window(self.get_mux_window_id()) {
Some(tab) => tab,
None => return Ok(()),
};
if let Some((code, mods)) = self.decode_key(key_press) {
if self.host.process_gui_shortcuts(&*tab, mods, code)? {
return Ok(());
}
tab.key_down(code, mods)?;
}
*/
}
xcb::MOTION_NOTIFY => {
let motion: &xcb::MotionNotifyEvent = unsafe { xcb::cast_event(event) };
eprintln!("MOTION_NOTIFY");
/*
let event = MouseEvent {
kind: MouseEventKind::Move,
button: MouseButton::None,
x: (motion.event_x() as usize / self.cell_width) as usize,
y: (motion.event_y() as usize / self.cell_height) as i64,
modifiers: xkeysyms::modifiers_from_state(motion.state()),
};
self.mouse_event(event)?;
*/
}
xcb::BUTTON_PRESS | xcb::BUTTON_RELEASE => {
let button_press: &xcb::ButtonPressEvent = unsafe { xcb::cast_event(event) };
eprintln!("BUTTON_PRESS");
/*
let event = MouseEvent {
kind: match r {
xcb::BUTTON_PRESS => MouseEventKind::Press,
xcb::BUTTON_RELEASE => MouseEventKind::Release,
_ => unreachable!("button event mismatch"),
},
x: (button_press.event_x() as usize / self.cell_width) as usize,
y: (button_press.event_y() as usize / self.cell_height) as i64,
button: match button_press.detail() {
1 => MouseButton::Left,
2 => MouseButton::Middle,
3 => MouseButton::Right,
4 => MouseButton::WheelUp(1),
5 => MouseButton::WheelDown(1),
_ => {
error!("button {} is not implemented", button_press.detail());
return Ok(());
}
},
modifiers: xkeysyms::modifiers_from_state(button_press.state()),
};
self.mouse_event(event)?;
*/
}
xcb::CLIENT_MESSAGE => {
let msg: &xcb::ClientMessageEvent = unsafe { xcb::cast_event(event) };
eprintln!("CLIENT_MESSAGE {:?}", msg.data().data32());
if msg.data().data32()[0] == self.window.conn.atom_delete() {
eprintln!("close requested");
if self.window.callbacks.lock().unwrap().can_close() {
self.close_window();
}
}
}
_ => {
eprintln!("unhandled: {:x}", r);
}
}
Ok(())
}
pub fn close_window(&self) {
self.window
.conn
.windows
.borrow_mut()
.remove(&self.window.window_id);
xcb::destroy_window(self.window.conn.conn(), self.window.window_id);
}
}