summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Silverstone <daniel.silverstone@codethink.co.uk>2020-04-24 08:23:24 +0100
committerDaniel Silverstone <daniel.silverstone@codethink.co.uk>2020-04-24 08:23:24 +0100
commit3095ae240fe2d7e4746d0037ebd93e81ddc2028c (patch)
tree8fade34bcd2c07b960e226a8000aa92a133f558b
downloadgtkplay-3095ae240fe2d7e4746d0037ebd93e81ddc2028c.tar.bz2
pre-refactor
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock558
-rw-r--r--Cargo.toml16
-rw-r--r--src/main.rs55
-rw-r--r--src/systray.rs378
5 files changed, 1008 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..574e545
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,558 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+[[package]]
+name = "anyhow"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
+
+[[package]]
+name = "atk"
+version = "0.8.0"
+source = "git+https://github.com/gtk-rs/atk#83b5e5a6c2d750244829719a12ec483f75da33d8"
+dependencies = [
+ "atk-sys",
+ "bitflags",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+]
+
+[[package]]
+name = "atk-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+
+[[package]]
+name = "cairo-rs"
+version = "0.8.1"
+source = "git+https://github.com/gtk-rs/cairo#9fac021d5ec6bbe5b3dd496771342c2a654fb4a4"
+dependencies = [
+ "bitflags",
+ "cairo-sys-rs",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.9.2"
+source = "git+https://github.com/gtk-rs/cairo#9fac021d5ec6bbe5b3dd496771342c2a654fb4a4"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.52"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d"
+
+[[package]]
+name = "either"
+version = "1.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7"
+dependencies = [
+ "proc-macro-hack",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-task"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
+
+[[package]]
+name = "futures-util"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
+dependencies = [
+ "futures-core",
+ "futures-macro",
+ "futures-task",
+ "pin-utils",
+ "proc-macro-hack",
+ "proc-macro-nested",
+ "slab",
+]
+
+[[package]]
+name = "gdk"
+version = "0.12.1"
+source = "git+https://github.com/gtk-rs/gdk#0287908a189dd0008d0c1df84fe43d3ac48787b2"
+dependencies = [
+ "bitflags",
+ "cairo-rs",
+ "cairo-sys-rs",
+ "gdk-pixbuf",
+ "gdk-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gdk-pixbuf"
+version = "0.8.0"
+source = "git+https://github.com/gtk-rs/gdk-pixbuf#3310e98ea62bd4bb1b327a7db1be9c49266f07fa"
+dependencies = [
+ "gdk-pixbuf-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "gdk-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "gio"
+version = "0.8.1"
+source = "git+https://github.com/gtk-rs/gio#fb5777a7ab69a0a87b56ed85615adc4f46854f89"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "glib"
+version = "0.9.3"
+source = "git+https://github.com/gtk-rs/glib#b5b4df5dc7ca52af170aafd18203bfdd42ca5608"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "futures-util",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.9.0"
+source = "git+https://github.com/gtk-rs/glib#b5b4df5dc7ca52af170aafd18203bfdd42ca5608"
+dependencies = [
+ "anyhow",
+ "heck",
+ "itertools",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "gtk"
+version = "0.8.1"
+source = "git+https://github.com/gtk-rs/gtk#6774194cc934da76f6f205f09e46e6cb4a46f931"
+dependencies = [
+ "atk",
+ "bitflags",
+ "cairo-rs",
+ "cairo-sys-rs",
+ "cc",
+ "gdk",
+ "gdk-pixbuf",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio",
+ "gio-sys",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "gtk-sys",
+ "libc",
+ "once_cell",
+ "pango",
+ "pango-sys",
+]
+
+[[package]]
+name = "gtk-sys"
+version = "0.9.2"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "atk-sys",
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "gtkplay"
+version = "0.1.0"
+dependencies = [
+ "cairo-rs",
+ "gdk",
+ "gdk-sys",
+ "gio",
+ "glib",
+ "gtk",
+ "x11",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "itertools"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
+
+[[package]]
+name = "once_cell"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
+
+[[package]]
+name = "pango"
+version = "0.8.0"
+source = "git+https://github.com/gtk-rs/pango#07a8f84a84a1a32193bcd3e34513c44358546999"
+dependencies = [
+ "bitflags",
+ "glib",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+ "pango-sys",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.9.1"
+source = "git+https://github.com/gtk-rs/sys#6bd0cab694a0b446d5fe530cd87bf2b499f065ff"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
+
+[[package]]
+name = "proc-macro-crate"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e"
+dependencies = [
+ "toml",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "syn-mid",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-hack"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
+
+[[package]]
+name = "proc-macro-nested"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399"
+
+[[package]]
+name = "slab"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
+
+[[package]]
+name = "syn"
+version = "1.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "syn-mid"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54b3d3d2ff68104100ab257bb6bb0cb26c901abe4bd4ba15961f3bf867924012"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca972988113b7715266f91250ddb98070d033c62a011fa0fcc57434a649310dd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
+
+[[package]]
+name = "version_check"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
+
+[[package]]
+name = "x11"
+version = "2.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ecd092546cb16f25783a5451538e73afc8d32e242648d54f4ae5459ba1e773"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..59688c5
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "gtkplay"
+version = "0.1.0"
+authors = ["Daniel Silverstone <daniel.silverstone@codethink.co.uk>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+gtk = {git="https://github.com/gtk-rs/gtk"}
+gdk = {git="https://github.com/gtk-rs/gdk"}
+gdk-sys = {git="https://github.com/gtk-rs/sys"}
+gio = {git="https://github.com/gtk-rs/gio"}
+glib = {git="https://github.com/gtk-rs/glib"}
+cairo-rs = {git="https://github.com/gtk-rs/cairo"}
+x11 = {version = "*", features = ["xlib"]} \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..56c8629
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,55 @@
+use gio::prelude::*;
+use gtk::prelude::*;
+
+use glib::clone;
+
+use std::cell::RefCell;
+use std::env::args;
+use std::rc::Rc;
+
+mod systray;
+
+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);
+
+ 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 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);
+ gtk::timeout_add(
+ 1000,
+ clone!(@weak tray => @default-return glib::Continue(false), move || {
+ println!("Notifying potential clients");
+ tray.borrow().notify_potential_clients();
+ glib::Continue(false)
+ }),
+ );
+ window.connect_destroy(move |_| {
+ tray.borrow_mut().unmanage();
+ });
+
+ window.show_all();
+}
+
+fn main() {
+ let application = gtk::Application::new(Some("org.digital-scurf.basic"), Default::default())
+ .expect("Initialization failed...");
+
+ application.connect_activate(|app| {
+ build_ui(app);
+ });
+
+ application.run(&args().collect::<Vec<_>>());
+}
diff --git a/src/systray.rs b/src/systray.rs
new file mode 100644
index 0000000..25c5707
--- /dev/null
+++ b/src/systray.rs
@@ -0,0 +1,378 @@
+use gtk::prelude::*;
+
+use std::convert::TryInto;
+
+pub struct Systray(Box<SystrayInner>);
+
+struct SystrayInner {
+ display: Option<*mut x11::xlib::Display>,
+ screen: i32,
+ atoms: SystrayAtoms,
+ manager: gtk::Invisible,
+ holding_selection: Option<x11::xlib::Window>,
+ tray_widget: gtk::Box,
+}
+
+struct SystrayAtoms {
+ g_selection_owner: gdk::Atom,
+ selection_owner: x11::xlib::Atom,
+ opcode: x11::xlib::Atom,
+ manager: x11::xlib::Atom,
+}
+
+fn intern_xatom(name: &str, disp: *mut x11::xlib::Display) -> x11::xlib::Atom {
+ gdk::error_trap_push();
+ let atom = unsafe {
+ use std::ffi::CString;
+ let name = CString::new(name).expect("Unable to create string");
+ let atom = x11::xlib::XInternAtom(disp, name.as_ptr(), 0);
+ atom
+ };
+ gdk::error_trap_pop();
+ atom
+}
+
+impl SystrayAtoms {
+ fn new(screen: i32, disp: *mut x11::xlib::Display) -> Self {
+ let selection_owner_name = format!("_NET_SYSTEM_TRAY_S{}", screen);
+ Self {
+ g_selection_owner: gdk::Atom::intern(&selection_owner_name),
+ selection_owner: intern_xatom(&selection_owner_name, disp),
+ opcode: intern_xatom("_NET_SYSTEM_TRAY_OPCODE", disp),
+ manager: intern_xatom("MANAGER", disp),
+ }
+ }
+}
+
+impl Systray {
+ pub fn manage_screen(screen: &gdk::Screen) -> Self {
+ let disp = unsafe { x11::xlib::XOpenDisplay(std::ptr::null()) };
+ let screen_n = screen.get_number();
+ let mut ret = Systray(Box::new(SystrayInner {
+ display: Some(disp),
+ screen: screen_n,
+ atoms: SystrayAtoms::new(screen_n, disp),
+ manager: gtk::Invisible::new_for_screen(&screen),
+ holding_selection: None,
+ tray_widget: gtk::Box::new(gtk::Orientation::Horizontal, 2),
+ }));
+
+ ret.0
+ .manager
+ .add_events(gdk::EventMask::PROPERTY_CHANGE_MASK | gdk::EventMask::STRUCTURE_MASK);
+
+ println!("Manager atom is: {}", ret.0.atoms.selection_owner);
+ println!("Opcode atom is: {}", ret.0.atoms.opcode);
+
+ ret.0.manager.realize();
+
+ ret.0.manage();
+ ret
+ }
+
+ pub fn unmanage(&mut self) {
+ self.0.unmanage();
+ }
+
+ pub fn get_tray_widget(&self) -> &gtk::Widget {
+ self.0.tray_widget.as_ref()
+ }
+
+ pub fn notify_potential_clients(&self) {
+ self.0.notify_clients();
+ }
+}
+
+const SYSTEM_TRAY_REQUEST_DOCK: i64 = 0;
+const SYSTEM_TRAY_BEGIN_MESSAGE: i64 = 1;
+const SYSTEM_TRAY_CANCEL_MESSAGE: i64 = 2;
+
+impl SystrayInner {
+ pub fn manage(&mut self) {
+ if self.holding_selection.is_some() {
+ return;
+ }
+ if gtk::selection_owner_set(
+ Some(&self.manager),
+ &self.atoms.g_selection_owner,
+ gdk_sys::GDK_CURRENT_TIME.try_into().unwrap(),
+ ) {
+ self.holding_selection = Some(unsafe {
+ x11::xlib::XGetSelectionOwner(self.display.unwrap(), self.atoms.selection_owner)
+ });
+ println!("Yep, got selection: {:x?}", self.holding_selection);
+ unsafe {
+ use glib::translate::ToGlibPtr;
+ gdk_sys::gdk_window_add_filter(
+ self.manager
+ .get_window()
+ .expect("Realization failed")
+ .to_glib_none()
+ .0,
+ Some(SystrayInner::window_filter),
+ (self as *mut SystrayInner) as *mut std::ffi::c_void,
+ );
+ }
+ } else {
+ println!("Setting selection owner failed");
+ }
+ }
+
+ pub fn unmanage(&mut self) {
+ if self.holding_selection.is_none() {
+ return;
+ }
+ println!("Unmanaging");
+ unsafe {
+ use glib::translate::ToGlibPtr;
+ gdk_sys::gdk_window_remove_filter(
+ self.manager
+ .get_window()
+ .expect("Realization failed")
+ .to_glib_none()
+ .0,
+ Some(SystrayInner::window_filter),
+ (self as *mut SystrayInner) as *mut std::ffi::c_void,
+ );
+ }
+ gtk::selection_owner_set(
+ None::<&gtk::Window>,
+ &self.atoms.g_selection_owner,
+ gdk_sys::GDK_CURRENT_TIME.try_into().unwrap(),
+ );
+ self.holding_selection = None;
+ }
+
+ unsafe extern "C" fn window_filter(
+ xevent: *mut std::ffi::c_void,
+ _gdkevent: *mut gdk_sys::GdkEvent,
+ pw: *mut std::ffi::c_void,
+ ) -> i32 {
+ let xevent = xevent as *mut x11::xlib::XEvent;
+ let tray = &mut *(pw as *mut SystrayInner);
+ println!("Got X event type {}", (*xevent).type_);
+ if (*xevent).type_ == x11::xlib::ClientMessage {
+ let msg = &((*xevent).client_message);
+ println!("Got a client message, atom: {}", msg.message_type);
+ if msg.message_type == (tray.atoms.opcode as u64) {
+ let opcode = msg.data.as_longs()[1];
+ println!("Opcode {} received", msg.data.as_longs()[1]);
+ if opcode == SYSTEM_TRAY_REQUEST_DOCK {
+ // Dock requested, let's make it so...
+ let xid = msg.data.as_longs()[2];
+ tray.create_dock(xid.try_into().expect("X Window ID too large"));
+ }
+ }
+ } else if (*xevent).type_ == x11::xlib::SelectionClear {
+ // We lost selection so unmanage
+ panic!("Lost selection")
+ }
+ gdk_sys::GDK_FILTER_CONTINUE
+ }
+
+ fn create_dock(&self, xid: gtk::xlib::Window) {
+ let socket = gtk::Socket::new();
+ let client = client::TrayClient::new(&socket);
+ self.tray_widget.pack_start(&client, false, false, 0);
+ client.show_all();
+ socket.add_id(xid);
+ }
+
+ fn notify_clients(&self) {
+ let root = unsafe { x11::xlib::XRootWindow(self.display.unwrap(), self.screen) };
+ let mut msg = x11::xlib::XClientMessageEvent {
+ type_: x11::xlib::ClientMessage,
+ serial: 0,
+ send_event: 0,
+ display: std::ptr::null_mut(),
+ window: root,
+ message_type: self.atoms.manager,
+ format: 32,
+ data: x11::xlib::ClientMessageData::new(),
+ };
+ msg.data.as_longs_mut()[0] = 0; // Current time
+ msg.data.as_longs_mut()[1] = self.atoms.selection_owner as i64; // atom
+ msg.data.as_longs_mut()[2] = self.holding_selection.unwrap() as i64; // tray
+ unsafe {
+ let mut event: x11::xlib::XEvent = msg.into();
+ println!("Sending event: {:?}", event);
+ x11::xlib::XSendEvent(
+ self.display.unwrap(),
+ root,
+ 0,
+ x11::xlib::StructureNotifyMask,
+ &mut event as *mut x11::xlib::XEvent,
+ );
+ x11::xlib::XFlush(self.display.unwrap());
+ }
+ }
+}
+
+impl Drop for SystrayInner {
+ fn drop(&mut self) {
+ if let Some(disp) = self.display.take() {
+ gdk::error_trap_push();
+ unsafe {
+ x11::xlib::XCloseDisplay(disp);
+ }
+ gdk::error_trap_pop();
+ }
+ }
+}
+
+impl Drop for Systray {
+ fn drop(&mut self) {
+ self.0.unmanage();
+ }
+}
+
+mod client {
+ use std::cell::RefCell;
+
+ use gdk::prelude::*;
+ use gio::prelude::*;
+ use gtk::prelude::*;
+
+ use glib::subclass;
+ use glib::subclass::prelude::*;
+ use glib::translate::*;
+ use glib::{glib_object_impl, glib_object_subclass, glib_object_wrapper, glib_wrapper};
+ use gtk::subclass::prelude::*;
+
+ // Properties for tray clients
+ static PROPERTIES: [glib::subclass::Property; 1] =
+ [glib::subclass::Property("socket", |name| {
+ glib::ParamSpec::object(
+ name,
+ "Socket",
+ "Socket to be embedded",
+ gtk::Socket::static_type(),
+ glib::ParamFlags::CONSTRUCT_ONLY
+ | glib::ParamFlags::READABLE
+ | glib::ParamFlags::WRITABLE,
+ )
+ })];
+
+ #[derive(Debug)]
+ pub struct TrayClientPrivate {
+ socket: RefCell<Option<gtk::Socket>>,
+ has_alpha: bool,
+ parent_relative: bool,
+ }
+
+ impl ObjectSubclass for TrayClientPrivate {
+ const NAME: &'static str = "TrayClientPrivate";
+ type ParentType = gtk::Bin;
+ type Instance = subclass::simple::InstanceStruct<Self>;
+ type Class = subclass::simple::ClassStruct<Self>;
+
+ glib_object_subclass!();
+
+ fn new() -> Self {
+ Self {
+ socket: RefCell::new(None),
+ has_alpha: false,
+ parent_relative: false,
+ }
+ }
+
+ fn class_init(_klass: &mut Self::Class) {
+ _klass.install_properties(&PROPERTIES);
+ }
+ }
+
+ impl ObjectImpl for TrayClientPrivate {
+ glib_object_impl!();
+
+ fn set_property(&self, _obj: &glib::Object, id: usize, value: &glib::Value) {
+ let prop = &PROPERTIES[id];
+ match *prop {
+ subclass::Property("socket", ..) => {
+ let socket = value
+ .get()
+ .expect("Conformity expected in Object::set_property");
+ println!("Replacing socket with {:?}", socket);
+ self.socket.replace(socket);
+ }
+ _ => unimplemented!(),
+ }
+ }
+
+ fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
+ let prop = &PROPERTIES[id];
+ match *prop {
+ subclass::Property("socket", ..) => Ok(self.socket.borrow().to_value()),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn constructed(&self, obj: &glib::Object) {
+ self.parent_constructed(obj);
+ let self_ = obj.downcast_ref::<TrayClient>().expect("Not a TrayClient");
+ self_.add(self.socket.borrow().as_ref().unwrap());
+ }
+ }
+
+ impl WidgetImpl for TrayClientPrivate {
+ fn get_preferred_width(&self, _widget: &gtk::Widget) -> (i32, i32) {
+ (22, 22)
+ }
+ fn get_preferred_height(&self, _widget: &gtk::Widget) -> (i32, i32) {
+ (22, 22)
+ }
+ fn size_allocate(&self, widget: &gtk::Widget, allocation: &gtk::Allocation) {
+ let old_alloc = widget.get_allocation();
+ let moved = (old_alloc.x != allocation.x) || (old_alloc.y != allocation.y);
+ let resized =
+ (old_alloc.width != allocation.width) || (old_alloc.height != allocation.height);
+ println!("Dealing with size_allocate of {:?}", allocation);
+ println!("Old allocation was {:?}", old_alloc);
+ println!("moved={:?} resized={:?}", moved, resized);
+ if (moved || resized) && widget.get_mapped() {
+ let parent = widget
+ .get_window()
+ .expect("No window?")
+ .get_parent()
+ .expect("No parent?");
+ parent.invalidate_rect(Some(allocation), false);
+ }
+ self.parent_size_allocate(widget, allocation);
+ if (moved || resized) && widget.get_mapped() {
+ // TODO: Send an expose event to the mapped client somehow?
+ }
+ }
+
+ fn draw(&self, widget: &gtk::Widget, context: &cairo::Context) -> glib::signal::Inhibit {
+ // TODO: Implement
+ self.parent_draw(widget, context);
+ context.set_source_rgba(0.0, 0.0, 0.0, 0.0);
+ context.set_operator(cairo::Operator::Source);
+ context.paint();
+ glib::signal::Inhibit(false)
+ }
+ }
+ impl ContainerImpl for TrayClientPrivate {}
+ impl BinImpl for TrayClientPrivate {}
+
+ glib_wrapper! {
+ pub struct TrayClient(
+ Object<subclass::simple::InstanceStruct<TrayClientPrivate>,
+ subclass::simple::ClassStruct<TrayClientPrivate>,
+ TrayClientClass>
+ )
+ @extends gtk::Widget, gtk::Container, gtk::Bin;
+
+ match fn {
+ get_type => || TrayClientPrivate::get_type().to_glib(),
+ }
+ }
+
+ impl TrayClient {
+ pub fn new(socket: &gtk::Socket) -> Self {
+ glib::Object::new(Self::static_type(), &[("socket", socket)])
+ .expect("Failed to create TrayClient")
+ .downcast::<TrayClient>()
+ .expect("TrayClient is not of expected type")
+ }
+ }
+}