Iced graphics applet (#26)

* feat: builds deb

* feat: working iced graphics applet

* fix: update deps to get fixes for iced-sctk

* fix: better popup size

* fix: better styling of button & popup

* fix: better popup container

* feat: sticky graphics mode after selection

* fix: refactor & handle close requests
This commit is contained in:
Ashley Wulber 2022-11-17 16:38:48 +01:00 committed by GitHub
parent e3be1c1809
commit eab7ddea1d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 3836 additions and 360 deletions

53
Cargo.lock generated
View file

@ -384,20 +384,6 @@ dependencies = [
"zbus 2.3.2",
]
[[package]]
name = "cosmic-applet-graphics"
version = "0.1.0"
dependencies = [
"gtk4",
"libadwaita",
"libcosmic",
"libcosmic-applet",
"once_cell",
"relm4-macros",
"tokio",
"zbus 2.3.2",
]
[[package]]
name = "cosmic-applet-network"
version = "0.1.0"
@ -1512,6 +1498,16 @@ dependencies = [
"unic-langid",
]
[[package]]
name = "io-lifetimes"
version = "1.0.0-rc1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f65dae1d3aa98e6877917ab4e6fdbfdfb00e95885ea7c4f4f29e3a5dfc08fdf"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.3"
@ -2914,12 +2910,13 @@ checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "wayland-backend"
version = "0.1.0-beta.9"
source = "git+https://github.com/smithay/wayland-rs#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59"
version = "0.1.0-beta.13"
source = "git+https://github.com/smithay/wayland-rs#051cbf6e38f4532c74ab5fc9a8d39ed84e1f2e04"
dependencies = [
"cc",
"downcast-rs",
"nix 0.24.2",
"io-lifetimes",
"nix 0.25.0",
"scoped-tls",
"smallvec",
"wayland-sys",
@ -2927,13 +2924,13 @@ dependencies = [
[[package]]
name = "wayland-client"
version = "0.30.0-beta.9"
source = "git+https://github.com/smithay/wayland-rs#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59"
version = "0.30.0-beta.13"
source = "git+https://github.com/smithay/wayland-rs#051cbf6e38f4532c74ab5fc9a8d39ed84e1f2e04"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"nix 0.24.2",
"nix 0.25.0",
"thiserror",
"wayland-backend",
"wayland-scanner",
@ -2941,8 +2938,8 @@ dependencies = [
[[package]]
name = "wayland-protocols"
version = "0.30.0-beta.9"
source = "git+https://github.com/smithay/wayland-rs#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59"
version = "0.30.0-beta.13"
source = "git+https://github.com/smithay/wayland-rs#051cbf6e38f4532c74ab5fc9a8d39ed84e1f2e04"
dependencies = [
"bitflags",
"wayland-backend",
@ -2952,8 +2949,8 @@ dependencies = [
[[package]]
name = "wayland-protocols-wlr"
version = "0.1.0-beta.9"
source = "git+https://github.com/smithay/wayland-rs#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59"
version = "0.1.0-beta.13"
source = "git+https://github.com/smithay/wayland-rs#051cbf6e38f4532c74ab5fc9a8d39ed84e1f2e04"
dependencies = [
"bitflags",
"wayland-backend",
@ -2964,8 +2961,8 @@ dependencies = [
[[package]]
name = "wayland-scanner"
version = "0.30.0-beta.9"
source = "git+https://github.com/smithay/wayland-rs#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59"
version = "0.30.0-beta.13"
source = "git+https://github.com/smithay/wayland-rs#051cbf6e38f4532c74ab5fc9a8d39ed84e1f2e04"
dependencies = [
"proc-macro2",
"quick-xml",
@ -2975,8 +2972,8 @@ dependencies = [
[[package]]
name = "wayland-sys"
version = "0.30.0-beta.9"
source = "git+https://github.com/smithay/wayland-rs#fa4be85e8f3b57c8b6e8dd2476d6b4734b810e59"
version = "0.30.0-beta.13"
source = "git+https://github.com/smithay/wayland-rs#051cbf6e38f4532c74ab5fc9a8d39ed84e1f2e04"
dependencies = [
"dlib",
"log",

View file

@ -2,7 +2,6 @@
members = [
"applets/cosmic-applet-audio",
"applets/cosmic-applet-battery",
"applets/cosmic-applet-graphics",
"applets/cosmic-applet-network",
"applets/cosmic-applet-notifications",
"applets/cosmic-applet-power",
@ -13,11 +12,14 @@ members = [
"applets/cosmic-panel-button",
"libcosmic-applet",
]
exclude = [
"applets/cosmic-applet-graphics",
]
[patch.crates-io]
wayland-protocols = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
wayland-protocols = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}
wayland-protocols-wlr = { git = "https://github.com/smithay/wayland-rs", version = "0.1.0-beta.9"}
wayland-sys = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
wayland-backend = { git = "https://github.com/smithay/wayland-rs", version = "0.1.0-beta.9"}
wayland-scanner = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
wayland-client = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.9"}
wayland-sys = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}
wayland-backend = { git = "https://github.com/smithay/wayland-rs", version = "0.1.0-beta.13"}
wayland-scanner = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}
wayland-client = { git = "https://github.com/smithay/wayland-rs", version = "0.30.0-beta.13"}

View file

@ -1,7 +1,7 @@
//! Utilities for using an [`EventQueue`] from wayland-client with an event loop that performs polling with
//! [`calloop`](https://crates.io/crates/calloop).
use std::{io, os::unix::prelude::RawFd};
use std::{io, os::unix::prelude::{RawFd, AsRawFd}};
use calloop::{
generic::Generic, EventSource, InsertError, Interest, LoopHandle, Mode, Poll, PostAction,
@ -30,7 +30,7 @@ impl<D> WaylandSource<D> {
/// Wrap an [`EventQueue`] as a [`WaylandSource`].
pub fn new(queue: EventQueue<D>) -> Result<WaylandSource<D>, WaylandError> {
let guard = queue.prepare_read()?;
let fd = Generic::new(guard.connection_fd(), Interest::READ, Mode::Level);
let fd = Generic::new(guard.connection_fd().as_raw_fd(), Interest::READ, Mode::Level);
drop(guard);
Ok(WaylandSource {
@ -192,12 +192,12 @@ impl<D> WaylandSource<D> {
break Err(Errno::EPROTO.into());
}
Err(DispatchError::BadMessage { msg, interface }) => {
Err(DispatchError::BadMessage { sender_id, interface, opcode }) => {
log::error!(
"Bad message on interface \"{}\": (opcode: {}, args: {:?})",
"Bad message on interface \"{}\": (opcode: {}, sender_id: {:?})",
interface,
msg.opcode,
msg.args,
opcode,
sender_id,
);
break Err(Errno::EPROTO.into());

3426
applets/cosmic-applet-graphics/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -6,11 +6,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gtk4 = { git = "https://github.com/gtk-rs/gtk4-rs", features = ["v4_2"] }
adw = { git = "https://gitlab.gnome.org/World/Rust/libadwaita-rs", package = "libadwaita"}
libcosmic = { git = "https://github.com/pop-os/libcosmic", default-features = false }
libcosmic-applet = { path = "../../libcosmic-applet" }
once_cell = "1.9.0"
relm4-macros = { git = "https://github.com/Relm4/Relm4.git", branch = "next" }
tokio = { version = "1.16.1", features = ["full"] }
zbus = "2.1.1"
zbus = "3.4"
libcosmic = { git = "https://github.com/pop-os/libcosmic/", branch = "sctk-cosmic-design-system", default-features = false, features = ["wayland", "applet"] }
cosmic-panel-config = {git = "https://github.com/pop-os/cosmic-panel", default-features = false }
iced_sctk = { git = "https://github.com/pop-os/iced-sctk" }
# iced_sctk = { path = "../../../fork/iced-sctk" }
sctk = { package = "smithay-client-toolkit", git = "https://github.com/Smithay/client-toolkit" }

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
//! # DBus interface proxy for: `com.system76.PowerDaemon`
//!
//! This code was generated by `zbus-xmlgen` `2.0.1` from DBus introspection data.
//! This code was generated by `zbus-xmlgen` `3.0.0` from DBus introspection data.
//! Source: `Interface '/com/system76/PowerDaemon' from service 'com.system76.PowerDaemon' on system bus`.
//!
//! You may prefer to adapt it, instead of using it verbatim.
@ -18,11 +18,10 @@
//!
//! …consequently `zbus-xmlgen` did not generate code for the above interfaces.
use zbus::dbus_proxy;
use zbus::{dbus_proxy, Connection};
#[dbus_proxy(
interface = "com.system76.PowerDaemon",
default_service = "com.system76.PowerDaemon",
default_path = "/com/system76/PowerDaemon"
)]
trait PowerDaemon {
@ -78,3 +77,17 @@ trait PowerDaemon {
#[dbus_proxy(signal)]
fn power_profile_switch(&self, profile: &str) -> zbus::Result<()>;
}
pub async fn init() -> Option<(Connection, PowerDaemonProxy<'static>)> {
let conn = match Connection::system().await {
Ok(conn) => conn,
_ => return None,
};
let proxy = match PowerDaemonProxy::new(&conn).await {
Ok(p) => p,
_ => return None,
};
Some((conn, proxy))
}

View file

@ -2,7 +2,7 @@
use crate::dbus::PowerDaemonProxy;
use zbus::Result;
#[derive(PartialEq, Eq)]
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Graphics {
Integrated,
Hybrid,
@ -10,7 +10,7 @@ pub enum Graphics {
Compute,
}
pub async fn get_current_graphics(daemon: &PowerDaemonProxy<'_>) -> Result<Graphics> {
pub async fn get_current_graphics(daemon: PowerDaemonProxy<'_>) -> Result<Graphics> {
let graphics = daemon.get_graphics().await?;
match graphics.as_str() {
"integrated" => Ok(Graphics::Integrated),
@ -21,7 +21,7 @@ pub async fn get_current_graphics(daemon: &PowerDaemonProxy<'_>) -> Result<Graph
}
}
pub async fn set_graphics(daemon: &PowerDaemonProxy<'_>, graphics: Graphics) -> Result<()> {
pub async fn set_graphics(daemon: PowerDaemonProxy<'_>, graphics: Graphics) -> Result<()> {
let graphics_str = match graphics {
Graphics::Integrated => "integrated",
Graphics::Hybrid => "hybrid",

View file

@ -1,178 +1,37 @@
// SPDX-License-Identifier: GPL-3.0-or-later
mod dbus;
mod graphics;
mod window;
#![allow(unused_parens, clippy::double_parens)] // needed for a quirk in the view! macro
#[macro_use]
extern crate relm4_macros;
pub mod dbus;
pub mod graphics;
pub mod mode_box;
use self::{dbus::PowerDaemonProxy, graphics::Graphics, mode_box::ModeSelection};
use gtk4::{
glib::{self, clone, MainContext, PRIORITY_DEFAULT},
prelude::*,
Align, Label, ListBox, ListBoxRow, Orientation, Overlay, Separator, Spinner,
use cosmic::{
iced::{sctk_settings::InitialSurface, Application},
iced_native::command::platform_specific::wayland::window::SctkWindowSettings,
iced_native::window::Settings,
settings,
};
use once_cell::sync::Lazy;
use tokio::runtime::Runtime;
use cosmic_panel_config::PanelSize;
use window::*;
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime"));
async fn get_current_graphics() -> zbus::Result<Graphics> {
let connection = zbus::Connection::system().await?;
let proxy = PowerDaemonProxy::new(&connection).await?;
graphics::get_current_graphics(&proxy).await
}
async fn set_graphics(graphics_mode: Graphics) -> zbus::Result<()> {
let connection = zbus::Connection::system().await?;
let proxy = PowerDaemonProxy::new(&connection).await?;
graphics::set_graphics(&proxy, graphics_mode).await
}
fn row_clicked(_: &ListBox, row: &ListBoxRow) {
let child = row.child().expect("UNEXPECTED: row has no child");
let selector = child
.downcast::<ModeSelection>()
.expect("UNEXPECTED: child is not a mode selector");
selector.emit_activate();
}
fn main() {
let _monitors = libcosmic::init();
let provider = gtk4::CssProvider::new();
provider.load_from_data(include_bytes!("style.css"));
gtk4::StyleContext::add_provider_for_display(
&gtk4::gdk::Display::default().expect("Could not connect to a display."),
&provider,
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
let current_graphics = RT
.block_on(get_current_graphics())
.expect("failed to connect to system76-power");
let (tx, rx) = MainContext::channel::<bool>(PRIORITY_DEFAULT);
view! {
window = libcosmic_applet::AppletWindow {
set_title: Some("COSMIC Graphics Applet"),
#[wrap(Some)]
set_child = &libcosmic_applet::AppletButton {
set_button_icon_name: "input-gaming-symbolic",
#[wrap(Some)]
set_popover_child: main_overlay = &Overlay {
add_overlay: loading_box = &gtk4::Box {
append: loading_explain_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_halign: Align::Center,
set_valign: Align::Center,
append: loading_spinner = &Spinner {
set_halign: Align::Center,
},
append: loading_explain = &Label {
set_label: "Please wait while your graphics mode is set...",
set_halign: Align::Center,
},
},
set_halign: Align::Center,
set_valign: Align::Center,
set_hexpand: true,
set_vexpand: true,
set_visible: false,
add_css_class: "loading-overlay",
},
#[wrap(Some)]
set_child: main_box = &gtk4::Box {
set_orientation: Orientation::Vertical,
set_spacing: 10,
set_margin_top: 20,
set_margin_bottom: 20,
set_margin_start: 24,
set_margin_end: 24,
append: mode_label = &Label {
set_text: "Graphics Mode"
},
append: separator = &Separator {
set_orientation: Orientation::Horizontal
},
append: graphics_modes_list = &ListBox {
connect_row_activated: row_clicked,
append: integrated_selector = &ModeSelection {
set_title: "Integrated Graphics",
set_description: "Disables external displays. Requires Restart.",
set_active: (current_graphics == Graphics::Integrated),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Integrated).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: nvidia_selector = &ModeSelection {
set_title: "NVIDIA Graphics",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Nvidia),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Nvidia).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: hybrid_selector = &ModeSelection {
set_title: "Hybrid Graphics",
set_description: "Requires Restart.",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Hybrid),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Hybrid).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
append: compute_selector = &ModeSelection {
set_title: "Compute Graphics",
set_description: "Disables external displays. Requires Restart.",
set_group: Some(&integrated_selector),
set_active: (current_graphics == Graphics::Compute),
connect_toggled: clone!(@strong tx => move |_| {
tx.send(true).expect("failed to send to main context");
let tx = tx.clone();
RT.spawn(async move {
set_graphics(Graphics::Compute).await.expect("failed to set graphics mode");
tx.send(false).expect("failed to send to main context");
});
})
},
}
}
}
}
}
}
rx.attach(
None,
clone!(@weak loading_box, @weak loading_spinner => @default-return Continue(true), move |val| {
loading_box.set_visible(val);
loading_spinner.set_spinning(val);
Continue(true)
}),
);
window.show();
let main_loop = glib::MainLoop::new(None, false);
main_loop.run();
pub fn main() -> cosmic::iced::Result {
let mut settings = settings();
let pixels = std::env::var("COSMIC_PANEL_SIZE")
.ok()
.and_then(|size| match size.parse::<PanelSize>() {
Ok(PanelSize::XL) => Some(64),
Ok(PanelSize::L) => Some(48),
Ok(PanelSize::M) => Some(36),
Ok(PanelSize::S) => Some(24),
Ok(PanelSize::XS) => Some(18),
Err(_) => Some(36),
})
.unwrap_or(36);
settings.initial_surface = InitialSurface::XdgWindow(SctkWindowSettings {
iced_settings: Settings {
size: (pixels + 32, pixels + 16),
min_size: Some((pixels + 32, pixels + 16)),
max_size: Some((pixels + 32, pixels + 16)),
..Default::default()
},
..Default::default()
});
Window::run(settings)
}

View file

@ -1,115 +0,0 @@
// SPDX-License-Identifier: GPL-3.0-or-later
use gtk4::{
glib::{self, Object},
prelude::*,
subclass::prelude::*,
Align, CheckButton, Label, Orientation,
};
glib::wrapper! {
pub struct ModeSelection(ObjectSubclass<ModeSelectionImp>)
@extends gtk4::Widget,
@implements gtk4::Accessible;
}
impl ModeSelection {
pub fn new() -> Self {
Self::default()
}
pub fn set_title(&self, title: &str) {
self.inner().label.set_text(title);
}
pub fn set_description(&self, title: &str) {
let inner = self.inner();
inner.description.set_text(title);
inner.description.show();
}
pub fn is_active(&self) -> bool {
self.inner().check.is_active()
}
pub fn set_active(&self, setting: bool) {
self.inner().check.set_active(setting)
}
pub fn set_group(&self, group: Option<&ModeSelection>) {
self.inner()
.check
.set_group(group.map(|x| &x.inner().check))
}
pub fn connect_toggled<F: Fn(&CheckButton) + 'static>(&self, f: F) {
self.inner().check.connect_toggled(f);
}
pub fn emit_activate(&self) {
self.inner().check.emit_activate();
}
fn inner(&self) -> &ModeSelectionImp {
ModeSelectionImp::from_instance(self)
}
}
impl Default for ModeSelection {
fn default() -> Self {
Object::new(&[]).expect("Failed to create `ModeSelection`.")
}
}
#[derive(Debug, Default)]
pub struct ModeSelectionImp {
inner_box: gtk4::Box,
label_box: gtk4::Box,
label: Label,
description: Label,
check: CheckButton,
}
#[glib::object_subclass]
impl ObjectSubclass for ModeSelectionImp {
const NAME: &'static str = "ModeSelection";
type Type = ModeSelection;
type ParentType = gtk4::Widget;
fn class_init(klass: &mut Self::Class) {
klass.set_layout_manager_type::<gtk4::BinLayout>();
}
}
impl ObjectImpl for ModeSelectionImp {
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
self.check.set_halign(Align::End);
self.label.set_halign(Align::Start);
self.label.add_css_class("title");
self.description.set_halign(Align::Start);
self.description.add_css_class("description");
self.description.hide();
self.label_box.set_orientation(Orientation::Vertical);
self.label_box.set_hexpand(true);
self.label_box.append(&self.label);
self.label_box.append(&self.description);
self.inner_box.set_orientation(Orientation::Horizontal);
self.inner_box.append(&self.label_box);
self.inner_box.append(&self.check);
self.inner_box.set_parent(obj);
}
fn dispose(&self, _obj: &Self::Type) {
self.inner_box.remove(&self.label);
self.inner_box.remove(&self.check);
self.inner_box.unparent();
}
}
impl WidgetImpl for ModeSelectionImp {}

View file

@ -1,4 +0,0 @@
.loading-overlay {
background-color: #2f2f2f;
opacity: 0.85;
}

View file

@ -0,0 +1,287 @@
use crate::dbus::{self, PowerDaemonProxy};
use crate::graphics::{get_current_graphics, set_graphics, Graphics};
use cosmic::applet::{get_popup_settings, icon_button, popup_container};
use cosmic::iced_style::application::{self, Appearance};
use cosmic::separator;
use cosmic::{
iced::widget::{column, radio, text},
iced::{self, Application, Command, Length},
iced_native::window,
theme::{self, Theme},
Element,
};
use cosmic_panel_config::{PanelAnchor, PanelSize};
use iced_sctk::alignment::Horizontal;
use iced_sctk::application::SurfaceIdWrapper;
use iced_sctk::commands::popup::{destroy_popup, get_popup};
use iced_sctk::Color;
use zbus::Connection;
#[derive(Clone, Copy)]
enum State {
SelectGraphicsMode(bool),
SettingGraphicsMode(Graphics),
}
#[derive(Clone, Copy)]
enum GraphicsMode {
SelectedGraphicsMode(Graphics),
CurrentGraphicsMode(Graphics),
}
impl GraphicsMode {
fn inner(&self) -> Graphics {
match self {
GraphicsMode::SelectedGraphicsMode(g) => *g,
GraphicsMode::CurrentGraphicsMode(g) => *g,
}
}
}
impl Default for State {
fn default() -> Self {
Self::SelectGraphicsMode(false)
}
}
#[derive(Default)]
pub struct Window {
popup: Option<window::Id>,
graphics_mode: Option<GraphicsMode>,
id_ctr: u32,
icon_size: u16,
anchor: PanelAnchor,
theme: Theme,
dbus: Option<(Connection, PowerDaemonProxy<'static>)>,
state: State,
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum Message {
CurrentGraphics(Option<Graphics>),
SelectedGraphicsMode(Option<Graphics>),
DBusInit(Option<(Connection, PowerDaemonProxy<'static>)>),
SelectGraphicsMode(Graphics),
TogglePopup,
PopupClosed(window::Id),
}
impl Application for Window {
type Executor = iced::executor::Default;
type Flags = ();
type Message = Message;
type Theme = Theme;
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
let mut window = Window::default();
let pixels = std::env::var("COSMIC_PANEL_SIZE")
.ok()
.and_then(|size| match size.parse::<PanelSize>() {
Ok(PanelSize::XL) => Some(64),
Ok(PanelSize::L) => Some(36),
Ok(PanelSize::M) => Some(24),
Ok(PanelSize::S) => Some(16),
Ok(PanelSize::XS) => Some(12),
Err(_) => Some(12),
})
.unwrap_or(16);
window.icon_size = pixels;
window.anchor = std::env::var("COSMIC_PANEL_ANCHOR")
.ok()
.map(|size| match size.parse::<PanelAnchor>() {
Ok(p) => p,
Err(_) => PanelAnchor::Top,
})
.unwrap_or(PanelAnchor::Top);
(
window,
Command::perform(dbus::init(), |dbus_init| Message::DBusInit(dbus_init)),
)
}
fn title(&self) -> String {
String::from("Cosmic Graphics Applet")
}
fn update(&mut self, message: Message) -> iced::Command<Self::Message> {
match message {
Message::SelectGraphicsMode(new_graphics_mode) => {
if let Some((_, proxy)) = self.dbus.as_ref() {
self.state = State::SettingGraphicsMode(new_graphics_mode);
return Command::perform(
set_graphics(proxy.clone(), new_graphics_mode),
move |success| {
Message::SelectedGraphicsMode(success.ok().map(|_| new_graphics_mode))
},
);
}
}
Message::SelectedGraphicsMode(g) => {
if let Some(g) = g {
self.graphics_mode
.replace(GraphicsMode::SelectedGraphicsMode(g));
self.state = State::SelectGraphicsMode(true);
}
}
Message::TogglePopup => {
if let Some(p) = self.popup.take() {
return destroy_popup(p);
} else {
self.id_ctr += 1;
let new_id = window::Id::new(self.id_ctr);
self.popup.replace(new_id);
let mut commands = Vec::new();
if let Some((_, proxy)) = self.dbus.as_ref() {
commands.push(Command::perform(
get_current_graphics(proxy.clone()),
|cur_graphics| Message::CurrentGraphics(cur_graphics.ok()),
));
}
let popup_settings =
get_popup_settings(window::Id::new(0), new_id, (200, 240), None, None);
commands.push(get_popup(popup_settings));
return Command::batch(commands);
}
}
Message::DBusInit(dbus) => {
self.dbus = dbus;
return Command::perform(
get_current_graphics(self.dbus.as_ref().unwrap().1.clone()),
|cur_graphics| {
Message::CurrentGraphics(match cur_graphics {
Ok(g) => Some(g),
Err(err) => {
dbg!(err);
None
}
})
},
);
}
Message::CurrentGraphics(g) => {
if let Some(g) = g {
self.graphics_mode = Some(match self.graphics_mode.take() {
Some(GraphicsMode::CurrentGraphicsMode(_)) | None => {
GraphicsMode::CurrentGraphicsMode(g)
}
Some(g) => g,
});
}
}
Message::PopupClosed(id) => {
if self.popup.as_ref() == Some(&id) {
self.popup = None;
}
},
}
Command::none()
}
fn view(&self, id: SurfaceIdWrapper) -> Element<Message> {
match id {
SurfaceIdWrapper::LayerSurface(_) => unimplemented!(),
SurfaceIdWrapper::Window(_) => icon_button().on_press(Message::TogglePopup).into(),
SurfaceIdWrapper::Popup(_) => {
let content = match self.state {
State::SelectGraphicsMode(pending_restart) => {
let mut content_list = vec![
radio(
"Integrated Graphics",
Graphics::Integrated,
self.graphics_mode.map(|g| g.inner()),
|g| Message::SelectGraphicsMode(g),
)
.into(),
radio(
"Nvidia Graphics",
Graphics::Nvidia,
self.graphics_mode.map(|g| g.inner()),
|g| Message::SelectGraphicsMode(g),
)
.into(),
radio(
"Hybrid Graphics",
Graphics::Hybrid,
self.graphics_mode.map(|g| g.inner()),
|g| Message::SelectGraphicsMode(g),
)
.into(),
radio(
"Compute Graphics",
Graphics::Compute,
self.graphics_mode.map(|g| g.inner()),
|g| Message::SelectGraphicsMode(g),
)
.into(),
];
if pending_restart {
content_list.insert(
0,
text("Restart to apply changes")
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.size(16)
.into(),
)
}
column(content_list).padding([8, 0]).spacing(8).into()
}
State::SettingGraphicsMode(graphics) => {
let graphics_str = match graphics {
Graphics::Integrated => "integrated",
Graphics::Hybrid => "hybrid",
Graphics::Nvidia => "nvidia",
Graphics::Compute => "compute",
};
column(vec![text(format!(
"Setting graphics mode to {graphics_str}..."
))
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.into()])
.into()
}
};
popup_container(
column(vec![
text("Graphics Mode")
.width(Length::Fill)
.horizontal_alignment(Horizontal::Center)
.size(24)
.into(),
separator!(1).into(),
content,
])
.padding(4)
.spacing(4),
)
.into()
},
}
}
fn close_requested(&self, id: SurfaceIdWrapper) -> Self::Message {
match id {
SurfaceIdWrapper::LayerSurface(_) | SurfaceIdWrapper::Window(_) => unimplemented!(),
SurfaceIdWrapper::Popup(id) => {
Message::PopupClosed(id)
},
}
}
fn style(&self) -> <Self::Theme as application::StyleSheet>::Style {
<Self::Theme as application::StyleSheet>::Style::Custom(|theme| Appearance {
background_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
text_color: theme.cosmic().on_bg_color().into(),
})
}
fn should_exit(&self) -> bool {
false
}
fn theme(&self) -> Theme {
self.theme
}
}

View file

@ -1,7 +1,7 @@
//! Utilities for using an [`EventQueue`] from wayland-client with an event loop that performs polling with
//! [`calloop`](https://crates.io/crates/calloop).
use std::{io, os::unix::prelude::RawFd};
use std::{io, os::unix::prelude::{RawFd, AsRawFd}};
use calloop::{
generic::Generic, EventSource, InsertError, Interest, LoopHandle, Mode, Poll, PostAction,
@ -30,7 +30,7 @@ impl<D> WaylandSource<D> {
/// Wrap an [`EventQueue`] as a [`WaylandSource`].
pub fn new(queue: EventQueue<D>) -> Result<WaylandSource<D>, WaylandError> {
let guard = queue.prepare_read()?;
let fd = Generic::new(guard.connection_fd(), Interest::READ, Mode::Level);
let fd = Generic::new(guard.connection_fd().as_raw_fd(), Interest::READ, Mode::Level);
drop(guard);
Ok(WaylandSource {
@ -192,12 +192,12 @@ impl<D> WaylandSource<D> {
break Err(Errno::EPROTO.into());
}
Err(DispatchError::BadMessage { msg, interface }) => {
Err(DispatchError::BadMessage { sender_id, interface, opcode }) => {
log::error!(
"Bad message on interface \"{}\": (opcode: {}, args: {:?})",
"Bad message on interface \"{}\": (opcode: {}, sender_id: {:?})",
interface,
msg.opcode,
msg.args,
opcode,
sender_id,
);
break Err(Errno::EPROTO.into());

7
debian/rules vendored
View file

@ -13,6 +13,7 @@ override_dh_shlibdeps:
override_dh_auto_clean:
if test "${CLEAN}" = "1"; then \
cargo clean; \
cargo clean --manifest-path applets/cosmic-applet-graphics/Cargo.toml; \
fi
if ! ischroot && test "${VENDOR}" = "1"; then \
@ -21,6 +22,12 @@ override_dh_auto_clean:
echo 'directory = "vendor"' >> .cargo/config; \
tar pcf vendor.tar vendor; \
rm -rf vendor; \
cd applets/cosmic-applet-graphics/; \
mkdir -p .cargo; \
cargo vendor --sync Cargo.toml | head -n -1 > .cargo/config; \
echo 'directory = "vendor"' >> .cargo/config; \
tar pcf vendor.tar vendor; \
rm -rf vendor; \
fi
override_dh_auto_build:

View file

@ -27,6 +27,10 @@ app_button_id := 'com.system76.CosmicPanelAppButton'
workspaces_button_id := 'com.system76.CosmicPanelWorkspacesButton'
all: _extract_vendor
#!/usr/bin/env bash
pushd applets/cosmic-applet-graphics/
cargo build {{cargo_args}}
popd
cargo build {{cargo_args}}
# Installs files into the system
@ -47,12 +51,6 @@ install:
install -Dm0644 applets/cosmic-applet-battery/data/icons/{{battery_id}}.svg {{iconsdir}}/{{battery_id}}.svg
install -Dm0644 applets/cosmic-applet-battery/data/{{battery_id}}.desktop {{sharedir}}/applications/{{battery_id}}.desktop
install -Dm0755 target/release/cosmic-applet-battery {{bindir}}/cosmic-applet-battery
# graphics
install -Dm0644 applets/cosmic-applet-graphics/data/icons/{{graphics_id}}.svg {{iconsdir}}/{{graphics_id}}.svg
install -Dm0644 applets/cosmic-applet-graphics/data/{{graphics_id}}.desktop {{sharedir}}/applications/{{graphics_id}}.desktop
install -Dm0755 target/release/cosmic-applet-graphics {{bindir}}/cosmic-applet-graphics
# network
install -Dm0644 applets/cosmic-applet-network/data/icons/{{network_id}}.svg {{iconsdir}}/{{network_id}}.svg
install -Dm0644 applets/cosmic-applet-network/data/{{network_id}}.desktop {{sharedir}}/applications/{{network_id}}.desktop
@ -94,9 +92,16 @@ install:
# panel button
install -Dm0755 target/release/cosmic-panel-button {{bindir}}/cosmic-panel-button
# graphics
install -Dm0644 applets/cosmic-applet-graphics/data/icons/{{graphics_id}}.svg {{iconsdir}}/{{graphics_id}}.svg
install -Dm0644 applets/cosmic-applet-graphics/data/{{graphics_id}}.desktop {{sharedir}}/applications/{{graphics_id}}.desktop
install -Dm0755 applets/cosmic-applet-graphics/target/release/cosmic-applet-graphics {{bindir}}/cosmic-applet-graphics
# Extracts vendored dependencies if vendor=1
_extract_vendor:
#!/usr/bin/env sh
if test {{vendor}} = 1; then
rm -rf vendor; tar pxf vendor.tar
rm -rf applets/cosmic-applet-graphics/vendor; tar xf applets/cosmic-applet-graphics/vendor.tar --directory applets/cosmic-applet-graphics
fi

View file

@ -1 +1 @@
1.63
1.65