diff --git a/gtk4/Cargo.toml b/gtk4/Cargo.toml deleted file mode 100644 index 1ed467b3..00000000 --- a/gtk4/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "libcosmic-gtk4" -version = "0.1.0" -edition = "2021" - -[dependencies] -cascade = "1.0.0" -derivative = { version = "2", optional = true } -gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_6"] } -adw = { git = "https://gitlab.gnome.org/World/Rust/libadwaita-rs", package = "libadwaita"} -adw-user-colors-lib = { git = "https://github.com/pop-os/user-color-editor" } -gdk4-wayland = { git = "https://github.com/gtk-rs/gtk4-rs", features = ["wayland_crate"], optional = true } -gdk4-x11 = { git = "https://github.com/gtk-rs/gtk4-rs", features = ["xlib"], optional = true } -x11 = { version = "2.19.1", features = ["xlib"], optional = true } -gobject-sys = { git = "https://github.com/gtk-rs/gtk-rs-core", optional = true } -wayland-client = { version = "0.29.4", optional = true } -wayland-protocols = { version = "0.29.4", features = ["client", "unstable_protocols"], optional = true } -once_cell = "1.13.0" -libcosmic-widgets = { path = "widgets", optional = true } -xdg = "2.4.1" - -[features] -default = ["widgets"] -layer-shell = ["derivative", "gdk4-wayland", "wayland-client", "wayland-protocols", "gobject-sys"] -x = ["x11", "gdk4-x11"] -widgets = ["libcosmic-widgets"] diff --git a/gtk4/src/deref_cell.rs b/gtk4/src/deref_cell.rs deleted file mode 100644 index d29cf10e..00000000 --- a/gtk4/src/deref_cell.rs +++ /dev/null @@ -1,33 +0,0 @@ -#![allow(dead_code)] - -use once_cell::unsync::OnceCell; - -/// Wrapper around `OnceCell` implementing `Deref`, and thus also panicking -/// when not set (or set twice). -/// -/// To be used in place of `gtk::TemplateChild`, but without xml. -pub struct DerefCell(OnceCell); - -impl DerefCell { - #[track_caller] - pub fn set(&self, value: T) { - if self.0.set(value).is_err() { - panic!("Initialized twice"); - } - } -} - -impl Default for DerefCell { - fn default() -> Self { - Self(OnceCell::default()) - } -} - -impl std::ops::Deref for DerefCell { - type Target = T; - - #[track_caller] - fn deref(&self) -> &T { - self.0.get().unwrap() - } -} diff --git a/gtk4/src/lib.rs b/gtk4/src/lib.rs deleted file mode 100644 index 51598de8..00000000 --- a/gtk4/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -mod deref_cell; -#[cfg(feature = "layer-shell")] -pub mod wayland; -#[cfg(feature = "layer-shell")] -mod wayland_custom_surface; -#[cfg(feature = "x")] -pub mod x; - -#[cfg(feature = "widgets")] -pub use libcosmic_widgets as widgets; - -use adw::StyleManager; -use gtk4::{ - gdk, - gio::{self, FileMonitor, FileMonitorEvent, FileMonitorFlags}, - glib, - prelude::*, -}; - -pub fn init() -> (Option, Option) { - let _ = gtk4::init(); - adw::init(); - - let gtk_user_provider = gtk4::CssProvider::new(); - if let Some(display) = gdk::Display::default() { - gtk4::StyleContext::add_provider_for_display( - &display, - >k_user_provider, - gtk4::STYLE_PROVIDER_PRIORITY_USER, - ); - } - - let cosmic_user_provider = gtk4::CssProvider::new(); - if let Some(display) = gdk::Display::default() { - gtk4::StyleContext::add_provider_for_display( - &display, - &cosmic_user_provider, - gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION, - ); - } - - let path = xdg::BaseDirectories::with_prefix("gtk-4.0") - .ok() - .and_then(|xdg_dirs| xdg_dirs.find_config_file("gtk.css")) - .unwrap_or_else(|| "~/.config/gtk-4.0/gtk.css".into()); - let gtk_file = gio::File::for_path(path); - let gtk_css_monitor = gtk_file.monitor(FileMonitorFlags::all(), None::<&gio::Cancellable>).ok().map(|monitor| { - monitor.connect_changed(glib::clone!(@strong gtk_user_provider => move |_monitor, file, _other_file, event| { - match event { - FileMonitorEvent::Deleted | FileMonitorEvent::MovedOut | FileMonitorEvent::Renamed => { - if adw::is_initialized() { - let manager = StyleManager::default(); - let css = if manager.is_dark() { - adw_user_colors_lib::colors::ColorOverrides::dark_default().as_gtk_css() - } else { - adw_user_colors_lib::colors::ColorOverrides::light_default().as_gtk_css() - }; - gtk_user_provider - .load_from_data(css.as_bytes()); - } - }, - FileMonitorEvent::ChangesDoneHint | FileMonitorEvent::Created | FileMonitorEvent::MovedIn => { - gtk_user_provider.load_from_file(file); - }, - _ => {} // ignored - } - })); - monitor - }); - let path = xdg::BaseDirectories::with_prefix("gtk-4.0") - .ok() - .and_then(|xdg_dirs| xdg_dirs.find_config_file("cosmic.css")) - .unwrap_or_else(|| "~/.config/gtk-4.0/cosmic.css".into()); - - let cosmic_file = gio::File::for_path(path); - cosmic_user_provider.load_from_file(&cosmic_file); - let cosmic_css_monitor = cosmic_file.monitor(FileMonitorFlags::all(), None::<&gio::Cancellable>).ok().map(|monitor| { - monitor.connect_changed(glib::clone!(@strong cosmic_user_provider => move |_monitor, file, _other_file, event| { - match event { - FileMonitorEvent::Deleted | FileMonitorEvent::MovedOut | FileMonitorEvent::Renamed => { - if adw::is_initialized() { - let manager = StyleManager::default(); - let css = if manager.is_dark() { - adw_user_colors_lib::colors::ColorOverrides::dark_default().as_gtk_css() - } else { - adw_user_colors_lib::colors::ColorOverrides::light_default().as_gtk_css() - }; - cosmic_user_provider - .load_from_data(css.as_bytes()); - } - }, - FileMonitorEvent::ChangesDoneHint | FileMonitorEvent::Created | FileMonitorEvent::MovedIn => { - cosmic_user_provider.load_from_file(file); - }, - _ => {} // ignored - } - })); - monitor - }); - (gtk_css_monitor, cosmic_css_monitor) -} diff --git a/gtk4/src/wayland.rs b/gtk4/src/wayland.rs deleted file mode 100644 index a87e3b16..00000000 --- a/gtk4/src/wayland.rs +++ /dev/null @@ -1,632 +0,0 @@ -// TODO: scale-factor? - -use derivative::Derivative; -use gdk4_wayland::prelude::*; -use gtk4::{ - cairo, gdk, - glib::{ - self, clone, - subclass::{prelude::*, Signal}, - translate::*, - ParamFlags, ParamSpec, ParamSpecBoolean, SignalHandlerId, Value, - }, - gsk::{self, traits::GskRendererExt}, - prelude::*, - subclass::prelude::*, -}; -use once_cell::sync::Lazy; -use std::{ - cell::{Cell, RefCell}, - os::raw::c_int, - ptr, - rc::Rc, -}; -use wayland_client::{event_enum, sys::client::wl_proxy, Filter, GlobalManager, Main, Proxy}; -use wayland_protocols::{ - wlr::unstable::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1}, - xdg_shell::client::xdg_popup, -}; - -use crate::{deref_cell::DerefCell, wayland_custom_surface::WaylandCustomSurface}; - -pub use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ - zwlr_layer_shell_v1::Layer, - zwlr_layer_surface_v1::{Anchor, KeyboardInteractivity}, -}; - -event_enum!( - Events | - LayerSurface => zwlr_layer_surface_v1::ZwlrLayerSurfaceV1 -); - -struct CosmicWaylandDisplay { - event_queue: RefCell, - wayland_display: wayland_client::Display, - wlr_layer_shell: Option>, -} - -impl CosmicWaylandDisplay { - fn for_display(display: &gdk4_wayland::WaylandDisplay) -> Rc { - const DATA_KEY: &str = "cosmic-wayland-display"; - - // `GdkWaylandDisplay` already associated with a `CosmicWaylandDisplay` - if let Some(data) = unsafe { display.data::>(DATA_KEY) } { - return unsafe { data.as_ref() }.clone(); - } - - let wayland_display = unsafe { - wayland_client::Display::from_external_display(display.wl_display().c_ptr() as *mut _) - }; // XXX is this sound? - - let mut event_queue = wayland_display.create_event_queue(); - let attached_display = wayland_display.attach(event_queue.token()); - let globals = GlobalManager::new(&attached_display); - - event_queue.sync_roundtrip(&mut (), |_, _, _| {}).unwrap(); - - let wlr_layer_shell = globals - .instantiate_exact::(1) - .ok(); - - let cosmic_wayland_display = Rc::new(Self { - event_queue: RefCell::new(event_queue), - wayland_display, - wlr_layer_shell, - }); - - unsafe { display.set_data(DATA_KEY, cosmic_wayland_display.clone()) }; - - // XXX Efficient way to poll? - // XXX unwrap? - glib::idle_add_local( - clone!(@weak cosmic_wayland_display => @default-return Continue(false), move || { - cosmic_wayland_display.wayland_display.flush().unwrap(); - let mut event_queue = cosmic_wayland_display.event_queue.borrow_mut(); - if let Some(guard) = event_queue.prepare_read() { - guard.read_events().unwrap(); - } - event_queue.dispatch_pending(&mut (), |_, _, _| {}).unwrap(); - Continue(true) - }), - ); - - cosmic_wayland_display - } -} - -#[derive(Derivative)] -#[derivative(Default)] -pub struct LayerShellWindowInner { - display: DerefCell, - surface: RefCell>, - renderer: RefCell>, - wlr_layer_surface: RefCell>>, - constraint_solver: DerefCell, - child: RefCell>, - monitor: DerefCell>, - #[derivative(Default(value = "Cell::new(Layer::Background)"))] - layer: Cell, - #[derivative(Default(value = "Cell::new(Anchor::empty())"))] - anchor: Cell, - exclusive_zone: Cell, - margin: Cell<(i32, i32, i32, i32)>, - #[derivative(Default(value = "Cell::new(KeyboardInteractivity::None)"))] - keyboard_interactivity: Cell, - namespace: DerefCell, - focus_widget: RefCell>, - is_active: Rc>, -} - -#[glib::object_subclass] -impl ObjectSubclass for LayerShellWindowInner { - const NAME: &'static str = "S76CosmicLayerShellWindow"; - type ParentType = gtk4::Widget; - type Interfaces = (gtk4::Native, gtk4::Root); - type Type = LayerShellWindow; -} - -impl ObjectImpl for LayerShellWindowInner { - fn constructed(&self, obj: &Self::Type) { - self.display - .set(gdk::Display::default().unwrap().downcast().unwrap()); // XXX any issue unwrapping? - self.constraint_solver.set(glib::Object::new(&[]).unwrap()); - - obj.add_css_class("background"); - } - - fn properties() -> &'static [ParamSpec] { - static PROPERTIES: Lazy> = Lazy::new(|| { - vec![ParamSpecBoolean::new( - // Name - "is-active", - // Nickname - "is-active", - // Short description - "Whether the window has keyboard focus", - // Default value - false, - // The property can be read and written to - ParamFlags::READWRITE, - )] - }); - PROPERTIES.as_ref() - } - - fn set_property(&self, _obj: &Self::Type, _id: usize, value: &Value, pspec: &ParamSpec) { - match pspec.name() { - "is-active" => { - let is_active = value - .get() - .expect("The is_active property needs to be of type `bool`"); - self.is_active.replace(is_active); - } - _ => unimplemented!(), - } - } - - fn property(&self, _obj: &Self::Type, _id: usize, pspec: &ParamSpec) -> Value { - match pspec.name() { - "is-active" => self.is_active.get().to_value(), - _ => unimplemented!(), - } - } - - fn signals() -> &'static [Signal] { - static SIGNALS: Lazy> = Lazy::new(|| { - vec![Signal::builder("is-active-notify") - .param_types(&[bool::static_type().into()]) - .build()] - }); - SIGNALS.as_ref() - } -} - -impl WidgetImpl for LayerShellWindowInner { - fn realize(&self, widget: &Self::Type) { - let surface = WaylandCustomSurface::new(&*self.display); - surface.set_get_popup_func(Some(Box::new(clone!(@strong widget => move |_surface, popup| { - if let Some(wlr_layer_surface) = widget.inner().wlr_layer_surface.borrow().as_ref() { - let xdg_popup: xdg_popup::XdgPopup = - unsafe { Proxy::from_c_ptr(gdk_wayland_popup_get_xdg_popup(popup.to_glib_none().0)).into() }; - wlr_layer_surface.get_popup(&xdg_popup); - } - true - })))); - widget.layer_shell_init(&surface); - - let widget_ptr: *mut _ = widget.to_glib_none().0; - let surface_ptr: *mut _ = surface.to_glib_none().0; - unsafe { gdk_surface_set_widget(surface_ptr as *mut _, widget_ptr as *mut _) }; - *self.surface.borrow_mut() = Some(surface.clone()); - surface.connect_render(move |_surface, region| { - unsafe { - gtk_widget_render( - widget_ptr as *mut _, - surface_ptr as *mut _, - region.to_glib_none().0, - ) - }; - true - }); - surface.connect_event( - glib::clone!(@weak widget => @default-return true, move |_, event| { - if event.event_type() == gdk::EventType::FocusChange { - let is_active = event.downcast_ref::().unwrap().is_in(); - widget.set_property("is-active", is_active); - widget.emit_by_name::<()>("is-active-notify", &[&is_active]); - } - unsafe { gtk_main_do_event(event.to_glib_none().0) }; - true - }), - ); - - self.parent_realize(widget); - - *self.renderer.borrow_mut() = - Some(gsk::Renderer::for_surface(surface.upcast_ref()).unwrap()); // XXX unwrap? - - unsafe { gtk4::ffi::gtk_native_realize(widget_ptr as *mut _) }; - } - - fn unrealize(&self, widget: &Self::Type) { - let widget_ptr: *mut Self::Instance = widget.to_glib_none().0; - - unsafe { gtk4::ffi::gtk_native_unrealize(widget_ptr as *mut _) }; - - self.parent_unrealize(widget); - - if let Some(renderer) = self.renderer.borrow_mut().take() { - renderer.unrealize(); - } - - if let Some(surface) = self.surface.borrow().as_ref() { - let surface_ptr: *mut _ = surface.to_glib_none().0; - unsafe { gdk_surface_set_widget(surface_ptr as *mut _, ptr::null_mut()) }; - } - } - - fn map(&self, widget: &Self::Type) { - if let Some(surface) = self.surface.borrow().as_ref() { - let width = widget.measure(gtk4::Orientation::Horizontal, -1).1; - let height = widget.measure(gtk4::Orientation::Vertical, width).1; - widget.set_size(width as u32, height as u32); - surface.present(width, height); - } - - self.parent_map(widget); - - if let Some(child) = self.child.borrow().as_ref() { - child.map(); - } - } - - fn unmap(&self, widget: &Self::Type) { - self.parent_unmap(widget); - - if let Some(surface) = self.surface.borrow().as_ref() { - surface.hide(); - } - - if let Some(child) = self.child.borrow().as_ref() { - child.unmap(); - } - } - - fn measure( - &self, - _widget: &Self::Type, - orientation: gtk4::Orientation, - for_size: i32, - ) -> (i32, i32, i32, i32) { - if let Some(child) = self.child.borrow().as_ref() { - child.measure(orientation, for_size) - } else { - (0, 0, 0, 0) - } - } - - fn size_allocate(&self, _widget: &Self::Type, width: i32, height: i32, baseline: i32) { - if let Some(child) = self.child.borrow().as_ref() { - child.allocate(width, height, baseline, None) - } - } - - fn show(&self, widget: &Self::Type) { - WidgetExt::realize(widget); - self.parent_show(widget); - widget.map(); - } - - fn hide(&self, widget: &Self::Type) { - self.parent_hide(widget); - widget.unmap(); - } -} - -// TODO: Move into gtk4-rs when support merged/released in gtk -unsafe impl IsImplementable for gtk4::Native { - fn interface_init(iface: &mut glib::Interface) { - let iface = unsafe { &mut *(iface as *mut _ as *mut GtkNativeInterface) }; - iface.get_surface = Some(get_surface); - iface.get_renderer = Some(get_renderer); - iface.get_surface_transform = Some(get_surface_transform); - iface.layout = Some(layout); - } - - fn instance_init(_instance: &mut glib::subclass::InitializingObject) {} -} - -// TODO: Move into gtk4-rs when support merged/released in gtk -unsafe impl IsImplementable for gtk4::Root { - fn interface_init(iface: &mut glib::Interface) { - let iface = unsafe { &mut *(iface as *mut _ as *mut GtkRootInterface) }; - iface.get_display = Some(get_display); - iface.get_constraint_solver = Some(get_constraint_solver); - iface.get_focus = Some(get_focus); - iface.set_focus = Some(set_focus); - } - - fn instance_init(_instance: &mut glib::subclass::InitializingObject) {} -} - -glib::wrapper! { - pub struct LayerShellWindow(ObjectSubclass) - @extends gtk4::Widget, @implements gtk4::Accessible, gtk4::Buildable, gtk4::ConstraintTarget, gtk4::Native, gtk4::Root; -} -// TODO handle configure/destroy -// TODO presumably call destroy() when appropriate? -// What do wayland-client types do when associated connection is gone? Panic? UB? -impl LayerShellWindow { - pub fn new( - monitor: Option<&gdk4_wayland::WaylandMonitor>, - layer: Layer, - namespace: &str, - ) -> Self { - let obj: Self = glib::Object::new(&[]).unwrap(); - obj.inner().monitor.set(monitor.cloned()); - obj.inner().layer.set(layer); - obj.inner().namespace.set(namespace.to_string()); - obj - } - - fn inner(&self) -> &LayerShellWindowInner { - LayerShellWindowInner::from_instance(self) - } - - pub fn set_child>(&self, w: Option<&T>) { - let mut child = self.inner().child.borrow_mut(); - if let Some(child) = child.take() { - child.unparent(); - } - if let Some(w) = w { - w.set_parent(self); - } - *child = w.map(|x| x.clone().upcast()); - } - - pub fn is_active(&self) -> bool { - self.property::("is-active") - } - - pub fn connect_is_active_notify(&self, f: F) -> SignalHandlerId { - self.connect_local("is-active-notify", false, move |args| { - f(&args[0].get::().unwrap()); - None - }) - } - - fn layer_shell_init(&self, surface: &WaylandCustomSurface) { - let width = self.measure(gtk4::Orientation::Horizontal, -1).1; - let height = self.measure(gtk4::Orientation::Vertical, width).1; - // XXX needed for wl_surface to exist - surface.present(width, height); - - let wl_surface = surface.wl_surface(); - - let cosmic_wayland_display = CosmicWaylandDisplay::for_display(&*self.inner().display); - let wlr_layer_shell = match cosmic_wayland_display.wlr_layer_shell.as_ref() { - Some(wlr_layer_shell) => wlr_layer_shell, - None => { - eprintln!("Error: Layer shell not supported by compositor"); - return; - } - }; - - let output = self.inner().monitor.as_ref().map(|x| x.wl_output()); - let layer = self.layer(); - let namespace = self.namespace().to_string(); - let wlr_layer_surface = - wlr_layer_shell.get_layer_surface(&wl_surface, output.as_ref(), layer, namespace); - - wlr_layer_surface.set_anchor(self.anchor()); - wlr_layer_surface.set_exclusive_zone(self.exclusive_zone()); - let margin = self.margin(); - wlr_layer_surface.set_margin(margin.0, margin.1, margin.2, margin.3); - wlr_layer_surface.set_keyboard_interactivity(self.keyboard_interactivity()); - wlr_layer_surface.set_size(width as u32, height as u32); - - let filter = Filter::new( - clone!(@strong self as self_ => move |event, _, _| match event { - Events::LayerSurface { event, object } => match event { - zwlr_layer_surface_v1::Event::Configure { - serial, - width: _, - height: _, - } => { - // TODO: should size window to match `width`/`height` - object.ack_configure(serial); - } - zwlr_layer_surface_v1::Event::Closed => {} - _ => {} - }, - }), - ); - wlr_layer_surface.assign(filter); - - wl_surface.commit(); - - cosmic_wayland_display - .event_queue - .borrow_mut() - .sync_roundtrip(&mut (), |_, _, _| {}) - .unwrap(); - - *self.inner().wlr_layer_surface.borrow_mut() = Some(wlr_layer_surface); - } - - fn set_size(&self, width: u32, height: u32) { - if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() { - wlr_layer_surface.set_size(width, height); - }; - } - - pub fn anchor(&self) -> Anchor { - self.inner().anchor.get() - } - - pub fn set_anchor(&self, anchor: Anchor) { - if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() { - wlr_layer_surface.set_anchor(anchor); - }; - self.inner().anchor.set(anchor); - } - - pub fn exclusive_zone(&self) -> i32 { - self.inner().exclusive_zone.get() - } - - pub fn set_exclusive_zone(&self, zone: i32) { - if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() { - wlr_layer_surface.set_exclusive_zone(zone); - }; - self.inner().exclusive_zone.set(zone); - } - - pub fn margin(&self) -> (i32, i32, i32, i32) { - self.inner().margin.get() - } - - pub fn set_margin(&self, top: i32, right: i32, bottom: i32, left: i32) { - if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() { - wlr_layer_surface.set_margin(top, right, bottom, left); - }; - self.inner().margin.set((top, right, bottom, left)); - } - - pub fn keyboard_interactivity(&self) -> KeyboardInteractivity { - self.inner().keyboard_interactivity.get() - } - - pub fn set_keyboard_interactivity(&self, interactivity: KeyboardInteractivity) { - if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() { - wlr_layer_surface.set_keyboard_interactivity(interactivity); - }; - self.inner().keyboard_interactivity.set(interactivity); - } - - pub fn layer(&self) -> Layer { - self.inner().layer.get() - } - - pub fn set_layer(&self, layer: Layer) { - if let Some(wlr_layer_surface) = self.inner().wlr_layer_surface.borrow().as_ref() { - wlr_layer_surface.set_layer(layer); - }; - self.inner().layer.set(layer); - } - - pub fn namespace(&self) -> &str { - self.inner().namespace.as_str() - } -} - -pub struct GtkConstraintSolver { - _private: [u8; 0], -} - -// XXX needs to be public in gtk -#[link(name = "gtk-4")] -extern "C" { - pub fn gtk_constraint_solver_get_type() -> glib::ffi::GType; - - pub fn gdk_surface_set_widget(surface: *mut gdk::ffi::GdkSurface, widget: glib::ffi::gpointer); - - pub fn _gtk_widget_set_visible_flag( - widget: *mut gtk4::ffi::GtkWidget, - visible: glib::ffi::gboolean, - ); - - pub fn gtk_widget_render( - widget: *mut gtk4::ffi::GtkWidget, - surface: *mut gdk::ffi::GdkSurface, - region: *const cairo::ffi::cairo_region_t, - ); - - pub fn gtk_main_do_event(event: *mut gdk::ffi::GdkEvent); - - // Added API - pub fn gdk_wayland_popup_get_xdg_popup( - popup: *mut gdk4_wayland::ffi::GdkWaylandPopup, - ) -> *mut wl_proxy; -} - -glib::wrapper! { - pub struct ConstraintSolver(Object); - - match fn { - type_ => || gtk_constraint_solver_get_type(), - } -} - -pub struct GtkNativeInterface { - pub g_iface: gobject_sys::GTypeInterface, - pub get_surface: - Option *mut gdk::ffi::GdkSurface>, - pub get_renderer: Option< - unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkNative) -> *mut gsk::ffi::GskRenderer, - >, - pub get_surface_transform: - Option, - pub layout: - Option, -} - -pub struct GtkRootInterface { - pub g_iface: gobject_sys::GTypeInterface, - pub get_display: - Option *mut gdk::ffi::GdkDisplay>, - pub get_constraint_solver: - Option *mut GtkConstraintSolver>, - pub get_focus: - Option *mut gtk4::ffi::GtkWidget>, - pub set_focus: Option< - unsafe extern "C" fn(self_: *mut gtk4::ffi::GtkRoot, focus: *mut gtk4::ffi::GtkWidget), - >, -} - -unsafe extern "C" fn get_surface(native: *mut gtk4::ffi::GtkNative) -> *mut gdk::ffi::GdkSurface { - let instance = &*(native as *mut ::Instance); - let imp = instance.imp(); - imp.surface.borrow().as_ref().map_or(ptr::null_mut(), |x| { - x.upcast_ref::().to_glib_none().0 - }) -} - -unsafe extern "C" fn get_renderer(native: *mut gtk4::ffi::GtkNative) -> *mut gsk::ffi::GskRenderer { - let instance = &*(native as *mut ::Instance); - let imp = instance.imp(); - imp.renderer - .borrow() - .as_ref() - .map_or(ptr::null_mut(), |x| x.to_glib_none().0) -} - -unsafe extern "C" fn get_surface_transform( - _native: *mut gtk4::ffi::GtkNative, - x: *mut f64, - y: *mut f64, -) { - // TODO: add css logic like `GtkWindow` has - - *x = 0.; - *y = 0.; -} - -unsafe extern "C" fn layout(native: *mut gtk4::ffi::GtkNative, width: c_int, height: c_int) { - // TODO: `GtkWindow` has more here - gtk4::ffi::gtk_widget_allocate(native as *mut _, width, height, -1, ptr::null_mut()); -} - -unsafe extern "C" fn get_display(root: *mut gtk4::ffi::GtkRoot) -> *mut gdk::ffi::GdkDisplay { - let instance = &*(root as *mut ::Instance); - let imp = instance.imp(); - imp.display.upcast_ref::().to_glib_none().0 -} - -unsafe extern "C" fn get_constraint_solver( - root: *mut gtk4::ffi::GtkRoot, -) -> *mut GtkConstraintSolver { - let instance = &*(root as *mut ::Instance); - let imp = instance.imp(); - imp.constraint_solver.to_glib_none().0 -} - -unsafe extern "C" fn get_focus(root: *mut gtk4::ffi::GtkRoot) -> *mut gtk4::ffi::GtkWidget { - let instance = &*(root as *mut ::Instance); - let imp = instance.imp(); - imp.focus_widget - .borrow() - .as_ref() - .map_or(ptr::null_mut(), |x| x.to_glib_none().0) -} - -unsafe extern "C" fn set_focus(root: *mut gtk4::ffi::GtkRoot, focus: *mut gtk4::ffi::GtkWidget) { - // TODO: `GtkWindow` does more here - let instance = &*(root as *mut ::Instance); - let imp = instance.imp(); - *imp.focus_widget.borrow_mut() = if focus.is_null() { - None - } else { - Some(gtk4::Widget::from_glib_none(focus)) - }; -} diff --git a/gtk4/src/wayland_custom_surface.rs b/gtk4/src/wayland_custom_surface.rs deleted file mode 100644 index fceb4cb5..00000000 --- a/gtk4/src/wayland_custom_surface.rs +++ /dev/null @@ -1,144 +0,0 @@ -use gdk4_wayland::{WaylandDisplay, WaylandPopup}; -use gtk4::{ - gdk, - glib::{self, translate::*}, -}; -use std::boxed::Box as Box_; -use std::fmt; - -mod ffi { - use gdk4_wayland::ffi::{GdkWaylandDisplay, GdkWaylandPopup}; - use gtk4::{ - gdk::ffi::GdkFrameClock, - glib::ffi::{gboolean, gpointer, GDestroyNotify, GType}, - }; - use std::os::raw::c_int; - - pub type GdkWaylandCustomSurfaceGetPopupFunc = Option< - unsafe extern "C" fn( - *mut GdkWaylandCustomSurface, - *mut GdkWaylandPopup, - gpointer, - ) -> gboolean, - >; - - #[repr(C)] - pub struct GdkWaylandCustomSurface { - _data: [u8; 0], - _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, - } - - impl ::std::fmt::Debug for GdkWaylandCustomSurface { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - f.debug_struct(&format!("GdkWaylandCustomSurface @ {:p}", self)) - .finish() - } - } - - extern "C" { - pub fn gdk_wayland_custom_surface_get_type() -> GType; - - pub fn gdk_wayland_custom_surface_new( - display: *mut GdkWaylandDisplay, - ) -> *mut GdkWaylandCustomSurface; - - pub fn gdk_wayland_custom_surface_present( - custom_surface: *mut GdkWaylandCustomSurface, - width: c_int, - height: c_int, - ); - - pub fn gdk_wayland_custom_surface_set_get_popup_func( - custom_surface: *mut GdkWaylandCustomSurface, - get_popup_func: GdkWaylandCustomSurfaceGetPopupFunc, - user_data: gpointer, - destroy: GDestroyNotify, - ); - - pub fn _gdk_frame_clock_idle_new() -> *mut GdkFrameClock; - } -} - -glib::wrapper! { - #[doc(alias = "GdkWaylandCustomSurface")] - pub struct WaylandCustomSurface(Object) @extends gdk4_wayland::WaylandSurface, gdk::Surface; - - match fn { - type_ => || ffi::gdk_wayland_custom_surface_get_type(), - } -} - -impl WaylandCustomSurface { - #[doc(alias = "gdk_wayland_custom_surface_new")] - pub fn new(display: &WaylandDisplay) -> WaylandCustomSurface { - unsafe { - from_glib_full(ffi::gdk_wayland_custom_surface_new( - display.to_glib_none().0, - )) - } - } - - #[doc(alias = "gdk_wayland_custom_surface_present")] - pub fn present(&self, width: i32, height: i32) { - unsafe { - ffi::gdk_wayland_custom_surface_present(self.to_glib_none().0, width, height); - } - } - - #[doc(alias = "gdk_wayland_custom_surface_set_get_popup_func")] - pub fn set_get_popup_func( - &self, - get_popup_func: Option< - Box_ bool + 'static>, - >, - ) { - let get_popup_func_data: Box_< - Option bool + 'static>>, - > = Box_::new(get_popup_func); - unsafe extern "C" fn get_popup_func_func( - custom_surface: *mut ffi::GdkWaylandCustomSurface, - popup: *mut gdk4_wayland::ffi::GdkWaylandPopup, - user_data: glib::ffi::gpointer, - ) -> glib::ffi::gboolean { - let custom_surface = from_glib_borrow(custom_surface); - let popup = from_glib_borrow(popup); - let callback: &Option< - Box_ bool + 'static>, - > = &*(user_data as *mut _); - let res = if let Some(ref callback) = *callback { - callback(&custom_surface, &popup) - } else { - panic!("cannot get closure...") - }; - res.into_glib() - } - let get_popup_func = if get_popup_func_data.is_some() { - Some(get_popup_func_func as _) - } else { - None - }; - unsafe extern "C" fn destroy_func(data: glib::ffi::gpointer) { - let _callback: Box_< - Option bool + 'static>>, - > = Box_::from_raw(data as *mut _); - } - let destroy_call3 = Some(destroy_func as _); - let super_callback0: Box_< - Option bool + 'static>>, - > = get_popup_func_data; - unsafe { - ffi::gdk_wayland_custom_surface_set_get_popup_func( - self.to_glib_none().0, - get_popup_func, - Box_::into_raw(super_callback0) as *mut _, - destroy_call3, - ); - } - } -} - -impl fmt::Display for WaylandCustomSurface { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("WaylandCustomSurface") - } -} diff --git a/gtk4/src/x.rs b/gtk4/src/x.rs deleted file mode 100644 index 95784769..00000000 --- a/gtk4/src/x.rs +++ /dev/null @@ -1,188 +0,0 @@ -use cascade::cascade; -use gdk4_x11::x11::xlib; -use glib::translate::ToGlibPtr; -use gtk4::{glib, prelude::*}; -use std::{ - ffi::{CString, NulError}, - os::raw::c_long, - ptr, -}; - -pub use std::os::raw::{c_int, c_uchar, c_ulong, c_ushort}; - -pub fn get_window_x11>( - window: &T, -) -> Option<(gdk4_x11::X11Display, gdk4_x11::X11Surface)> { - let surface = window - .upcast_ref() - .surface() - .downcast::() - .ok()?; - let display = surface.display().downcast::().ok()?; - Some((display, surface)) -} - -#[repr(transparent)] -pub struct Atom(xlib::Atom); - -impl Atom { - pub fn new(display: &gdk4_x11::X11Display, prop: &str) -> Result { - unsafe { - let prop = CString::new(prop)?; - Ok(Self(gdk4_x11::ffi::gdk_x11_get_xatom_by_name_for_display( - display.to_glib_none().0, - prop.as_ptr(), - ))) - } - } -} - -pub unsafe trait XElement { - const TYPE: xlib::Atom; - const SIZE: c_int; -} - -unsafe impl XElement for c_uchar { - const TYPE: xlib::Atom = xlib::XA_CARDINAL; - const SIZE: c_int = 8; -} - -unsafe impl XElement for c_ushort { - const TYPE: xlib::Atom = xlib::XA_CARDINAL; - const SIZE: c_int = 16; -} - -unsafe impl XElement for c_ulong { - const TYPE: xlib::Atom = xlib::XA_CARDINAL; - const SIZE: c_int = 32; -} - -unsafe impl XElement for Atom { - const TYPE: xlib::Atom = xlib::XA_ATOM; - const SIZE: c_int = 32; -} - -pub unsafe trait XProp { - const TYPE: xlib::Atom; - const SIZE: c_int; - - fn ptr(&self) -> *const u8; - - fn nelements(&self) -> c_int; -} - -unsafe impl XProp for &[T; LEN] { - const TYPE: xlib::Atom = T::TYPE; - const SIZE: c_int = T::SIZE; - - fn ptr(&self) -> *const u8 { - self.as_ptr() as _ - } - - fn nelements(&self) -> c_int { - LEN as c_int - } -} - -unsafe impl XProp for &[T] { - const TYPE: xlib::Atom = T::TYPE; - const SIZE: c_int = T::SIZE; - - fn ptr(&self) -> *const u8 { - self.as_ptr() as _ - } - - fn nelements(&self) -> c_int { - self.len() as c_int - } -} - -unsafe impl XProp for &str { - const TYPE: xlib::Atom = xlib::XA_STRING; - const SIZE: c_int = 8; - - fn ptr(&self) -> *const u8 { - self.as_ptr() - } - - fn nelements(&self) -> c_int { - self.len() as c_int - } -} - -#[allow(dead_code)] -pub enum PropMode { - Replace, - Prepend, - Append, -} - -pub unsafe fn change_property( - display: &gdk4_x11::X11Display, - surface: &gdk4_x11::X11Surface, - prop: &str, - mode: PropMode, - value: T, -) { - // TODO check error return value - let mode = match mode { - PropMode::Replace => xlib::PropModeReplace, - PropMode::Prepend => xlib::PropModePrepend, - PropMode::Append => xlib::PropModeAppend, - }; - xlib::XChangeProperty( - display.xdisplay(), - surface.xid(), - Atom::new(display, prop).unwrap().0, - T::TYPE, - T::SIZE, - mode, - value.ptr(), - value.nelements(), - ); -} - -pub unsafe fn set_position( - display: &gdk4_x11::X11Display, - surface: &gdk4_x11::X11Surface, - x: c_int, - y: c_int, -) { - // XXX check error return value - xlib::XMoveWindow(display.xdisplay(), surface.xid(), x, y); -} - -pub unsafe fn wm_state_add( - display: &gdk4_x11::X11Display, - surface: &gdk4_x11::X11Surface, - state: &str, -) { - const _NET_WM_STATE_ADD: c_long = 1; - // XXX check error return value - let mut event = xlib::XEvent { - client_message: xlib::XClientMessageEvent { - type_: xlib::ClientMessage, - serial: 0, - send_event: 0, - display: ptr::null_mut(), - window: surface.xid(), - message_type: Atom::new(display, "_NET_WM_STATE").unwrap().0, - format: 32, - data: cascade! { - xlib::ClientMessageData::new(); - ..set_long(0, _NET_WM_STATE_ADD); - ..set_long(1, Atom::new(display, state).unwrap().0 as _); - ..set_long(2, Atom::new(display, "").unwrap().0 as _); - ..set_long(3, 1); - ..set_long(3, 0); - }, - }, - }; - xlib::XSendEvent( - display.xdisplay(), - display.xrootwindow(), - 0, - xlib::SubstructureRedirectMask | xlib::SubstructureNotifyMask, - &mut event, - ); -} diff --git a/gtk4/widgets/Cargo.toml b/gtk4/widgets/Cargo.toml deleted file mode 100644 index c1229f5a..00000000 --- a/gtk4/widgets/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "libcosmic-widgets" -version = "0.1.0" -edition = "2021" - -[dependencies] -relm4 = { git = "https://github.com/Relm4/Relm4", branch = "next" } -relm4-macros = { git = "https://github.com/Relm4/Relm4", branch = "next" } -tracker = "0.1.1" diff --git a/gtk4/widgets/src/labeled_item/imp.rs b/gtk4/widgets/src/labeled_item/imp.rs deleted file mode 100644 index 8c84bd59..00000000 --- a/gtk4/widgets/src/labeled_item/imp.rs +++ /dev/null @@ -1,173 +0,0 @@ -use relm4::{ - component::ComponentSenderInner, - gtk::{prelude::*, Align, Box as GtkBox, Label, Orientation, Widget}, - ComponentParts, ComponentSender, SimpleComponent, -}; -use std::{cell::RefCell, sync::Arc}; - -#[derive(Debug)] -pub(crate) enum LabeledItemMessage { - Title(String), - Desc(Option), - Align(Align), - Child(Widget), -} - -#[track] -pub(crate) struct LabeledItem { - _title: String, - _desc: Option, - _align: Align, - _child: Option, - #[do_not_track] - _remove_child: RefCell>, - #[do_not_track] - _sender: ComponentSender, -} - -impl LabeledItem { - pub fn title(&self) -> &str { - &self._title - } - - pub fn description(&self) -> Option<&String> { - self._desc.as_ref() - } - - pub fn alignment(&self) -> Align { - self._align - } - - pub fn child(&self) -> Option<&Widget> { - self._child.as_ref() - } - - pub fn set_title(&self, title: S) - where - S: ToString, - { - self._sender - .input(LabeledItemMessage::Title(title.to_string())); - } - - pub fn set_description<'a, O>(&self, description: O) - where - O: Into>, - { - let description = description.into(); - self._sender - .input(LabeledItemMessage::Desc(description.map(|s| s.to_string()))); - } - - pub fn set_alignment(&self, align: Align) { - self._sender.input(LabeledItemMessage::Align(align)); - } - - pub fn set_child(&self, child: Widget) { - self._sender.input(LabeledItemMessage::Child(child)); - } -} - -#[component(pub(crate))] -impl SimpleComponent for LabeledItem { - type Widgets = AppWidgets; - type InitParams = (); - type Input = LabeledItemMessage; - type Output = (); - - view! { - base_box = GtkBox { - add_css_class: "labeled-item", - set_orientation: Orientation::Horizontal, - set_hexpand: true, - set_margin_start: 24, - set_margin_end: 24, - set_margin_top: 8, - set_margin_bottom: 8, - set_spacing: 16, - append: labeled_item_info = &GtkBox { - add_css_class: "labeled-item-info", - set_orientation: Orientation::Vertical, - set_hexpand: true, - set_spacing: 8, - set_valign: Align::Center, - Label { - add_css_class: "labeled-item-title", - set_halign: Align::Start, - #[watch] - set_label: &model._title - }, - Label { - add_css_class: "labeled-item-desc", - set_halign: Align::Start, - #[watch] - set_visible: model._desc.is_some(), - #[watch] - set_label: &model._desc.clone().unwrap_or_default() - }, - } - } - } - - fn init( - _init_params: Self::InitParams, - root: &Self::Root, - _sender: Arc>, - ) -> ComponentParts { - let model = LabeledItem { - _title: String::default(), - _desc: None, - _align: Align::Start, - _child: None, - _remove_child: RefCell::new(None), - _sender: _sender.clone(), - tracker: 0, - }; - let widgets = view_output!(); - - ComponentParts { model, widgets } - } - - fn update( - &mut self, - msg: Self::Input, - _sender: Arc>, - ) { - self.reset(); - match msg { - LabeledItemMessage::Title(title) => self.set__title(title), - LabeledItemMessage::Desc(desc) => self.set__desc(desc), - LabeledItemMessage::Align(align) => self.set__align(align), - LabeledItemMessage::Child(child) => { - *self._remove_child.borrow_mut() = self._child.take(); - self.set__child(Some(child)) - } - } - } - - fn post_view() { - if let Some(child) = self._remove_child.borrow_mut().take() { - widgets.base_box.remove(&child); - } - if self.changed(LabeledItem::_child()) { - let child = self._child.as_ref().expect("there's no child??"); - widgets.base_box.append(child); - } - if self.changed(LabeledItem::_align()) { - let child = self._child.as_ref().expect("set alignment without child"); - match self._align { - Align::Start => { - widgets - .base_box - .reorder_child_after(&widgets.labeled_item_info, Some(child)); - } - Align::End => { - widgets - .base_box - .reorder_child_after(child, Some(&widgets.labeled_item_info)); - } - _ => unimplemented!(), - } - } - } -} diff --git a/gtk4/widgets/src/labeled_item/mod.rs b/gtk4/widgets/src/labeled_item/mod.rs deleted file mode 100644 index 0684793d..00000000 --- a/gtk4/widgets/src/labeled_item/mod.rs +++ /dev/null @@ -1,90 +0,0 @@ -mod imp; - -use relm4::{ - gtk::{glib::IsA, prelude::*, Align, Box as GtkBox, Orientation, Widget}, - Component, ComponentController, ComponentParts, Controller, -}; -use std::{cell::Ref, ops::Deref}; - -pub struct LabeledItem { - root: GtkBox, - controller: Controller, -} - -impl LabeledItem { - fn inner(&self) -> Ref<'_, ComponentParts> { - self.controller.state().get() - } - - pub fn new() -> Self { - Self::default() - } - - pub fn widget(&self) -> GtkBox { - self.root.clone() - } - - pub fn title(&self) -> String { - self.inner().model.title().to_owned() - } - - pub fn description(&self) -> Option { - self.inner().model.description().cloned() - } - - pub fn alignment(&self) -> Align { - self.inner().model.alignment() - } - - pub fn child(&self) -> Option { - self.inner().model.child().cloned() - } - - pub fn set_title(&self, title: S) - where - S: ToString, - { - self.inner().model.set_title(title) - } - - pub fn set_description<'a, O>(&self, description: O) - where - O: Into>, - { - self.inner().model.set_description(description) - } - - pub fn set_alignment(&self, align: Align) { - self.inner().model.set_alignment(align) - } - - pub fn set_child(&self, child: &impl IsA) { - let widget = child.upcast_ref(); - self.inner().model.set_child(widget.clone()); - } -} - -impl Default for LabeledItem { - fn default() -> Self { - let root = GtkBox::new(Orientation::Horizontal, 0); - let controller = imp::LabeledItem::builder() - .attach_to(&root) - .launch(()) - .detach(); - Self { root, controller } - } -} - -impl AsRef for LabeledItem { - fn as_ref(&self) -> &Widget { - self.root.upcast_ref() - } -} - -impl Deref for LabeledItem { - type Target = GtkBox; - - fn deref(&self) -> &Self::Target { - &self.root - } -} diff --git a/gtk4/widgets/src/lib.rs b/gtk4/widgets/src/lib.rs deleted file mode 100644 index dc90fc27..00000000 --- a/gtk4/widgets/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[macro_use] -extern crate relm4_macros; -#[macro_use] -extern crate tracker; - -pub mod labeled_item; - -pub use labeled_item::LabeledItem; -pub use relm4;