Read config from xdg directory
This commit is contained in:
parent
39a888c7ba
commit
c5ca08f556
8 changed files with 86 additions and 172 deletions
|
@ -19,6 +19,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
|
||||
# read and parse configuration
|
||||
config = "0.13"
|
||||
shellexpand = "2.1"
|
||||
|
||||
# dependencies for building the `window` surface
|
||||
winit = { git = "https://github.com/rust-windowing/winit" }
|
||||
|
|
28
default.toml
28
default.toml
|
@ -1,28 +0,0 @@
|
|||
log_level = "trace"
|
||||
|
||||
[completions]
|
||||
"e" = "emacs"
|
||||
"f" = "firefox"
|
||||
"t" = "xfce4-terminal"
|
||||
"j" = "jetbrains"
|
||||
|
||||
[sources.primary]
|
||||
matcher = "Prefix"
|
||||
source = "Windows"
|
||||
|
||||
[sources.additional]
|
||||
matcher = "Fuse"
|
||||
source = ["Apps", "Shell", "Test"]
|
||||
|
||||
[theme]
|
||||
font = ["Sans Regular", 13]
|
||||
border = 2
|
||||
divider = 3
|
||||
|
||||
[theme.color_scheme]
|
||||
base = [0.13, 0.05, 0.23, 0.9]
|
||||
border = [0.23, 0.05, 0.11, 1.0]
|
||||
highlight = [0.12, 0.04, 0.08, 0.9]
|
||||
divider = [0.23, 0.05, 0.11, 1.0]
|
||||
text = [0.87, 0.95, 0.77, 1.0]
|
||||
text_highlight = [0.6, 0.8, 0.4, 1.0]
|
|
@ -1,8 +1,10 @@
|
|||
use config::Config;
|
||||
use config::builder::DefaultState;
|
||||
use config::ConfigError;
|
||||
use config::{Config, ConfigBuilder};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Color(f64, f64, f64, f64);
|
||||
|
@ -98,16 +100,27 @@ pub struct Settings {
|
|||
|
||||
impl Settings {
|
||||
pub fn parse() -> Result<Self, ConfigError> {
|
||||
let mut config: Config = Config::builder()
|
||||
.add_source(config::File::with_name("default"))
|
||||
.add_source(
|
||||
let mut config: ConfigBuilder<DefaultState> = Config::builder();
|
||||
|
||||
let xdg_path_expanded = shellexpand::tilde("~/.config/roftl/config.toml");
|
||||
let xdg_config_path = Path::new(xdg_path_expanded.as_ref());
|
||||
let cwd_config_path = Path::new("default.toml");
|
||||
|
||||
if xdg_config_path.exists() {
|
||||
config = config.add_source(config::File::from(xdg_config_path));
|
||||
}
|
||||
|
||||
if cwd_config_path.exists() {
|
||||
config = config.add_source(config::File::from(cwd_config_path));
|
||||
}
|
||||
|
||||
config = config.add_source(
|
||||
config::Environment::with_prefix("roftl")
|
||||
.ignore_empty(true)
|
||||
.separator("_"),
|
||||
)
|
||||
.build()?;
|
||||
);
|
||||
|
||||
config.try_deserialize()
|
||||
config.build()?.try_deserialize()
|
||||
}
|
||||
|
||||
pub fn primary_matcher(&self) -> Box<dyn super::shared::Matcher> {
|
||||
|
|
|
@ -3,10 +3,8 @@ use std::collections::HashMap;
|
|||
use super::core::shared::{Entry, Source};
|
||||
use log::debug;
|
||||
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 {
|
||||
|
@ -21,11 +19,15 @@ impl Window {
|
|||
}
|
||||
|
||||
fn is_normal_window(&self, con: &ewmh::Connection, window: xcb::x::Window) -> bool {
|
||||
let wm_type_request = ewmh::proto::GetWmWindowType(window);
|
||||
let wm_type_request = ewmh::GetWmWindowType(window);
|
||||
let wm_types = con
|
||||
.wait_for_reply(con.send_request(&wm_type_request))
|
||||
.window_types;
|
||||
|
||||
if wm_types.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
for atom in wm_types {
|
||||
if atom == con.atoms._NET_WM_WINDOW_TYPE_NORMAL {
|
||||
return true;
|
||||
|
@ -35,84 +37,28 @@ impl Window {
|
|||
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: &icccm::Connection, window: xcb::x::Window) -> String {
|
||||
let wm_category_request = con.send_request(&icccm::GetWmClass::new(window));
|
||||
let wm_category = con.wait_for_reply(wm_category_request);
|
||||
wm_category.class
|
||||
}
|
||||
|
||||
fn window_title(&self, con: &ewmh::Connection, window: xcb::x::Window) -> String {
|
||||
let wm_name_request = ewmh::proto::GetWmName(window);
|
||||
let wm_name_request = ewmh::GetWmName(window);
|
||||
let wm_name = con.wait_for_reply(con.send_request(&wm_name_request));
|
||||
wm_name.name
|
||||
}
|
||||
|
||||
fn switch_window(&self, con: &ewmh::Connection, window: u32) {
|
||||
let change_active_window = ewmh::proto::SendActiveWindow::new(
|
||||
let change_active_window = ewmh::SendActiveWindow::new(
|
||||
con,
|
||||
unsafe { xcb::x::Window::new(window.into()) },
|
||||
1,
|
||||
xcb::x::CURRENT_TIME,
|
||||
Option::None,
|
||||
);
|
||||
con.send_request(&change_active_window);
|
||||
con.send_and_check_request(&change_active_window).unwrap();
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -123,29 +69,30 @@ impl Source for Window {
|
|||
fn entries(&mut self) -> Vec<Entry> {
|
||||
let (xcb_conn, screen_num) =
|
||||
xcb::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(&xcb_conn);
|
||||
let ewmh_con = ewmh::Connection::connect(&xcb_conn);
|
||||
let icccm_con = icccm::Connection::connect(&xcb_conn);
|
||||
|
||||
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 client_list_reply = ewmh_con.send_request(&xcb_wm::ewmh::GetClientList);
|
||||
let windows = ewmh_con.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) {
|
||||
match self.is_normal_window(&ewmh_con, w) {
|
||||
true => {}
|
||||
false => continue,
|
||||
}
|
||||
|
||||
let title = self.window_title(&ewmh_conn, w);
|
||||
let category: String = "Test".into(); //self.window_category(&ewmh_conn, *w).unwrap();
|
||||
let title = self.window_title(&ewmh_con, w);
|
||||
let category: String = self.window_category(&icccm_con, w);
|
||||
|
||||
debug!("Found window {} - {}", title, category);
|
||||
|
||||
entries.push(Entry {
|
||||
source: self.name(),
|
||||
name: title.clone(),
|
||||
description: title.clone(),
|
||||
name: category,
|
||||
description: title,
|
||||
identifier: count,
|
||||
});
|
||||
self.action_data
|
||||
|
@ -157,8 +104,17 @@ impl Source for Window {
|
|||
entries
|
||||
}
|
||||
|
||||
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
||||
vec![
|
||||
"Switch".into(),
|
||||
"Toggle maximize".into(),
|
||||
"Toggle hide".into(),
|
||||
"Close".into(),
|
||||
]
|
||||
}
|
||||
|
||||
fn exec_action(&self, entry: &Entry, action: u8) {
|
||||
debug!("Doing action {:?} for entry {}", action, entry.name);
|
||||
debug!("Executing action {:?} for entry {}", action, entry.name);
|
||||
|
||||
match action {
|
||||
0 => {
|
||||
|
@ -169,47 +125,7 @@ impl Source for Window {
|
|||
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},
|
||||
}
|
||||
}
|
||||
|
||||
fn actions(&self, _entry: &Entry) -> Vec<String> {
|
||||
vec![
|
||||
"Switch".into(),
|
||||
"Toggle maximize".into(),
|
||||
"Toggle hide".into(),
|
||||
"Close".into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ impl<'a> Painter<'a> {
|
|||
x: T,
|
||||
y: T,
|
||||
source: &'a str,
|
||||
result: &'a str,
|
||||
result: [&str; 2],
|
||||
indices: &[usize],
|
||||
selected: bool,
|
||||
) where
|
||||
|
@ -107,10 +107,17 @@ impl<'a> Painter<'a> {
|
|||
ctx.fill().unwrap();
|
||||
|
||||
// draw text
|
||||
//source
|
||||
ctx.move_to(x + 0.2, y);
|
||||
ctx.theme_text(&source, selected, &[], &self.theme);
|
||||
|
||||
// category
|
||||
ctx.move_to(x + 3.5, y);
|
||||
ctx.theme_text(&result, selected, indices, &self.theme);
|
||||
ctx.theme_text(result[0], selected, indices, &self.theme);
|
||||
|
||||
// title
|
||||
ctx.move_to(x + 3.5 + 10.0, y);
|
||||
ctx.theme_text(result[1], selected, indices, &self.theme);
|
||||
}
|
||||
|
||||
pub fn divider<T>(&self, x: T, y: T)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use pangocairo::pango::{self, AttrInt, AttrList, Attribute, FontDescription};
|
||||
use pangocairo::pango::{self, AttrInt, AttrList, Attribute, EllipsizeMode, FontDescription};
|
||||
|
||||
// (r,g,b,a)
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -126,7 +126,9 @@ impl ThemedContextExt for cairo::Context {
|
|||
font.set_size(theme.font.1 * pango::SCALE);
|
||||
layout.set_font_description(Some(&font));
|
||||
|
||||
layout.set_width(200 * pango::SCALE);
|
||||
layout.set_spacing(0);
|
||||
layout.set_ellipsize(EllipsizeMode::End);
|
||||
|
||||
layout.set_text(text);
|
||||
|
||||
|
|
17
src/ui/ui.rs
17
src/ui/ui.rs
|
@ -22,7 +22,8 @@ pub fn draw(window: &Window, input: &str, result: Vec<(&Entry, Vec<usize>)>, sel
|
|||
|
||||
result.iter().enumerate().for_each(|(i, r)| {
|
||||
let e = r.0;
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, selection == i);
|
||||
let result = [e.name.as_str(), e.description.as_str()];
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, result, &r.1, selection == i);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -36,25 +37,27 @@ pub fn redraw_quick(window: &Window, result: Vec<(&Entry, Vec<usize>)>, selectio
|
|||
|
||||
result.iter().enumerate().for_each(|(i, r)| {
|
||||
let e = r.0;
|
||||
let result = [e.name.as_str(), e.description.as_str()];
|
||||
|
||||
// clear first and last as these may produce artifacts otherwise
|
||||
if i == 0 {
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, false)
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, result, &r.1, false)
|
||||
}
|
||||
if i == result.len() - 1 {
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, false)
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, result, &r.1, false)
|
||||
}
|
||||
|
||||
// clear boxes around selection as these could be the old selection
|
||||
if selection > 0 && i == (selection - 1) {
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, false)
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, result, &r.1, false)
|
||||
}
|
||||
if i == (selection + 1) {
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, false)
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, result, &r.1, false)
|
||||
}
|
||||
|
||||
// mark selection, note that this negates any unmarking above in case they are the same
|
||||
if i == selection {
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, &e.name, &r.1, true)
|
||||
painter.result_box(0, 1 + (i as u32), &e.source, result, &r.1, true)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -75,7 +78,7 @@ pub fn draw_actions(window: &Window, actions: Vec<String>, input: &str) {
|
|||
0,
|
||||
1 + (i as u32),
|
||||
&usize::to_string(&(i + 1)),
|
||||
r,
|
||||
[r.as_str(), ""],
|
||||
&[],
|
||||
false,
|
||||
);
|
||||
|
|
|
@ -22,7 +22,7 @@ use winit::window::{Window, WindowBuilder};
|
|||
use xcb::ffi::xcb_connection_t;
|
||||
use xcb::x::{GrabMode, PropMode};
|
||||
use xcb::XidNew;
|
||||
use xcb_wm::ewmh;
|
||||
use xcb_wm::{ewmh, icccm};
|
||||
|
||||
pub fn build_window() -> Result<(Window, EventLoop<()>)> {
|
||||
trace! {"Initialize `winit` event loop"}
|
||||
|
@ -68,25 +68,24 @@ fn postprocess_window(window: &Window) -> Result<()> {
|
|||
mem::ManuallyDrop::new(con)
|
||||
};
|
||||
|
||||
let ewmh_con = xcb_wm::ewmh::Connection::connect(&xcb_con);
|
||||
let icccm_con = xcb_wm::icccm::Connection::connect(&xcb_con);
|
||||
let ewmh_con = ewmh::Connection::connect(&xcb_con);
|
||||
let icccm_con = icccm::Connection::connect(&xcb_con);
|
||||
|
||||
trace! {"Unmap window"}
|
||||
let req = xcb::x::UnmapWindow { window: xcb_window };
|
||||
xcb_con.send_and_check_request(&req)?;
|
||||
|
||||
trace! {"Hide window from taskbar"}
|
||||
let window_state = xcb_wm::ewmh::proto::SendWmState::new(
|
||||
ewmh_con.send_and_check_request(&ewmh::SendWmState::new(
|
||||
&ewmh_con,
|
||||
xcb_window,
|
||||
PropMode::Append,
|
||||
xcb::x::PropMode::Append,
|
||||
[
|
||||
ewmh_con.atoms._NET_WM_STATE,
|
||||
ewmh_con.atoms._NET_WM_STATE_SKIP_TASKBAR,
|
||||
],
|
||||
1,
|
||||
);
|
||||
ewmh_con.send_request(&window_state);
|
||||
))?;
|
||||
|
||||
trace! {"Center window on screen"}
|
||||
let screen_pixel = window
|
||||
|
@ -101,7 +100,7 @@ fn postprocess_window(window: &Window) -> Result<()> {
|
|||
.position();
|
||||
|
||||
// Configure the window to the center with a size of 800x600 pixels.
|
||||
let window_width = 800u32;
|
||||
let window_width = 1000u32;
|
||||
let window_height = 300u32;
|
||||
let x = screen_position.x as i32 + (screen_pixel.width as i32 / 2 - window_width as i32 / 2);
|
||||
let y = screen_position.y as i32 + (screen_pixel.height as i32 / 2 - window_height as i32 / 2);
|
||||
|
@ -129,5 +128,6 @@ fn postprocess_window(window: &Window) -> Result<()> {
|
|||
};
|
||||
xcb_con.send_request(&req);
|
||||
|
||||
xcb_con.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue