Use xcb-wm
and bump xcb
to 1.1.1
This commit is contained in:
parent
0ae84e89bc
commit
e37563eb7a
7 changed files with 285 additions and 2104 deletions
1870
Cargo.lock
generated
1870
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
28
Cargo.toml
28
Cargo.toml
|
@ -3,23 +3,33 @@ name = "roftl"
|
|||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
default = ["x11"]
|
||||
# TODO ui module should be independent of windowing framework if possible, still xcb feature is apparently used in cairo
|
||||
# which is used in ui to paint to the window surface
|
||||
x11 = ["dep:xcb", "dep:xcb-wm", "cairo-rs/xcb", "cairo-sys-rs/xcb", "xcb-wm?/ewmh", "xcb-wm?/icccm"]
|
||||
|
||||
[dependencies]
|
||||
# globally useful dependencies
|
||||
log = "0.4"
|
||||
env_logger = "0.9"
|
||||
rayon = "1.5"
|
||||
anyhow = "1.0"
|
||||
config = "0.13"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
# ui
|
||||
winit = {git="https://github.com/rust-windowing/winit"}
|
||||
cairo-rs = {version = "0.15", features = ["xcb"]}
|
||||
cairo-sys-rs = {version = "0.15", features = ["xcb"]}
|
||||
# read and parse configuration
|
||||
config = "0.13"
|
||||
|
||||
# dependencies for building the `window` surface
|
||||
winit = { git = "https://github.com/rust-windowing/winit" }
|
||||
## `window` dependencies specific for x11. Only enabled if compiled with `x11` feature on [default]
|
||||
xcb = { version = "1.1.1", optional = true }
|
||||
xcb-wm = { git = "https://github.com/arminfriedl/xcb-wm", optional = true, features=["icccm", "ewmh"] }
|
||||
|
||||
# dependencies for drawing on the `window` surface (`ui` module)
|
||||
cairo-rs = "0.15.11"
|
||||
cairo-sys-rs = "0.15.1"
|
||||
pangocairo = "0.15"
|
||||
xcb = "0.10"
|
||||
xcb-util = {version = "0.4", features = ["ewmh", "icccm"]}
|
||||
|
||||
# matcher
|
||||
fuzzy-matcher = "0.3"
|
||||
|
|
63
src/main.rs
63
src/main.rs
|
@ -6,8 +6,7 @@ use winit::event::{
|
|||
WindowEvent::{CloseRequested, ReceivedCharacter},
|
||||
};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::platform::unix::x11::util::WindowType;
|
||||
use winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix, XWindowType};
|
||||
use winit::platform::unix::WindowExtUnix;
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
use self::core::roftl::Roftl;
|
||||
|
@ -16,6 +15,7 @@ mod core;
|
|||
mod matcher;
|
||||
mod sources;
|
||||
mod ui;
|
||||
mod window;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
|
@ -37,67 +37,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
debug! {"Source roftl sources"}
|
||||
roftl.source();
|
||||
|
||||
debug! {"Build window"}
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_decorations(false)
|
||||
.with_transparent(true)
|
||||
.with_title("roftl")
|
||||
.with_visible(true)
|
||||
.with_always_on_top(true)
|
||||
.build(&event_loop)
|
||||
.expect("Could not create window");
|
||||
|
||||
debug! {"Hide window from taskbar"}
|
||||
let screen = window
|
||||
.xlib_screen_id()
|
||||
.expect("Could not get screen id of window");
|
||||
|
||||
let xcb_conn = unsafe {
|
||||
xcb::Connection::from_raw_conn(
|
||||
window.xcb_connection().unwrap() as *mut xcb::ffi::xcb_connection_t
|
||||
)
|
||||
};
|
||||
|
||||
let ewmh_conn = xcb_util::ewmh::Connection::connect(xcb_conn)
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
|
||||
let xcb_window: xcb::xproto::Window =
|
||||
window.xlib_window().expect("Cannot get xlib window") as u32;
|
||||
|
||||
xcb_util::ewmh::request_change_wm_state(
|
||||
&ewmh_conn,
|
||||
screen,
|
||||
xcb_window,
|
||||
xcb_util::ewmh::STATE_ADD,
|
||||
ewmh_conn.WM_STATE(),
|
||||
ewmh_conn.WM_STATE_SKIP_TASKBAR(),
|
||||
xcb_util::ewmh::CLIENT_SOURCE_TYPE_NORMAL,
|
||||
);
|
||||
|
||||
debug! {"Grab keyboard, disable tabbing out"}
|
||||
let xcb_conn = unsafe {
|
||||
xcb::Connection::from_raw_conn(
|
||||
window.xcb_connection().unwrap() as *mut xcb::ffi::xcb_connection_t
|
||||
)
|
||||
};
|
||||
|
||||
xcb::xproto::grab_keyboard(
|
||||
&xcb_conn,
|
||||
true,
|
||||
xcb_window,
|
||||
xcb::ffi::base::XCB_CURRENT_TIME,
|
||||
xcb::xproto::GRAB_MODE_ASYNC as u8,
|
||||
xcb::xproto::GRAB_MODE_ASYNC as u8,
|
||||
);
|
||||
|
||||
debug! {"Window id: {:?}", window.id()}
|
||||
let (window, event_loop) = window::build_window()?;
|
||||
|
||||
debug! {"Draw primary state to window"}
|
||||
ui::draw(&window, "", roftl.narrow(""), 0);
|
||||
|
||||
debug! {"Start event loop"}
|
||||
trace! {"Start event loop"}
|
||||
let roftl_loop = RoftlLoop::new(roftl, window);
|
||||
roftl_loop.run(event_loop);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,12 @@ use std::collections::HashMap;
|
|||
|
||||
use super::core::shared::{Entry, Source};
|
||||
use log::debug;
|
||||
use xcb::ffi::XCB_CURRENT_TIME;
|
||||
use xcb_util::ewmh;
|
||||
use xcb_util::ffi::ewmh::XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER;
|
||||
use xcb_util::icccm;
|
||||
use winit::platform::unix::x11::ffi::Connection;
|
||||
use xcb::x::{InputFocus, CURRENT_TIME};
|
||||
use xcb::{Xid, XidNew};
|
||||
use xcb_wm::ewmh;
|
||||
use xcb_wm::ewmh::proto::{GetActiveWindow, GetWmDesktop};
|
||||
use xcb_wm::icccm;
|
||||
|
||||
pub struct Window {
|
||||
action_data: HashMap<u64, (u32, i32)>,
|
||||
|
@ -18,114 +20,99 @@ impl Window {
|
|||
})
|
||||
}
|
||||
|
||||
fn is_normal_window(
|
||||
&self,
|
||||
con: &ewmh::Connection,
|
||||
window: u32,
|
||||
) -> Result<bool, xcb::ReplyError> {
|
||||
let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?;
|
||||
fn is_normal_window(&self, con: &ewmh::Connection, window: xcb::x::Window) -> bool {
|
||||
let wm_type_request = ewmh::proto::GetWmWindowType(window);
|
||||
let wm_types = con
|
||||
.wait_for_reply(con.send_request(&wm_type_request))
|
||||
.window_types;
|
||||
|
||||
for atom in wm_type_reply.atoms() {
|
||||
if *atom == con.WM_WINDOW_TYPE_NORMAL() {
|
||||
return Ok(true);
|
||||
for atom in wm_types {
|
||||
if atom == con.atoms._NET_WM_WINDOW_TYPE_NORMAL {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
false
|
||||
}
|
||||
|
||||
fn window_category(
|
||||
&self,
|
||||
con: &ewmh::Connection,
|
||||
window: u32,
|
||||
) -> Result<String, xcb::ReplyError> {
|
||||
Ok(icccm::get_wm_class(&con, window)
|
||||
.get_reply()?
|
||||
.class()
|
||||
.into())
|
||||
// fn window_category(
|
||||
// &self,
|
||||
// con: &ewmh::Connection,
|
||||
// window: u32,
|
||||
// ) -> Result<String, xcb::ReplyError> {
|
||||
// Ok(icccm::get_wm_class(&con, window)
|
||||
// .get_reply()?
|
||||
// .class()
|
||||
// .into())
|
||||
// }
|
||||
|
||||
fn window_title(&self, con: &ewmh::Connection, window: xcb::x::Window) -> String {
|
||||
let wm_name_request = ewmh::proto::GetWmName(window);
|
||||
let wm_name = con.wait_for_reply(con.send_request(&wm_name_request));
|
||||
wm_name.name
|
||||
}
|
||||
|
||||
fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::ReplyError> {
|
||||
Ok(ewmh::get_wm_name(con, window).get_reply()?.string().into())
|
||||
}
|
||||
|
||||
fn switch_window(
|
||||
&self,
|
||||
con: &ewmh::Connection,
|
||||
window: u32,
|
||||
screen: i32,
|
||||
) -> Result<(), xcb::ReplyError> {
|
||||
let window_desktop = ewmh::get_wm_desktop(con, window).get_reply()?;
|
||||
let active_window = ewmh::get_active_window(con, screen).get_reply()?;
|
||||
ewmh::set_current_desktop(con, screen, window_desktop);
|
||||
ewmh::request_change_active_window(
|
||||
fn switch_window(&self, con: &ewmh::Connection, window: u32) {
|
||||
let change_active_window = ewmh::proto::SendActiveWindow::new(
|
||||
con,
|
||||
screen,
|
||||
window,
|
||||
XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER,
|
||||
XCB_CURRENT_TIME,
|
||||
active_window,
|
||||
)
|
||||
.request_check()
|
||||
.unwrap();
|
||||
ewmh::set_active_window(con, screen, window)
|
||||
.request_check()
|
||||
.unwrap();
|
||||
xcb::set_input_focus(&con, 0, window, XCB_CURRENT_TIME);
|
||||
|
||||
Ok(())
|
||||
unsafe { xcb::x::Window::new(window.into()) },
|
||||
1,
|
||||
xcb::x::CURRENT_TIME,
|
||||
Option::None,
|
||||
);
|
||||
con.send_request(&change_active_window);
|
||||
}
|
||||
|
||||
fn toggle_maximize_window(
|
||||
&self,
|
||||
con: &ewmh::Connection,
|
||||
window: u32,
|
||||
screen: i32,
|
||||
) -> Result<(), xcb::ReplyError> {
|
||||
let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
||||
let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
||||
let action_atom = ewmh::STATE_TOGGLE;
|
||||
|
||||
debug! {"Toggle maximize for {}", window}
|
||||
ewmh::request_change_wm_state(
|
||||
con,
|
||||
screen,
|
||||
window,
|
||||
action_atom,
|
||||
max_horz_atom,
|
||||
max_vert_atom,
|
||||
0,
|
||||
)
|
||||
.request_check()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn close_window(
|
||||
&self,
|
||||
con: &ewmh::Connection,
|
||||
window: u32,
|
||||
screen: i32,
|
||||
) -> Result<(), xcb::ReplyError> {
|
||||
debug! {"Toggle maximize for {}", window}
|
||||
ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
||||
}
|
||||
|
||||
fn toggle_hide_window(
|
||||
&self,
|
||||
con: &ewmh::Connection,
|
||||
window: u32,
|
||||
screen: i32,
|
||||
) -> Result<(), xcb::ReplyError> {
|
||||
let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
||||
let action_atom = ewmh::STATE_TOGGLE;
|
||||
|
||||
debug! {"Toggle hidden for {}", window}
|
||||
ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0)
|
||||
.request_check()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// fn toggle_maximize_window(
|
||||
// &self,
|
||||
// con: &ewmh::Connection,
|
||||
// window: u32,
|
||||
// screen: i32,
|
||||
// ) -> Result<(), xcb::ReplyError> {
|
||||
// let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
||||
// let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
||||
// let action_atom = ewmh::STATE_TOGGLE;
|
||||
//
|
||||
// debug! {"Toggle maximize for {}", window}
|
||||
// ewmh::request_change_wm_state(
|
||||
// con,
|
||||
// screen,
|
||||
// window,
|
||||
// action_atom,
|
||||
// max_horz_atom,
|
||||
// max_vert_atom,
|
||||
// 0,
|
||||
// )
|
||||
// .request_check()?;
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// fn close_window(
|
||||
// &self,
|
||||
// con: &ewmh::Connection,
|
||||
// window: u32,
|
||||
// screen: i32,
|
||||
// ) -> Result<(), xcb::ReplyError> {
|
||||
// debug! {"Toggle maximize for {}", window}
|
||||
// ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
||||
// }
|
||||
//
|
||||
// fn toggle_hide_window(
|
||||
// &self,
|
||||
// con: &ewmh::Connection,
|
||||
// window: u32,
|
||||
// screen: i32,
|
||||
// ) -> Result<(), xcb::ReplyError> {
|
||||
// let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
||||
// let action_atom = ewmh::STATE_TOGGLE;
|
||||
//
|
||||
// debug! {"Toggle hidden for {}", window}
|
||||
// ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0)
|
||||
// .request_check()?;
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
impl Source for Window {
|
||||
|
@ -135,38 +122,34 @@ impl Source for Window {
|
|||
|
||||
fn entries(&mut self) -> Vec<Entry> {
|
||||
let (xcb_conn, screen_num) =
|
||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
xcb::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(&xcb_conn);
|
||||
|
||||
let client_list_reply = xcb_util::ewmh::get_client_list(&ewmh_conn, screen_num)
|
||||
.get_reply()
|
||||
.unwrap();
|
||||
let windows = client_list_reply.windows();
|
||||
let client_list_reply = ewmh_conn.send_request(&xcb_wm::ewmh::proto::GetClientList);
|
||||
let windows = ewmh_conn.wait_for_reply(client_list_reply).clients;
|
||||
|
||||
let mut entries: Vec<Entry> = vec![];
|
||||
|
||||
let mut count: u64 = 0;
|
||||
for w in windows {
|
||||
match self.is_normal_window(&ewmh_conn, *w) {
|
||||
Ok(true) => {}
|
||||
Ok(false) => continue,
|
||||
Err(_err) => {}
|
||||
match self.is_normal_window(&ewmh_conn, w) {
|
||||
true => {}
|
||||
false => continue,
|
||||
}
|
||||
|
||||
let title = self.window_title(&ewmh_conn, *w).unwrap();
|
||||
let category = self.window_category(&ewmh_conn, *w).unwrap();
|
||||
let title = self.window_title(&ewmh_conn, w);
|
||||
let category: String = "Test".into(); //self.window_category(&ewmh_conn, *w).unwrap();
|
||||
|
||||
debug!("Found window {} - {}", title, category);
|
||||
|
||||
entries.push(Entry {
|
||||
source: self.name(),
|
||||
name: category,
|
||||
description: title,
|
||||
name: title.clone(),
|
||||
description: title.clone(),
|
||||
identifier: count,
|
||||
});
|
||||
self.action_data.insert(count, (*w, screen_num));
|
||||
self.action_data
|
||||
.insert(count, (w.resource_id(), screen_num));
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
@ -181,43 +164,42 @@ impl Source for Window {
|
|||
0 => {
|
||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
let (xcb_conn, _screen_num) =
|
||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
||||
}
|
||||
1 => {
|
||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
let (xcb_conn, _screen_num) =
|
||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
self.toggle_maximize_window(&ewmh_conn, window, screen)
|
||||
.unwrap();
|
||||
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
||||
}
|
||||
2 => {
|
||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
let (xcb_conn, _screen_num) =
|
||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
|
||||
self.toggle_hide_window(&ewmh_conn, window, screen).unwrap();
|
||||
}
|
||||
3 => {
|
||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
let (xcb_conn, _screen_num) =
|
||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
.map_err(|(e, _)| e)
|
||||
.unwrap();
|
||||
|
||||
self.close_window(&ewmh_conn, window, screen).unwrap();
|
||||
xcb::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(&xcb_conn);
|
||||
self.switch_window(&ewmh_conn, window);
|
||||
xcb_conn.flush().unwrap();
|
||||
}
|
||||
// 1 => {
|
||||
// let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
// let (xcb_conn, _screen_num) =
|
||||
// xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
// let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
// .map_err(|(e, _)| e)
|
||||
// .unwrap();
|
||||
// self.toggle_maximize_window(&ewmh_conn, window, screen)
|
||||
// .unwrap();
|
||||
// self.switch_window(&ewmh_conn, window, screen).unwrap()
|
||||
// }
|
||||
// 2 => {
|
||||
// let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
// let (xcb_conn, _screen_num) =
|
||||
// xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
// let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
// .map_err(|(e, _)| e)
|
||||
// .unwrap();
|
||||
//
|
||||
// self.toggle_hide_window(&ewmh_conn, window, screen).unwrap();
|
||||
// }
|
||||
// 3 => {
|
||||
// let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
// let (xcb_conn, _screen_num) =
|
||||
// xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
// let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
||||
// .map_err(|(e, _)| e)
|
||||
// .unwrap();
|
||||
//
|
||||
// self.close_window(&ewmh_conn, window, screen).unwrap();
|
||||
// }
|
||||
_ => panic! {"Unknown action {:?}", action},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use super::core;
|
||||
|
||||
mod ui;
|
||||
pub use ui::draw;
|
||||
pub use ui::draw_actions;
|
||||
|
|
26
src/ui/ui.rs
26
src/ui/ui.rs
|
@ -1,11 +1,13 @@
|
|||
use std::borrow::BorrowMut;
|
||||
use std::ffi::c_void;
|
||||
use std::mem;
|
||||
|
||||
use log::debug;
|
||||
use winit::{platform::unix::WindowExtUnix, window::Window};
|
||||
use xcb::XidNew;
|
||||
|
||||
use super::core::shared::Entry;
|
||||
use super::painter;
|
||||
use crate::core::shared::Entry;
|
||||
|
||||
pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, selection: usize) {
|
||||
let context = make_context(&window);
|
||||
|
@ -80,7 +82,7 @@ pub fn draw_actions(window: &Window, actions: Vec<String>, input: &str) {
|
|||
});
|
||||
}
|
||||
|
||||
fn get_visual_type<T>(mut connection: T, window_id: u32) -> xcb::Visualtype
|
||||
fn get_visual_type<T>(mut connection: T, window_id: u32) -> xcb::x::Visualtype
|
||||
where
|
||||
T: BorrowMut<*mut c_void>,
|
||||
{
|
||||
|
@ -88,15 +90,22 @@ where
|
|||
xcb::Connection::from_raw_conn(*connection.borrow_mut() as *mut xcb::ffi::xcb_connection_t)
|
||||
};
|
||||
|
||||
let window_visualid = xcb::get_window_attributes(&xcb_conn, window_id)
|
||||
.get_reply()
|
||||
let window_attribute_request = unsafe {
|
||||
xcb::x::GetWindowAttributes {
|
||||
window: xcb::x::Window::new(window_id),
|
||||
}
|
||||
};
|
||||
|
||||
let window_attribute_cookie = xcb_conn.send_request(&window_attribute_request);
|
||||
let window_visualid = xcb_conn
|
||||
.wait_for_reply(window_attribute_cookie)
|
||||
.expect("Could not fetch attributes for window")
|
||||
.visual();
|
||||
|
||||
debug! {"Found visualid {} for window", window_visualid}
|
||||
|
||||
debug! {"Trying to map visualid to visualtype {}", window_visualid}
|
||||
let visualtype = xcb_conn
|
||||
let visualtype = *xcb_conn
|
||||
.get_setup()
|
||||
.roots()
|
||||
.flat_map(|screen| screen.allowed_depths())
|
||||
|
@ -122,11 +131,8 @@ fn make_context(window: &Window) -> cairo::Context {
|
|||
let xlib_window_id = window.xlib_window().expect("Could not get xlib window");
|
||||
|
||||
let visual_type = unsafe {
|
||||
let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32).base;
|
||||
cairo::XCBVisualType::from_raw_none(
|
||||
&mut visual_type as *mut xcb::ffi::xcb_visualtype_t
|
||||
as *mut cairo::ffi::xcb_visualtype_t,
|
||||
)
|
||||
let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32);
|
||||
cairo::XCBVisualType::from_raw_none(mem::transmute(&mut visual_type))
|
||||
};
|
||||
|
||||
let connection = unsafe {
|
||||
|
|
110
src/window.rs
Normal file
110
src/window.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
//! # Window
|
||||
//!
|
||||
//! Build a surface for `roftl`. The surface in turn provides an event loop as
|
||||
//! well as a canvas for the `ui` module to paint on. Adhering to common
|
||||
//! terminology we refer to this surface as window.
|
||||
//!
|
||||
//! Currently only x11 is implemented. The separation into a separate module is
|
||||
//! there to make conditional compilation for other frameworks easier to handle.
|
||||
//!
|
||||
//! Some of the heavy lifting is done by `winit`. However, `winit` has some
|
||||
//! assumptions built-in that are not changeable via the `winit` API. Since a
|
||||
//! `roftl` does not behave like a regular application window we have to
|
||||
//! directly interact with the underlying windowing framework for fine-tuning.
|
||||
|
||||
use std::mem;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use log::{debug, trace};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix};
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
use xcb::ffi::xcb_connection_t;
|
||||
use xcb::x::{GrabMode, PropMode};
|
||||
use xcb::XidNew;
|
||||
|
||||
pub fn build_window() -> Result<(Window, EventLoop<()>)> {
|
||||
trace! {"Initialize `winit` event loop"}
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
trace! {"Build `winit` base window"}
|
||||
let window = WindowBuilder::new()
|
||||
.with_decorations(false)
|
||||
.with_transparent(true)
|
||||
.with_title("roftl")
|
||||
.with_visible(true)
|
||||
.with_always_on_top(true)
|
||||
.with_override_redirect(true)
|
||||
.build(&event_loop)?;
|
||||
trace! {"Built base window {:?}", window.id()}
|
||||
|
||||
trace! {"Post-process window"}
|
||||
postprocess_window(&window)?;
|
||||
|
||||
Ok((window, event_loop))
|
||||
}
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
fn postprocess_window(window: &Window) -> Result<()> {
|
||||
trace! {"Post-process window for x11"}
|
||||
|
||||
trace! {"Get low level connection and data"}
|
||||
let screen_id = window
|
||||
.xlib_screen_id()
|
||||
.ok_or(anyhow! {"Could not get X11 screen id for the window"})?;
|
||||
|
||||
let xcb_window: xcb::x::Window = unsafe {
|
||||
xcb::x::Window::new(
|
||||
window
|
||||
.xlib_window()
|
||||
.ok_or(anyhow! {"Cannot get xlib window"})? as u32,
|
||||
)
|
||||
};
|
||||
|
||||
let xcb_conn_raw = window
|
||||
.xcb_connection()
|
||||
.ok_or(anyhow! {"Cannot get raw xcb connection"})?;
|
||||
|
||||
let xcb_conn = unsafe {
|
||||
mem::ManuallyDrop::new(xcb::Connection::from_raw_conn(mem::transmute(xcb_conn_raw)))
|
||||
};
|
||||
|
||||
let req = xcb::x::UnmapWindow { window: xcb_window };
|
||||
let cookie = xcb_conn.send_request_checked(&req);
|
||||
xcb_conn.check_request(cookie)?;
|
||||
|
||||
trace! {"Hide window from taskbar"}
|
||||
let xcb_conn = unsafe { xcb::Connection::from_raw_conn(mem::transmute(xcb_conn_raw)) };
|
||||
let ewmh_conn = xcb_wm::ewmh::Connection::connect(&xcb_conn);
|
||||
|
||||
let window_state = xcb_wm::ewmh::proto::SendWmState::new(
|
||||
&ewmh_conn,
|
||||
xcb_window,
|
||||
PropMode::Append,
|
||||
[
|
||||
ewmh_conn.atoms._NET_WM_STATE,
|
||||
ewmh_conn.atoms._NET_WM_STATE_SKIP_TASKBAR,
|
||||
],
|
||||
1,
|
||||
);
|
||||
ewmh_conn.send_request(&window_state);
|
||||
|
||||
let req = xcb::x::MapWindow { window: xcb_window };
|
||||
xcb_conn.send_request(&req);
|
||||
|
||||
trace! {"Grab keyboard"}
|
||||
let req = xcb::x::GrabKeyboard {
|
||||
owner_events: true,
|
||||
grab_window: xcb_window,
|
||||
time: xcb::x::CURRENT_TIME,
|
||||
pointer_mode: GrabMode::Async,
|
||||
keyboard_mode: GrabMode::Async,
|
||||
};
|
||||
xcb_conn.send_request(&req);
|
||||
|
||||
mem::forget(xcb_conn);
|
||||
|
||||
// trace! {"Center window on screen"}
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue