From 537539f43dc9421cf403925e39a84261dd358290 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 11 Jan 2022 00:05:57 -0500 Subject: [PATCH] basic plugin! --- Cargo.toml | 8 +- examples/dock/dock_item/imp.rs | 2 +- examples/dock/dock_item/mod.rs | 33 ++--- examples/dock/dock_list/mod.rs | 165 ++++++++++++++++----- examples/dock/dock_object/.#mod.rs | 1 + examples/dock/dock_object/imp.rs | 3 +- examples/dock/dock_object/mod.rs | 62 +++++++- examples/dock/dock_popover/mod.rs | 228 +++++++++++++---------------- examples/dock/main.rs | 34 ++--- examples/dock/plugin.rs | 10 ++ examples/dock/style.css | 4 + examples/dock/utils.rs | 2 +- examples/dock/window/mod.rs | 4 +- 13 files changed, 332 insertions(+), 224 deletions(-) create mode 120000 examples/dock/dock_object/.#mod.rs create mode 100644 examples/dock/plugin.rs diff --git a/Cargo.toml b/Cargo.toml index 068a66f0..ff44855e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ wayland-protocols = { version = "0.28", features = [ "client", "unstable_protoco x11 = { version = "2", features = ["xlib"] } # examples +gtk4 = { version ="0.3.1", features = ["v4_4"] } +glib-sys = "0.14.0" pop-launcher-service = { git = "https://github.com/wash2/launcher.git" } pop-launcher = { git = "https://github.com/wash2/launcher.git" } serde = "1.0.130" @@ -30,10 +32,8 @@ x11rb = "0.9.0" zbus = "2.0.0" zvariant = "3.0.0" zvariant_derive = "3.0.0" - -[dependencies.gtk4] -version = "0.3" -features = ["v4_4"] +libloading = "0.7.2" +gtk4-sys = "0.3.1" [profile.release] incremental = true diff --git a/examples/dock/dock_item/imp.rs b/examples/dock/dock_item/imp.rs index 12b86ae9..7649c747 100644 --- a/examples/dock/dock_item/imp.rs +++ b/examples/dock/dock_item/imp.rs @@ -10,7 +10,7 @@ use crate::dock_popover::DockPopover; #[derive(Debug, Default)] pub struct DockItem { - pub image: Rc>, + pub image: Rc>>, pub dots: Rc>, pub item_box: Rc>, pub popover: Rc>, diff --git a/examples/dock/dock_item/mod.rs b/examples/dock/dock_item/mod.rs index b7e83aba..9795a17c 100644 --- a/examples/dock/dock_item/mod.rs +++ b/examples/dock/dock_item/mod.rs @@ -1,6 +1,5 @@ use cascade::cascade; use gio::DesktopAppInfo; -use gio::Icon; use gtk4::glib; use gtk4::prelude::*; use gtk4::subclass::prelude::*; @@ -13,6 +12,7 @@ use gtk4::Popover; use crate::dock_object::DockObject; use crate::dock_popover::DockPopover; +use crate::plugin; use crate::utils::BoxedWindowList; mod imp; @@ -82,7 +82,7 @@ impl DockItem { .unwrap(); let imp = imp::DockItem::from_instance(&self_); - imp.image.replace(image); + imp.image.replace(Some(image)); imp.dots.replace(dots); imp.item_box.replace(item_box); imp.popover.replace(popover); @@ -92,24 +92,21 @@ impl DockItem { } // refactor to emit event for removing the item? - pub fn set_app_info(&self, dock_object: &DockObject) { + pub fn set_dock_object(&self, dock_object: &DockObject) { let self_ = imp::DockItem::from_instance(self); - if let Ok(app_info_value) = dock_object.property("appinfo") { - if let Ok(Some(app_info)) = app_info_value.get::>() { - self_ - .image - .borrow() - .set_tooltip_text(Some(&app_info.name())); - - let icon = app_info.icon().unwrap_or( - Icon::for_string("image-missing").expect("Failed to set default icon"), - ); - - self_.image.borrow().set_from_gicon(&icon); - } - } else { - println!("initializing dock item failed..."); + let image = cascade! { + dock_object.get_image(); + ..set_hexpand(true); + ..set_halign(Align::Center); + ..set_pixel_size(64); + ..set_tooltip_text(dock_object.get_name().as_deref()); + }; + let old_image = self_.image.replace(None); + if let Some(old_image) = old_image { + self_.item_box.borrow().remove(&old_image); } + self_.item_box.borrow().prepend(&image); + let old_image = self_.image.replace(Some(image)); if let Ok(active_value) = dock_object.property("active") { if let Ok(active) = active_value.get::() { let dots = self_.dots.borrow(); diff --git a/examples/dock/dock_list/mod.rs b/examples/dock/dock_list/mod.rs index b1072fbc..8332cd69 100644 --- a/examples/dock/dock_list/mod.rs +++ b/examples/dock/dock_list/mod.rs @@ -1,3 +1,12 @@ +use crate::dock_item::DockItem; +use crate::dock_object::DockObject; +use crate::plugin; +use crate::utils::data_path; +use crate::BoxedWindowList; +use crate::Event; +use crate::Item; +use crate::PLUGINS; +use crate::TX; use cascade::cascade; use gdk4::ContentProvider; use gdk4::Display; @@ -17,17 +26,12 @@ use gtk4::SignalListItemFactory; use gtk4::Window; use gtk4::{gio, glib}; use gtk4::{DragSource, GestureClick}; +use std::ffi::CStr; use std::fs::File; +use std::io::BufReader; +use std::io::Read; use std::path::Path; -use crate::dock_item::DockItem; -use crate::dock_object::DockObject; -use crate::utils::data_path; -use crate::BoxedWindowList; -use crate::Event; -use crate::Item; -use crate::TX; - mod imp; glib::wrapper! { @@ -97,41 +101,122 @@ impl DockList { let model = self.model(); model.splice(model.n_items(), 0, &dock_objects); - return; } - } - eprintln!("Error loading saved apps!"); - let model = &self.model(); - xdg::BaseDirectories::new() - .expect("could not access XDG Base directory") - .get_data_dirs() - .iter_mut() - .for_each(|xdg_data_path| { - let defaults = ["Firefox Web Browser", "Files", "Terminal", "Pop!_Shop"]; - xdg_data_path.push("applications"); - // dbg!(&xdg_data_path); - if let Ok(dir_iter) = std::fs::read_dir(xdg_data_path) { - dir_iter.for_each(|dir_entry| { - if let Ok(dir_entry) = dir_entry { - if let Some(path) = dir_entry.path().file_name() { - if let Some(path) = path.to_str() { - if let Some(app_info) = gio::DesktopAppInfo::new(path) { - if app_info.should_show() - && defaults.contains(&app_info.name().as_str()) - { - model.append(&DockObject::new(app_info)); + } else { + eprintln!("Error loading saved apps!"); + let model = &self.model(); + xdg::BaseDirectories::new() + .expect("could not access XDG Base directory") + .get_data_dirs() + .iter_mut() + .for_each(|xdg_data_path| { + let defaults = ["Firefox Web Browser", "Files", "Terminal", "Pop!_Shop"]; + xdg_data_path.push("applications"); + // dbg!(&xdg_data_path); + if let Ok(dir_iter) = std::fs::read_dir(xdg_data_path) { + dir_iter.for_each(|dir_entry| { + if let Ok(dir_entry) = dir_entry { + if let Some(path) = dir_entry.path().file_name() { + if let Some(path) = path.to_str() { + if let Some(app_info) = gio::DesktopAppInfo::new(path) { + if app_info.should_show() + && defaults.contains(&app_info.name().as_str()) + { + model.append(&DockObject::new(app_info)); + } else { + // println!("Ignoring {}", path); + } } else { - // println!("Ignoring {}", path); + // println!("error loading {}", path); } - } else { - // println!("error loading {}", path); } } } - } - }) - } - }); + }) + } + }); + } + // TODO load saved plugins here... for now, load the hardcoded example. + // TODO unload plugin library before the dynamic library is changed, otherwise, it will crash after segfault + // TODO unload plugin on removal from model + // TODO dnd for plugin? I think they should either be at the start or end of the dock and not draggable + // TODO call plugin click handler on click or if it is not provided by the library, open the popover menu instead + let mut path_dir = glib::user_data_dir(); + path_dir.push(crate::ID); + std::fs::create_dir_all(&path_dir).expect("Could not create directory."); + path_dir.push("plugins"); + std::fs::create_dir_all(&path_dir).expect("Could not create directory."); + let mut path = path_dir.clone(); + path.push("dock_plugin_uwu.so"); + let mut path_css = path_dir.clone(); + path_css.push("dock_plugin_uwu.css"); + let path = path + .as_os_str() + .to_str() + .expect("plugin path needs to be a valid string"); + let provider = gtk4::CssProvider::new(); + if let Ok(f) = File::open(path_css) { + let mut reader = BufReader::new(f); + let mut buffer = Vec::new(); + + if reader.read_to_end(&mut buffer).is_ok() { + provider.load_from_data(&buffer); + // Add the provider to the default screen + gtk4::StyleContext::add_provider_for_display( + &gdk4::Display::default().expect("Error initializing GTK CSS provider."), + &provider, + gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION, + ); + } else { + eprintln!("loading plugin css failed"); + } + } else { + eprintln!("loading plugin css failed"); + } + + let (popover_menu, image, name, lib) = unsafe { + let lib = libloading::Library::new(path).unwrap(); + // store library until unloading the plugin + let image_func: libloading::Symbol *mut gtk4_sys::GtkWidget> = + lib.get(b"dock_plugin_image").unwrap(); + let popover_func: libloading::Symbol< + unsafe extern "C" fn() -> *mut gtk4_sys::GtkWidget, + > = lib.get(b"dock_plugin_popover_menu").unwrap(); + let name_func: libloading::Symbol< + unsafe extern "C" fn() -> *const std::os::raw::c_char, + > = lib.get(b"dock_plugin_name").unwrap(); + // click handler is optional + + (popover_func(), image_func(), name_func(), lib) + }; + if let Ok(ref mut mutex) = PLUGINS.try_lock() { + mutex.insert(String::from(path), lib); + } + let name = if !name.is_null() { + unsafe { String::from(CStr::from_ptr(name).to_str().unwrap_or_default()) } + } else { + String::new() + }; + let image = if !image.is_null() { + unsafe { gtk4::glib::translate::from_glib_none::<_, gtk4::Widget>(image).unsafe_cast() } + } else { + gtk4::Image::new() + }; + let popover_menu = if !popover_menu.is_null() { + unsafe { + gtk4::glib::translate::from_glib_none::<_, gtk4::Widget>(popover_menu).unsafe_cast() + } + } else { + gtk4::Box::new(Orientation::Vertical, 4) + }; + let boxed_plugin = plugin::BoxedDockPlugin { + path: String::from(path), + name, + image, + popover_menu, + }; + let model = self.model(); + model.append(&DockObject::from_plugin(boxed_plugin).upcast::()); } fn store_data(model: &gio::ListStore) { @@ -160,6 +245,7 @@ impl DockList { let file = File::create(data_path()).expect("Could not create json file."); serde_json::to_writer_pretty(file, &backup_data) .expect("Could not write data to json file"); + // TODO save plugins here for now examples are hardcoded and don't need to be saved } fn layout(&self) { @@ -420,7 +506,6 @@ impl DockList { if type_ == DockListType::Saved { if let Some(old_handle) = drag_end.replace(Some(self_.connect_drag_end( glib::clone!(@weak model => move |_self, _drag, _delete_data| { - dbg!(_delete_data); if _delete_data { model.remove(index); glib::MainContext::default().spawn_local(async move { @@ -526,7 +611,7 @@ impl DockList { }), ); factory.connect_bind(move |_, list_item| { - let application_object = list_item + let dock_object = list_item .item() .expect("The item has to exist.") .downcast::() @@ -536,7 +621,7 @@ impl DockList { .expect("The list item child needs to exist.") .downcast::() .expect("The list item type needs to be `DockItem`"); - dock_item.set_app_info(&application_object); + dock_item.set_dock_object(&dock_object); }); // Set the factory of the list view imp.list_view.get().unwrap().set_factory(Some(&factory)); diff --git a/examples/dock/dock_object/.#mod.rs b/examples/dock/dock_object/.#mod.rs new file mode 120000 index 00000000..b83bca2f --- /dev/null +++ b/examples/dock/dock_object/.#mod.rs @@ -0,0 +1 @@ +ashleywulber@pop-os.190471:1641501268 \ No newline at end of file diff --git a/examples/dock/dock_object/imp.rs b/examples/dock/dock_object/imp.rs index cfa6e56e..b2f964f6 100644 --- a/examples/dock/dock_object/imp.rs +++ b/examples/dock/dock_object/imp.rs @@ -8,6 +8,7 @@ use gtk4::prelude::*; use gtk4::subclass::prelude::*; use once_cell::sync::Lazy; +use crate::plugin::BoxedDockPlugin; use crate::utils::BoxedWindowList; // Object holding the state @@ -15,6 +16,7 @@ use crate::utils::BoxedWindowList; pub struct DockObject { pub(super) appinfo: RefCell>, pub(super) active: RefCell, + pub(super) plugin: RefCell>, pub(super) saved: Cell, pub(super) popover: Cell, } @@ -93,7 +95,6 @@ impl ObjectImpl for DockObject { self.popover .replace(value.get().expect("Value needs to be a boolean")); } - _ => unimplemented!(), } } diff --git a/examples/dock/dock_object/mod.rs b/examples/dock/dock_object/mod.rs index 3a3bf309..75fb9518 100644 --- a/examples/dock/dock_object/mod.rs +++ b/examples/dock/dock_object/mod.rs @@ -1,13 +1,13 @@ use std::path::Path; -use gdk4::glib::Object; -use gdk4::prelude::FileExt; -use gdk4::subclass::prelude::ObjectSubclassExt; -use gio::DesktopAppInfo; -use gtk4::glib; -use gtk4::prelude::AppInfoExt; - +use crate::plugin::{self, BoxedDockPlugin}; use crate::utils::BoxedWindowList; +use gdk4::glib::Object; +use gdk4::subclass::prelude::ObjectSubclassExt; +use gio::{DesktopAppInfo, Icon}; +use gtk4::prelude::*; +use gtk4::{glib, Image}; +use std::cell::Ref; mod imp; @@ -37,17 +37,63 @@ impl DockObject { None } - pub fn get_name(&self) -> Option { + pub fn from_plugin(plugin: plugin::BoxedDockPlugin) -> Self { + let self_ = Object::new(&[("saved", &true)]).expect("Failed to create `DockObject`."); + let imp = imp::DockObject::from_instance(&self_); + imp.plugin.replace(Some(plugin)); + self_ + } + + pub fn get_path(&self) -> Option { let imp = imp::DockObject::from_instance(&self); if let Some(app_info) = imp.appinfo.borrow().as_ref() { app_info .filename() .map(|name| name.to_string_lossy().into()) + } else if let Some(plugin) = imp.plugin.borrow().as_ref() { + Some(plugin.path.clone()) } else { None } } + pub fn get_name(&self) -> Option { + let imp = imp::DockObject::from_instance(&self); + if let Some(app_info) = imp.appinfo.borrow().as_ref() { + Some(app_info.name().to_string()) + } else if let Some(plugin) = imp.plugin.borrow().as_ref() { + Some(plugin.name.clone()) + } else { + None + } + } + + pub fn get_popover_menu(&self) -> Option { + let imp = imp::DockObject::from_instance(&self); + if let Some(plugin) = imp.plugin.borrow().as_ref() { + Some(plugin.popover_menu.clone()) + } else { + None + } + } + + pub fn get_image(&self) -> gtk4::Image { + let imp = imp::DockObject::from_instance(&self); + if let Some(app_info) = imp.appinfo.borrow().as_ref() { + let image = Image::new(); + let icon = app_info + .icon() + .unwrap_or(Icon::for_string("image-missing").expect("Failed to set default icon")); + image.set_from_gicon(&icon); + image + } else if let Some(plugin) = imp.plugin.borrow().as_ref() { + plugin.image.clone() + } else { + println!("failed to load image"); + Image::new() + } + } + pub fn set_saved(&self, is_saved: bool) { let imp = imp::DockObject::from_instance(&self); imp.saved.replace(is_saved); diff --git a/examples/dock/dock_popover/mod.rs b/examples/dock/dock_popover/mod.rs index 8e5be5dc..32334f0e 100644 --- a/examples/dock/dock_popover/mod.rs +++ b/examples/dock/dock_popover/mod.rs @@ -4,12 +4,11 @@ use gio::DesktopAppInfo; use gtk4::subclass::prelude::*; use gtk4::{gio, glib}; use gtk4::{prelude::*, Label}; -use gtk4::{Box, Button, Image, ListBox, Orientation, Revealer, Window}; +use gtk4::{Box, Button, Image, ListBox, Orientation, Window}; -use crate::dock_object::{self, DockObject}; +use crate::dock_object::DockObject; use crate::utils::BoxedWindowList; use crate::Event; -use crate::Item; use crate::TX; mod imp; @@ -50,137 +49,108 @@ impl DockPopover { let dock_object = imp.dock_object.borrow(); let menu_handle = imp.menu_handle.borrow(); if let Some(dock_object) = dock_object.as_ref() { - cascade! { - &self; - ..set_spacing(4); - ..set_orientation(Orientation::Vertical); - ..set_hexpand(true); - }; + if let Some(menu) = dock_object.get_popover_menu() { + // TODO investigate (dock:255244): Gtk-CRITICAL **: 19:12:38.668: gtk_at_context_set_accessible_role: assertion '!self->realized' failed + // appears after setting the menu handle a second time + menu_handle.append(&menu); + } else { + cascade! { + &self; + ..set_spacing(4); + ..set_orientation(Orientation::Vertical); + ..set_hexpand(true); + }; - let all_windows_item_container = cascade! { - Box::new(Orientation::Vertical, 4); - }; - menu_handle.append(&all_windows_item_container); - - // let all_windows_item_header = cascade! { - // Button::new(); - // ..set_hexpand(true); - // }; - // all_windows_item_container.append(&all_windows_item_header); - - // let all_windows_item_header_box = cascade! { - // Box::new(Orientation::Horizontal, 4); - // ..set_hexpand(true); - // }; - // all_windows_item_header.set_child(Some(&all_windows_item_header_box)); - - // let all_windows_item_header_title = cascade! { - // Label::new(Some("All Windows")); - // ..add_css_class("header-5"); - // ..set_halign(gtk4::Align::Start); - // }; - // all_windows_item_header_box.append(&all_windows_item_header_title); - - // let all_windows_item_header_icon = cascade! { - // Image::from_icon_name(Some("go-down")); - // ..set_halign(gtk4::Align::End); - // ..set_hexpand(true); - // ..set_pixel_size(16); - // }; - // all_windows_item_header_box.append(&all_windows_item_header_icon); - // imp.all_windows_item_header.replace(all_windows_item_header); - - if let Ok(window_list) = dock_object - .property("active") - .unwrap() - .get::() - { - if window_list.0.len() == 0 { - all_windows_item_container.hide(); - } else { - // let window_list_revealer = cascade! { - // Revealer::new(); - // ..set_reveal_child(false); - // ..set_transition_type(gtk4::RevealerTransitionType::SlideDown); - // }; - // all_windows_item_container.append(&window_list_revealer); - let window_listbox = cascade! { - ListBox::new(); - ..set_activate_on_single_click(true); - }; - all_windows_item_container.append(&window_listbox); - for w in window_list.0 { - let window_box = cascade! { - Box::new(Orientation::Vertical, 4); - }; - window_listbox.append(&window_box); - - let window_title = cascade! { - Label::new(Some(w.name.as_str())); - ..set_margin_start(4); - ..set_margin_end(4); - ..set_margin_top(4); - ..set_margin_bottom(4); - ..set_wrap(true); - ..set_max_width_chars(20); - ..set_ellipsize(EllipsizeMode::End); - ..add_css_class("title-4"); - ..add_css_class("window_title"); - }; - - let window_image = cascade! { - //TODO fill with image of window - Image::from_pixbuf(None); - }; - window_box.append(&window_image); - window_box.append(&window_title); - } - // imp.all_windows_item_revealer.replace(window_list_revealer); - imp.window_list.replace(window_listbox); - } - } - - let launch_item_container = cascade! { - Box::new(Orientation::Vertical, 4); - ..set_hexpand(true); - }; - menu_handle.append(&launch_item_container); - - let launch_new_item = cascade! { - Button::with_label("New Window"); - }; - launch_item_container.append(&launch_new_item); - imp.launch_new_item.replace(launch_new_item); - - let favorite_item = cascade! { - Button::with_label(if dock_object.property("saved").unwrap().get::().unwrap() {"Remove from Favorites"} else {"Add to Favorites"}); - }; - menu_handle.append(&favorite_item); - imp.favorite_item.replace(favorite_item); - - if let Ok(window_list) = dock_object - .property("active") - .unwrap() - .get::() - { - if window_list.0.len() > 1 { - let quit_all_item = cascade! { - Button::with_label(format!("Quit {} Windows", window_list.0.len()).as_str()); - }; - menu_handle.append(&quit_all_item); - imp.quit_all_item.replace(quit_all_item); - } else { - let quit_all_item = cascade! { - Button::with_label("Quit"); - }; - menu_handle.append(&quit_all_item); + let all_windows_item_container = cascade! { + Box::new(Orientation::Vertical, 4); + }; + menu_handle.append(&all_windows_item_container); + if let Ok(window_list) = dock_object + .property("active") + .unwrap() + .get::() + { if window_list.0.len() == 0 { - quit_all_item.hide(); + all_windows_item_container.hide(); + } else { + let window_listbox = cascade! { + ListBox::new(); + ..set_activate_on_single_click(true); + }; + all_windows_item_container.append(&window_listbox); + for w in window_list.0 { + let window_box = cascade! { + Box::new(Orientation::Vertical, 4); + }; + window_listbox.append(&window_box); + + let window_title = cascade! { + Label::new(Some(w.name.as_str())); + ..set_margin_start(4); + ..set_margin_end(4); + ..set_margin_top(4); + ..set_margin_bottom(4); + ..set_wrap(true); + ..set_max_width_chars(20); + ..set_ellipsize(EllipsizeMode::End); + ..add_css_class("title-4"); + ..add_css_class("window_title"); + }; + + let window_image = cascade! { + //TODO fill with image of window + Image::from_pixbuf(None); + }; + window_box.append(&window_image); + window_box.append(&window_title); + } + // imp.all_windows_item_revealer.replace(window_list_revealer); + imp.window_list.replace(window_listbox); } - imp.quit_all_item.replace(quit_all_item); } + + let launch_item_container = cascade! { + Box::new(Orientation::Vertical, 4); + ..set_hexpand(true); + }; + menu_handle.append(&launch_item_container); + + let launch_new_item = cascade! { + Button::with_label("New Window"); + }; + launch_item_container.append(&launch_new_item); + imp.launch_new_item.replace(launch_new_item); + + let favorite_item = cascade! { + Button::with_label(if dock_object.property("saved").unwrap().get::().unwrap() {"Remove from Favorites"} else {"Add to Favorites"}); + }; + menu_handle.append(&favorite_item); + imp.favorite_item.replace(favorite_item); + + if let Ok(window_list) = dock_object + .property("active") + .unwrap() + .get::() + { + if window_list.0.len() > 1 { + let quit_all_item = cascade! { + Button::with_label(format!("Quit {} Windows", window_list.0.len()).as_str()); + }; + menu_handle.append(&quit_all_item); + imp.quit_all_item.replace(quit_all_item); + } else { + let quit_all_item = cascade! { + Button::with_label("Quit"); + }; + menu_handle.append(&quit_all_item); + if window_list.0.len() == 0 { + quit_all_item.hide(); + } + imp.quit_all_item.replace(quit_all_item); + } + } + self.setup_handlers(); } - self.setup_handlers(); } } diff --git a/examples/dock/main.rs b/examples/dock/main.rs index 8b51833f..a87990df 100644 --- a/examples/dock/main.rs +++ b/examples/dock/main.rs @@ -1,6 +1,9 @@ use std::collections::BTreeMap; +use std::sync::Mutex; use std::time::Duration; +use crate::dock_list::DockListType; +use crate::utils::{block_on, BoxedWindowList}; use gdk4::Display; use gio::DesktopAppInfo; use gtk4::gio; @@ -9,16 +12,14 @@ use gtk4::prelude::*; use gtk4::Application; use gtk4::CssProvider; use gtk4::StyleContext; -use once_cell::sync::OnceCell; +use once_cell::sync::{Lazy, OnceCell}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use tokio::sync::mpsc; use x11rb::rust_connection::RustConnection; use zbus::Connection; use zvariant_derive::Type; -use crate::dock_list::DockListType; -use crate::utils::{block_on, thread_context, BoxedWindowList}; - use self::dock_object::DockObject; use self::window::Window; @@ -26,16 +27,18 @@ mod dock_item; mod dock_list; mod dock_object; mod dock_popover; +mod plugin; mod utils; mod window; +const ID: &str = "com.cosmic.dock"; const DEST: &str = "com.System76.PopShell"; const PATH: &str = "/com/System76/PopShell"; -const NUM_LAUNCHER_ITEMS: u8 = 10; static TX: OnceCell> = OnceCell::new(); static X11_CONN: OnceCell = OnceCell::new(); -static PLUGIN_POOL: OnceCell = OnceCell::new(); +static PLUGINS: Lazy>> = + Lazy::new(|| Mutex::new(HashMap::new())); pub enum Event { WindowList(Vec), @@ -97,7 +100,7 @@ fn spawn_zbus(tx: mpsc::Sender) -> Connection { connection } -fn _setup_shortcuts(app: &Application) {} +fn _setup_shortcuts(_app: &Application) {} fn load_css() { // Load the css file and add it to the provider @@ -114,22 +117,15 @@ fn load_css() { fn main() { assert!(utils::BoxedWindowList::static_type().is_valid()); - let app = gtk4::Application::builder() - .application_id("com.system76.dock") - .build(); + assert!(plugin::BoxedDockPlugin::static_type().is_valid()); + let app = gtk4::Application::builder().application_id(ID).build(); - app.connect_startup(|app| { + app.connect_startup(|_app| { // setup_shortcuts(app); load_css() }); app.connect_activate(move |app| { - let pool = glib::ThreadPool::new_shared(None).expect("Failed to spawn thread pool"); - if PLUGIN_POOL.set(pool).is_err() { - eprintln!("failed to set global thread pool. Exiting"); - std::process::exit(1); - }; - let (tx, mut rx) = mpsc::channel(100); let zbus_conn = spawn_zbus(tx.clone()); @@ -174,7 +170,7 @@ fn main() { let mut index: Option = None; while let Some(item) = active_app_model.item(cur) { if let Ok(cur_dock_object) = item.downcast::() { - if cur_dock_object.get_name() == Some(name.clone()) { + if cur_dock_object.get_path() == Some(name.clone()) { cur_dock_object.set_saved(true); index = Some(cur); } @@ -191,7 +187,7 @@ fn main() { let mut index: Option = None; while let Some(item) = saved_app_model.item(cur) { if let Ok(cur_dock_object) = item.downcast::() { - if cur_dock_object.get_name() == Some(name.clone()) { + if cur_dock_object.get_path() == Some(name.clone()) { cur_dock_object.set_saved(false); index = Some(cur); } diff --git a/examples/dock/plugin.rs b/examples/dock/plugin.rs new file mode 100644 index 00000000..aa0e2f83 --- /dev/null +++ b/examples/dock/plugin.rs @@ -0,0 +1,10 @@ +use gtk4::glib; + +#[derive(Clone, Debug, Default, gtk4::glib::GBoxed)] +#[gboxed(type_name = "BoxedDockPlugin")] +pub struct BoxedDockPlugin { + pub path: String, + pub name: String, + pub image: gtk4::Image, + pub popover_menu: gtk4::Box, +} diff --git a/examples/dock/style.css b/examples/dock/style.css index 5aff07f5..60afd214 100644 --- a/examples/dock/style.css +++ b/examples/dock/style.css @@ -60,6 +60,10 @@ box.dock { background: #333333CC; } +image.dock { + border-radius: 12px; +} + window.root_window { background: transparent; } diff --git a/examples/dock/utils.rs b/examples/dock/utils.rs index 21269e5c..108a7a7a 100644 --- a/examples/dock/utils.rs +++ b/examples/dock/utils.rs @@ -16,7 +16,7 @@ pub struct BoxedDockObject(pub Option); pub fn data_path() -> PathBuf { let mut path = glib::user_data_dir(); - path.push("com.cosmic.dock"); + path.push(crate::ID); std::fs::create_dir_all(&path).expect("Could not create directory."); path.push("data.json"); path diff --git a/examples/dock/window/mod.rs b/examples/dock/window/mod.rs index e142e760..eba80a04 100644 --- a/examples/dock/window/mod.rs +++ b/examples/dock/window/mod.rs @@ -1,11 +1,9 @@ use cascade::cascade; use gdk4::Rectangle; -use gdk4::Snapshot; use gdk4_x11::X11Display; use gdk4_x11::X11Surface; use glib::Object; use glib::Type; -use gsk::BlurNode; use gtk4::prelude::*; use gtk4::subclass::prelude::*; use gtk4::Align; @@ -17,7 +15,7 @@ use gtk4::Orientation; use gtk4::Revealer; use gtk4::RevealerTransitionType; use gtk4::Separator; -use gtk4::{gio, glib, gsk}; +use gtk4::{gio, glib}; use x11rb::connection::Connection; use x11rb::protocol::xproto; use x11rb::protocol::xproto::ConnectionExt;