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
24
Cargo.toml
24
Cargo.toml
|
@ -3,23 +3,33 @@ name = "roftl"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
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]
|
[dependencies]
|
||||||
|
# globally useful dependencies
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
config = "0.13"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
# ui
|
# read and parse configuration
|
||||||
|
config = "0.13"
|
||||||
|
|
||||||
|
# dependencies for building the `window` surface
|
||||||
winit = { git = "https://github.com/rust-windowing/winit" }
|
winit = { git = "https://github.com/rust-windowing/winit" }
|
||||||
cairo-rs = {version = "0.15", features = ["xcb"]}
|
## `window` dependencies specific for x11. Only enabled if compiled with `x11` feature on [default]
|
||||||
cairo-sys-rs = {version = "0.15", features = ["xcb"]}
|
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"
|
pangocairo = "0.15"
|
||||||
xcb = "0.10"
|
|
||||||
xcb-util = {version = "0.4", features = ["ewmh", "icccm"]}
|
|
||||||
|
|
||||||
# matcher
|
# matcher
|
||||||
fuzzy-matcher = "0.3"
|
fuzzy-matcher = "0.3"
|
||||||
|
|
63
src/main.rs
63
src/main.rs
|
@ -6,8 +6,7 @@ use winit::event::{
|
||||||
WindowEvent::{CloseRequested, ReceivedCharacter},
|
WindowEvent::{CloseRequested, ReceivedCharacter},
|
||||||
};
|
};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
use winit::platform::unix::x11::util::WindowType;
|
use winit::platform::unix::WindowExtUnix;
|
||||||
use winit::platform::unix::{WindowBuilderExtUnix, WindowExtUnix, XWindowType};
|
|
||||||
use winit::window::{Window, WindowBuilder};
|
use winit::window::{Window, WindowBuilder};
|
||||||
|
|
||||||
use self::core::roftl::Roftl;
|
use self::core::roftl::Roftl;
|
||||||
|
@ -16,6 +15,7 @@ mod core;
|
||||||
mod matcher;
|
mod matcher;
|
||||||
mod sources;
|
mod sources;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
mod window;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
@ -37,67 +37,12 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
debug! {"Source roftl sources"}
|
debug! {"Source roftl sources"}
|
||||||
roftl.source();
|
roftl.source();
|
||||||
|
|
||||||
debug! {"Build window"}
|
let (window, event_loop) = window::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()}
|
|
||||||
|
|
||||||
debug! {"Draw primary state to window"}
|
debug! {"Draw primary state to window"}
|
||||||
ui::draw(&window, "", roftl.narrow(""), 0);
|
ui::draw(&window, "", roftl.narrow(""), 0);
|
||||||
|
|
||||||
debug! {"Start event loop"}
|
trace! {"Start event loop"}
|
||||||
let roftl_loop = RoftlLoop::new(roftl, window);
|
let roftl_loop = RoftlLoop::new(roftl, window);
|
||||||
roftl_loop.run(event_loop);
|
roftl_loop.run(event_loop);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,12 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use super::core::shared::{Entry, Source};
|
use super::core::shared::{Entry, Source};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use xcb::ffi::XCB_CURRENT_TIME;
|
use winit::platform::unix::x11::ffi::Connection;
|
||||||
use xcb_util::ewmh;
|
use xcb::x::{InputFocus, CURRENT_TIME};
|
||||||
use xcb_util::ffi::ewmh::XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER;
|
use xcb::{Xid, XidNew};
|
||||||
use xcb_util::icccm;
|
use xcb_wm::ewmh;
|
||||||
|
use xcb_wm::ewmh::proto::{GetActiveWindow, GetWmDesktop};
|
||||||
|
use xcb_wm::icccm;
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
action_data: HashMap<u64, (u32, i32)>,
|
action_data: HashMap<u64, (u32, i32)>,
|
||||||
|
@ -18,114 +20,99 @@ impl Window {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_normal_window(
|
fn is_normal_window(&self, con: &ewmh::Connection, window: xcb::x::Window) -> bool {
|
||||||
&self,
|
let wm_type_request = ewmh::proto::GetWmWindowType(window);
|
||||||
con: &ewmh::Connection,
|
let wm_types = con
|
||||||
window: u32,
|
.wait_for_reply(con.send_request(&wm_type_request))
|
||||||
) -> Result<bool, xcb::ReplyError> {
|
.window_types;
|
||||||
let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?;
|
|
||||||
|
|
||||||
for atom in wm_type_reply.atoms() {
|
for atom in wm_types {
|
||||||
if *atom == con.WM_WINDOW_TYPE_NORMAL() {
|
if atom == con.atoms._NET_WM_WINDOW_TYPE_NORMAL {
|
||||||
return Ok(true);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(false)
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_category(
|
// fn window_category(
|
||||||
&self,
|
// &self,
|
||||||
con: &ewmh::Connection,
|
// con: &ewmh::Connection,
|
||||||
window: u32,
|
// window: u32,
|
||||||
) -> Result<String, xcb::ReplyError> {
|
// ) -> Result<String, xcb::ReplyError> {
|
||||||
Ok(icccm::get_wm_class(&con, window)
|
// Ok(icccm::get_wm_class(&con, window)
|
||||||
.get_reply()?
|
// .get_reply()?
|
||||||
.class()
|
// .class()
|
||||||
.into())
|
// .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> {
|
fn switch_window(&self, con: &ewmh::Connection, window: u32) {
|
||||||
Ok(ewmh::get_wm_name(con, window).get_reply()?.string().into())
|
let change_active_window = ewmh::proto::SendActiveWindow::new(
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
con,
|
con,
|
||||||
screen,
|
unsafe { xcb::x::Window::new(window.into()) },
|
||||||
window,
|
1,
|
||||||
XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER,
|
xcb::x::CURRENT_TIME,
|
||||||
XCB_CURRENT_TIME,
|
Option::None,
|
||||||
active_window,
|
);
|
||||||
)
|
con.send_request(&change_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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_maximize_window(
|
// fn toggle_maximize_window(
|
||||||
&self,
|
// &self,
|
||||||
con: &ewmh::Connection,
|
// con: &ewmh::Connection,
|
||||||
window: u32,
|
// window: u32,
|
||||||
screen: i32,
|
// screen: i32,
|
||||||
) -> Result<(), xcb::ReplyError> {
|
// ) -> Result<(), xcb::ReplyError> {
|
||||||
let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
// let max_horz_atom = ewmh::Connection::WM_STATE_MAXIMIZED_HORZ(con);
|
||||||
let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
// let max_vert_atom = ewmh::Connection::WM_STATE_MAXIMIZED_VERT(con);
|
||||||
let action_atom = ewmh::STATE_TOGGLE;
|
// let action_atom = ewmh::STATE_TOGGLE;
|
||||||
|
//
|
||||||
debug! {"Toggle maximize for {}", window}
|
// debug! {"Toggle maximize for {}", window}
|
||||||
ewmh::request_change_wm_state(
|
// ewmh::request_change_wm_state(
|
||||||
con,
|
// con,
|
||||||
screen,
|
// screen,
|
||||||
window,
|
// window,
|
||||||
action_atom,
|
// action_atom,
|
||||||
max_horz_atom,
|
// max_horz_atom,
|
||||||
max_vert_atom,
|
// max_vert_atom,
|
||||||
0,
|
// 0,
|
||||||
)
|
// )
|
||||||
.request_check()?;
|
// .request_check()?;
|
||||||
|
//
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
fn close_window(
|
// fn close_window(
|
||||||
&self,
|
// &self,
|
||||||
con: &ewmh::Connection,
|
// con: &ewmh::Connection,
|
||||||
window: u32,
|
// window: u32,
|
||||||
screen: i32,
|
// screen: i32,
|
||||||
) -> Result<(), xcb::ReplyError> {
|
// ) -> Result<(), xcb::ReplyError> {
|
||||||
debug! {"Toggle maximize for {}", window}
|
// debug! {"Toggle maximize for {}", window}
|
||||||
ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
// ewmh::request_close_window(con, screen, window, XCB_CURRENT_TIME, 0).request_check()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
fn toggle_hide_window(
|
// fn toggle_hide_window(
|
||||||
&self,
|
// &self,
|
||||||
con: &ewmh::Connection,
|
// con: &ewmh::Connection,
|
||||||
window: u32,
|
// window: u32,
|
||||||
screen: i32,
|
// screen: i32,
|
||||||
) -> Result<(), xcb::ReplyError> {
|
// ) -> Result<(), xcb::ReplyError> {
|
||||||
let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
// let hidden_atom = ewmh::Connection::WM_STATE_HIDDEN(con);
|
||||||
let action_atom = ewmh::STATE_TOGGLE;
|
// let action_atom = ewmh::STATE_TOGGLE;
|
||||||
|
//
|
||||||
debug! {"Toggle hidden for {}", window}
|
// debug! {"Toggle hidden for {}", window}
|
||||||
ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0)
|
// ewmh::request_change_wm_state(con, screen, window, action_atom, hidden_atom, 0, 0)
|
||||||
.request_check()?;
|
// .request_check()?;
|
||||||
|
//
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Source for Window {
|
impl Source for Window {
|
||||||
|
@ -135,38 +122,34 @@ impl Source for Window {
|
||||||
|
|
||||||
fn entries(&mut self) -> Vec<Entry> {
|
fn entries(&mut self) -> Vec<Entry> {
|
||||||
let (xcb_conn, screen_num) =
|
let (xcb_conn, screen_num) =
|
||||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
xcb::Connection::connect(None).expect("Could not connect to X server");
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
let ewmh_conn = ewmh::Connection::connect(&xcb_conn);
|
||||||
.map_err(|(e, _)| e)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let client_list_reply = xcb_util::ewmh::get_client_list(&ewmh_conn, screen_num)
|
let client_list_reply = ewmh_conn.send_request(&xcb_wm::ewmh::proto::GetClientList);
|
||||||
.get_reply()
|
let windows = ewmh_conn.wait_for_reply(client_list_reply).clients;
|
||||||
.unwrap();
|
|
||||||
let windows = client_list_reply.windows();
|
|
||||||
|
|
||||||
let mut entries: Vec<Entry> = vec![];
|
let mut entries: Vec<Entry> = vec![];
|
||||||
|
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
for w in windows {
|
for w in windows {
|
||||||
match self.is_normal_window(&ewmh_conn, *w) {
|
match self.is_normal_window(&ewmh_conn, w) {
|
||||||
Ok(true) => {}
|
true => {}
|
||||||
Ok(false) => continue,
|
false => continue,
|
||||||
Err(_err) => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = self.window_title(&ewmh_conn, *w).unwrap();
|
let title = self.window_title(&ewmh_conn, w);
|
||||||
let category = self.window_category(&ewmh_conn, *w).unwrap();
|
let category: String = "Test".into(); //self.window_category(&ewmh_conn, *w).unwrap();
|
||||||
|
|
||||||
debug!("Found window {} - {}", title, category);
|
debug!("Found window {} - {}", title, category);
|
||||||
|
|
||||||
entries.push(Entry {
|
entries.push(Entry {
|
||||||
source: self.name(),
|
source: self.name(),
|
||||||
name: category,
|
name: title.clone(),
|
||||||
description: title,
|
description: title.clone(),
|
||||||
identifier: count,
|
identifier: count,
|
||||||
});
|
});
|
||||||
self.action_data.insert(count, (*w, screen_num));
|
self.action_data
|
||||||
|
.insert(count, (w.resource_id(), screen_num));
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
@ -181,43 +164,42 @@ impl Source for Window {
|
||||||
0 => {
|
0 => {
|
||||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||||
let (xcb_conn, _screen_num) =
|
let (xcb_conn, _screen_num) =
|
||||||
xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
xcb::Connection::connect(None).expect("Could not connect to X server");
|
||||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn)
|
let ewmh_conn = ewmh::Connection::connect(&xcb_conn);
|
||||||
.map_err(|(e, _)| e)
|
self.switch_window(&ewmh_conn, window);
|
||||||
.unwrap();
|
xcb_conn.flush().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();
|
|
||||||
}
|
}
|
||||||
|
// 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},
|
_ => panic! {"Unknown action {:?}", action},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use super::core;
|
|
||||||
|
|
||||||
mod ui;
|
mod ui;
|
||||||
pub use ui::draw;
|
pub use ui::draw;
|
||||||
pub use ui::draw_actions;
|
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::borrow::BorrowMut;
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use winit::{platform::unix::WindowExtUnix, window::Window};
|
use winit::{platform::unix::WindowExtUnix, window::Window};
|
||||||
|
use xcb::XidNew;
|
||||||
|
|
||||||
use super::core::shared::Entry;
|
|
||||||
use super::painter;
|
use super::painter;
|
||||||
|
use crate::core::shared::Entry;
|
||||||
|
|
||||||
pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, selection: usize) {
|
pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, selection: usize) {
|
||||||
let context = make_context(&window);
|
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
|
where
|
||||||
T: BorrowMut<*mut c_void>,
|
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)
|
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)
|
let window_attribute_request = unsafe {
|
||||||
.get_reply()
|
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")
|
.expect("Could not fetch attributes for window")
|
||||||
.visual();
|
.visual();
|
||||||
|
|
||||||
debug! {"Found visualid {} for window", window_visualid}
|
debug! {"Found visualid {} for window", window_visualid}
|
||||||
|
|
||||||
debug! {"Trying to map visualid to visualtype {}", window_visualid}
|
debug! {"Trying to map visualid to visualtype {}", window_visualid}
|
||||||
let visualtype = xcb_conn
|
let visualtype = *xcb_conn
|
||||||
.get_setup()
|
.get_setup()
|
||||||
.roots()
|
.roots()
|
||||||
.flat_map(|screen| screen.allowed_depths())
|
.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 xlib_window_id = window.xlib_window().expect("Could not get xlib window");
|
||||||
|
|
||||||
let visual_type = unsafe {
|
let visual_type = unsafe {
|
||||||
let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32).base;
|
let mut visual_type = get_visual_type(winit_xcb_conn, xlib_window_id as u32);
|
||||||
cairo::XCBVisualType::from_raw_none(
|
cairo::XCBVisualType::from_raw_none(mem::transmute(&mut visual_type))
|
||||||
&mut visual_type as *mut xcb::ffi::xcb_visualtype_t
|
|
||||||
as *mut cairo::ffi::xcb_visualtype_t,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let connection = unsafe {
|
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