summaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs225
1 files changed, 215 insertions, 10 deletions
diff --git a/src/main.rs b/src/main.rs
index 56c8629..4be5992 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,4 @@
+use gdk::prelude::*;
use gio::prelude::*;
use gtk::prelude::*;
@@ -9,25 +10,183 @@ use std::rc::Rc;
mod systray;
+const STYLE: &str = r#"
+#bar {
+ background-color: #222;
+}
+
+#desktops {
+ color: #eee;
+}
+
+.tray-client {
+ background-color: #fff;
+}
+"#;
+
+struct Desktops {
+ display: *mut x11::xlib::Display,
+ root: x11::xlib::Window,
+ label: glib::WeakRef<gtk::Label>,
+ cardinal: x11::xlib::Atom,
+ string: x11::xlib::Atom,
+ curdesk: x11::xlib::Atom,
+ desknames: x11::xlib::Atom,
+ deskcount: x11::xlib::Atom,
+}
+
+use std::os::raw::{c_long, c_ulong};
+
+impl Desktops {
+ fn new(label: &gtk::Label) -> Self {
+ let display = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
+ Self {
+ label: label.downgrade(),
+ display,
+ root: unsafe { x11::xlib::XRootWindow(display, 0) },
+ cardinal: systray::intern_xatom("CARDINAL", display),
+ string: systray::intern_xatom("UTF8_STRING", display),
+ curdesk: systray::intern_xatom("_NET_CURRENT_DESKTOP", display),
+ desknames: systray::intern_xatom("_NET_DESKTOP_NAMES", display),
+ deskcount: systray::intern_xatom("_NET_NUMBER_OF_DESKTOPS", display),
+ }
+ }
+
+ fn get_number_property(&self, prop: x11::xlib::Atom) -> usize {
+ unsafe {
+ let mut ret_type: x11::xlib::Atom = 0;
+ let mut format = 0;
+ let mut nitems: c_ulong = 0;
+ let mut bytes_after: c_ulong = 0;
+ let mut retval: *mut u8 = std::ptr::null_mut();
+ x11::xlib::XGetWindowProperty(
+ self.display,
+ self.root,
+ prop,
+ 0,
+ 1,
+ 0,
+ self.cardinal,
+ &mut ret_type as *mut _,
+ &mut format as *mut _,
+ &mut nitems as *mut _,
+ &mut bytes_after as *mut _,
+ &mut retval as *mut _,
+ );
+ assert_eq!(ret_type, self.cardinal);
+ let value = *(retval as *mut std::os::raw::c_long);
+ x11::xlib::XFree(retval as *mut _);
+ value as usize
+ }
+ }
+
+ fn get_string_property(&self, prop: x11::xlib::Atom) -> String {
+ unsafe {
+ let mut ret_type: x11::xlib::Atom = 0;
+ let mut format = 0;
+ let mut nitems: c_ulong = 0;
+ let mut bytes_after: c_ulong = 0;
+ let mut retval: *mut u8 = std::ptr::null_mut();
+ x11::xlib::XGetWindowProperty(
+ self.display,
+ self.root,
+ prop,
+ 0,
+ 100,
+ 0,
+ self.string,
+ &mut ret_type as *mut _,
+ &mut format as *mut _,
+ &mut nitems as *mut _,
+ &mut bytes_after as *mut _,
+ &mut retval as *mut _,
+ );
+ let s = std::slice::from_raw_parts(retval, nitems as usize);
+ let s = std::str::from_utf8_unchecked(s);
+ let s = String::from(s);
+ x11::xlib::XFree(retval as *mut _);
+ s
+ }
+ }
+
+ fn get_desktops(&self) -> Vec<String> {
+ let ndesktops = self.get_number_property(self.deskcount);
+ let desknames = self.get_string_property(self.desknames);
+ desknames
+ .split('\0')
+ .take(ndesktops)
+ .map(String::from)
+ .collect()
+ }
+
+ fn update_label(&self) {
+ let desktops = self.get_desktops();
+ let curdesk = self.get_number_property(self.curdesk);
+ let desklabel: String = desktops
+ .into_iter()
+ .enumerate()
+ .map(|(i, s)| {
+ if i == curdesk {
+ format!("[{}] ", s)
+ } else {
+ format!("{} ", s)
+ }
+ })
+ .collect();
+ if let Some(label) = self.label.upgrade() {
+ label.set_text(&desklabel);
+ }
+ }
+}
+
+impl Drop for Desktops {
+ fn drop(&mut self) {
+ let p = std::mem::replace(&mut self.display, std::ptr::null_mut());
+ if p != std::ptr::null_mut() {
+ unsafe { x11::xlib::XCloseDisplay(p) };
+ }
+ }
+}
+
+unsafe extern "C" fn property_change_filter(
+ _xevent: *mut std::ffi::c_void,
+ _gdkevent: *mut gdk_sys::GdkEvent,
+ _pw: *mut std::ffi::c_void,
+) -> i32 {
+ let atoms: &Desktops = &(*(_pw as *mut Desktops));
+ let xevent: &mut x11::xlib::XEvent = &mut (*(_xevent as *mut x11::xlib::XEvent));
+ if xevent.type_ == x11::xlib::PropertyNotify {
+ let pev = &xevent.property;
+ if pev.atom == atoms.curdesk || pev.atom == atoms.desknames {
+ atoms.update_label();
+ }
+ }
+ gdk_sys::GDK_FILTER_CONTINUE
+}
+
fn build_ui(application: &gtk::Application) {
let window = gtk::ApplicationWindow::new(application);
-
- window.set_title("First GTK+ Program");
- window.set_border_width(10);
- window.set_position(gtk::WindowPosition::Center);
- window.set_default_size(350, 70);
+ window.set_widget_name("bar");
+ window.set_title("My bar");
+ window.set_decorated(false);
+ window.set_type_hint(gdk::WindowTypeHint::Dock);
+ let width = window.get_screen().expect("No screen?").get_width();
+ window.move_(0, 0);
+ window.resize(width, 24);
let disp = gdk::DisplayManager::get()
.get_default_display()
.expect("Strange");
let screen = disp.get_default_screen();
- let vbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
- window.add(&vbox);
+ let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 2);
+ window.add(&hbox);
let tray = Rc::new(RefCell::new(systray::Systray::manage_screen(&screen)));
- vbox.add(tray.borrow().get_tray_widget());
- let label = gtk::Label::new(Some("Nothing to see here"));
- vbox.add(&label);
+ let desktop_label = gtk::Label::new(Some("Desktops"));
+ desktop_label.set_widget_name("desktops");
+ hbox.pack_start(&desktop_label, false, false, 0);
+ hbox.pack_start(&gtk::Label::new(None), true, true, 0);
+ hbox.pack_end(tray.borrow().get_tray_widget(), false, false, 0);
gtk::timeout_add(
1000,
clone!(@weak tray => @default-return glib::Continue(false), move || {
@@ -41,6 +200,40 @@ fn build_ui(application: &gtk::Application) {
});
window.show_all();
+ let topw = window
+ .get_toplevel()
+ .expect("top level must be possible")
+ .get_window()
+ .expect("Window must be there");
+ let cardinal = gdk::Atom::intern("CARDINAL");
+ let strut = gdk::Atom::intern("_NET_WM_STRUT");
+ let partial = gdk::Atom::intern("_NET_WM_STRUT_PARTIAL");
+ let data = gdk::ChangeData::ULongs(&[0, 0, 24, 0]);
+ gdk::property_change(&topw, &strut, &cardinal, 32, gdk::PropMode::Replace, data);
+ let partials = [0, 0, 24, 0, 0, 0, 0, 0, 0, (width - 1) as u64, 0, 0];
+ let data = gdk::ChangeData::ULongs(&partials);
+ gdk::property_change(&topw, &partial, &cardinal, 32, gdk::PropMode::Replace, data);
+
+ // Now try to register for changes on the root window
+ let win = window
+ .get_screen()
+ .expect("no screen")
+ .get_root_window()
+ .expect("no root");
+ let events = win.get_events();
+ win.set_events(events | gdk::EventMask::PROPERTY_CHANGE_MASK);
+ let disp = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
+ let desktops = Desktops::new(&desktop_label);
+ let atoms = Box::into_raw(Box::new(desktops));
+ unsafe { x11::xlib::XCloseDisplay(disp) };
+ unsafe {
+ use glib::translate::ToGlibPtr;
+ gdk_sys::gdk_window_add_filter(
+ win.to_glib_none().0,
+ Some(property_change_filter),
+ atoms as *mut _,
+ );
+ }
}
fn main() {
@@ -48,6 +241,18 @@ fn main() {
.expect("Initialization failed...");
application.connect_activate(|app| {
+ // The CSS "magic" happens here.
+ let provider = gtk::CssProvider::new();
+ provider
+ .load_from_data(STYLE.as_bytes())
+ .expect("Failed to load CSS");
+ // We give the CssProvided to the default screen so the CSS rules we added
+ // can be applied to our window.
+ gtk::StyleContext::add_provider_for_screen(
+ &gdk::Screen::get_default().expect("Error initializing gtk css provider."),
+ &provider,
+ gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
+ );
build_ui(app);
});