Compare commits

..

No commits in common. "bfe657faa016ccf747b16d2bcfcde6b6cd627894" and "bf630a4b572e937b56851cc1088e7563f64d0b34" have entirely different histories.

18 changed files with 751 additions and 1703 deletions

1668
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -80,15 +80,12 @@ cosmic-randr-shell = { workspace = true }
kdl.workspace = true
color-eyre.workspace = true
image = { version = "0.25", default-features = false, features = [
"avif-native",
"jpeg",
"png",
"rayon",
"webp",
"hdr",
] }
# JPEG-XL support
jxl-oxide = { version = "0.12.6", features = ["image"] }
[dependencies.greetd_ipc]
version = "0.10.3"

1
debian/control vendored
View file

@ -7,7 +7,6 @@ Build-Depends:
git,
just (>= 1.13.0),
libclang-dev,
libdav1d-dev,
libinput-dev,
libpam-dev,
libwayland-dev,

View file

@ -1,37 +0,0 @@
cancel = Annuller
restart = Genstart
caps-lock = Skiftelås er aktiv.
enter-user = Indtast navn manuelt...
type-username = Brugernavn:
keyboard-layout = Tastaturopsætning
restart-now = Genstart nu?
session = Session
shutdown = Luk ned
shutdown-now = Luk ned nu?
user = Bruger
suspend = Slumre
auth-error-default = Godkendelse fejlede. Prøv venligst igen.
auth-error-credentials = Ukorrekt adgangskode. Kontrollér venligst din tastaturopsætning og prøv igen.
auth-error-denied = Adgang nægtet.
auth-error-maxtries = For mange mislykkede godkendelsesforsøg.
auth-error-account = Konto er utilgængelig eller deaktiveret.
accessibility = Accessibility
.screen-reader = Skærmlæser
.magnifier = Forstørrelsesglas
.high-contrast = Høj kontrast
.invert-colors = Invertér Farver
authenticating = Godkender...
restart-timeout =
The system will restart automatically
{ $seconds ->
[0] now.
[1] in 1 second.
*[other] in { $seconds } seconds.
}
shutdown-timeout =
Systemet lukker automatisk ned
{ $seconds ->
[0] nu.
[1] om 1 sekund.
*[other] om { $seconds } sekunder.
}

View file

@ -27,11 +27,11 @@ accessibility = Barrierefreiheit
.magnifier = Vergrößerungsglas
.high-contrast = Hoher Kontrast
.invert-colors = Farben invertieren
enter-user = Name manuell eingeben ...
enter-user = Name manuell eingeben…
type-username = Benutzername:
auth-error-default = Authentifizierung fehlgeschlagen. Bitte versuche es erneut.
auth-error-credentials = Falsches Passwort. Bitte überprüfe deine Tastaturbelegung und versuche es erneut.
auth-error-denied = Zugriff verweigert.
auth-error-maxtries = Zu viele fehlgeschlagene Authentifizierungsversuche.
auth-error-account = Konto ist nicht verfügbar oder deaktiviert.
authenticating = Authentifizierung ...
authenticating = Authentifizierung...

View file

@ -1 +0,0 @@
cancel = Cancel

View file

@ -1 +0,0 @@
cancel = لغو

View file

@ -1,37 +0,0 @@
cancel = Cancelar
accessibility = Accesibilidade
.screen-reader = Lector de pantalla
.magnifier = Lupa
.high-contrast = Alto contraste
.invert-colors = Inverter cores
authenticating = Autentificando...
caps-lock = Bloqueo de Maiúsculas activo.
enter-user = Introducir nome manualmente...
type-username = Nome de usuario:
keyboard-layout = Distribución do teclado
restart = Reiniciar
restart-now = Reiniciar agora?
restart-timeout =
O sistema reiniciarase automaticamente
{ $seconds ->
[0] agora.
[1] dentro de 1 segundo.
*[other] dentro de { $seconds } segundos.
}
session = Sesión
shutdown = Apagar
shutdown-now = Apagar agora?
shutdown-timeout =
O sistema apagarase automaticamente
{ $seconds ->
[0] agora.
[1] dentro de 1 segundo.
*[other] dentro de { $seconds } segundos.
}
suspend = Suspender
user = Usuario
auth-error-default = A autenticación fallou. Téntao de novo.
auth-error-credentials = Contrasinal incorrecto. Comproba a distribución do teclado e téntao de novo.
auth-error-denied = Acceso denegado.
auth-error-maxtries = Demasiados intentos de autenticación.
auth-error-account = A conta non estña dispoñible ou está deshabilitada.

View file

@ -1,15 +0,0 @@
shutdown = Atudar
restart-timeout =
Lo sistèma reaviarà automaticament
{ $seconds ->
[0] ara.
[1] d’aquí 1 segondas.
*[other] d’aquí { $seconds } segondas.
}
shutdown-timeout =
Lo sistèma s'atudarà automaticament
{ $seconds ->
[0] ara.
[1] d’aquí 1 segondas.
*[other] d’aquí { $seconds } segondas.
}

View file

@ -29,7 +29,7 @@ shutdown-timeout =
}
suspend = Suspender
user = Usuário
authenticating = Autenticando...
authenticating = Autenticando…
auth-error-default = A autenticação falhou. Tente novamente.
auth-error-credentials = Senha incorreta. Verifique o layout do seu teclado e tente novamente.
auth-error-denied = Acesso negado.

View file

@ -1 +0,0 @@
cancel = ᱵᱟᱹᱛᱤᱞ

View file

@ -3,21 +3,14 @@ use cosmic::iced::core::SmolStr;
use cosmic::iced::event::wayland::{Event as WaylandEvent, OutputEvent, SessionLockEvent};
use cosmic::iced::event::{self};
use cosmic::iced::keyboard::{Event as KeyEvent, Key, Modifiers};
use cosmic::iced::platform_specific::shell::commands::blur::blur;
use cosmic::iced::runtime::core::window::Id as SurfaceId;
use cosmic::iced::runtime::platform_specific::wayland::CornerRadius;
use cosmic::iced::{self, Rectangle, Size, Subscription};
use cosmic::surface::corner_radius::rounded_rect_strips;
use cosmic::widget::rectangle_tracker::{RectangleUpdate, rectangle_tracker_subscription};
use cosmic::widget::{self, RectangleTracker};
use cosmic_greeter_daemon::{BgSource, UserData};
use cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1;
use std::collections::{HashMap, HashSet};
use cosmic::widget;
use cosmic_config::{ConfigSet, CosmicConfigEntry};
use cosmic_greeter_daemon::{BgSource, CosmicCompConfig, UserData};
use std::collections::HashMap;
use std::sync::Arc;
use wayland_client::protocol::wl_output::WlOutput;
use wayland_client::{Connection, Proxy};
use crate::keyboard_layout_wayland;
pub const DEFAULT_MENU_ITEM_HEIGHT: f32 = 36.;
@ -29,15 +22,13 @@ pub struct ActiveLayout {
}
pub struct Common<M> {
pub wayland_connection: Option<Connection>,
pub keyboard_layout: Option<ZcosmicKeyboardLayoutV1>,
pub current_keyboard_layout: usize,
pub active_layouts: Vec<ActiveLayout>,
pub active_surface_id_opt: Option<SurfaceId>,
pub on_battery: bool,
pub battery_percent: f64,
pub charging_limit: Option<bool>,
pub caps_lock: bool,
pub comp_config_handler: Option<cosmic_config::Config>,
pub core: Core,
pub error_opt: Option<String>,
pub fallback_background: widget::image::Handle,
@ -48,12 +39,8 @@ pub struct Common<M> {
pub output_names: HashMap<WlOutput, String>,
pub power_info_opt: Option<(widget::Icon, f64)>,
pub prompt_opt: Option<(String, bool, Option<String>)>,
pub rectangle_tracker: Option<RectangleTracker<(SurfaceId, bool)>>,
pub rectangles: HashMap<(SurfaceId, bool), iced::Rectangle>,
pub include_menu: bool,
pub subsurface_rects: HashMap<WlOutput, Rectangle>,
pub surface_ids: HashMap<WlOutput, SurfaceId>,
pub subsurface_outputs: HashMap<SurfaceId, WlOutput>,
pub surface_images: HashMap<SurfaceId, widget::image::Handle>,
pub surface_names: HashMap<SurfaceId, String>,
pub text_input_ids: HashMap<String, widget::Id>,
@ -67,15 +54,12 @@ pub enum Message {
Focus(SurfaceId),
Key(Modifiers, Key, Option<SmolStr>),
NetworkIcon(Option<&'static str>),
SubsurfaceOpened(SurfaceId),
OutputEvent(OutputEvent, WlOutput),
PowerInfo(Option<(f64, bool, bool)>),
Prompt(String, bool, Option<String>),
SessionLockEvent(SessionLockEvent),
Tick,
Tz(jiff::tz::TimeZone),
Rectangle(RectangleUpdate<(SurfaceId, bool)>),
KeyboardLayoutWayland(keyboard_layout_wayland::Event),
}
impl<M: From<Message> + Send + 'static> Common<M> {
@ -88,6 +72,17 @@ impl<M: From<Message> + Send + 'static> Common<M> {
core.window.show_minimize = false;
core.window.use_template = false;
let comp_config_handler = match cosmic_config::Config::new(
"com.system76.CosmicComp",
CosmicCompConfig::VERSION,
) {
Ok(config_handler) => Some(config_handler),
Err(err) => {
tracing::error!("failed to create cosmic-comp config handler: {}", err);
None
}
};
let layouts_opt = match xkb_data::all_keyboard_layouts() {
Ok(ok) => Some(Arc::new(ok)),
Err(err) => {
@ -97,12 +92,10 @@ impl<M: From<Message> + Send + 'static> Common<M> {
};
let app = Self {
wayland_connection: None,
keyboard_layout: None,
current_keyboard_layout: 0,
active_layouts: Vec::new(),
active_surface_id_opt: None,
caps_lock: false,
comp_config_handler,
core,
error_opt: None,
fallback_background: widget::image::Handle::from_bytes(
@ -125,10 +118,6 @@ impl<M: From<Message> + Send + 'static> Common<M> {
battery_percent: 0.0,
on_battery: false,
charging_limit: None,
subsurface_outputs: HashMap::new(),
rectangle_tracker: None,
rectangles: HashMap::new(),
include_menu: false,
};
(
app,
@ -139,6 +128,27 @@ impl<M: From<Message> + Send + 'static> Common<M> {
)
}
pub fn set_xkb_config(&self, user_data: &UserData) {
if let Some(mut xkb_config) = user_data.xkb_config_opt.clone() {
xkb_config.layout = String::new();
xkb_config.variant = String::new();
for (i, layout) in self.active_layouts.iter().enumerate() {
if i > 0 {
xkb_config.layout.push(',');
xkb_config.variant.push(',');
}
xkb_config.layout.push_str(&layout.layout);
xkb_config.variant.push_str(&layout.variant);
}
if let Some(comp_config_handler) = &self.comp_config_handler {
match comp_config_handler.set("xkb_config", xkb_config) {
Ok(()) => tracing::info!("updated cosmic-comp xkb_config"),
Err(err) => tracing::error!("failed to update cosmic-comp xkb_config: {}", err),
}
}
}
}
pub fn update_wallpapers(&mut self, user_data: &UserData) {
for (_output, surface_id) in self.surface_ids.iter() {
if self.surface_images.contains_key(surface_id) {
@ -277,11 +287,6 @@ impl<M: From<Message> + Send + 'static> Common<M> {
network_icon_opt.map(|name| widget::icon::from_name(name).into());
}
Message::OutputEvent(output_event, output) => {
if self.wayland_connection.is_none() {
if let Some(backend) = output.backend().upgrade() {
self.wayland_connection = Some(Connection::from_backend(backend));
}
}
if let Some(on_output_event) = &self.on_output_event {
return Task::done(cosmic::Action::App(on_output_event(output_event, output)));
}
@ -317,88 +322,13 @@ impl<M: From<Message> + Send + 'static> Common<M> {
Message::Tz(tz) => {
self.time.set_tz(tz);
}
Message::SubsurfaceOpened(id) => {
return self.blur_rects(id);
}
Message::Rectangle(u) => match u {
RectangleUpdate::Rectangle(r) => {
self.rectangles.insert(r.0, r.1);
return self.blur_rects(r.0.0);
}
RectangleUpdate::Init(tracker) => {
self.rectangle_tracker.replace(tracker);
}
},
Message::KeyboardLayoutWayland(w) => match w {
keyboard_layout_wayland::Event::KeyboardLayout(keyboard_layout) => {
self.keyboard_layout = Some(keyboard_layout);
}
keyboard_layout_wayland::Event::Group(group) => {
self.current_keyboard_layout = group;
}
},
}
Task::none()
}
pub(crate) fn dropdown_blur_rects(&mut self, enable: bool) -> Task<M> {
let mut ids = HashSet::new();
for r in self.rectangles.keys() {
ids.insert(r.0);
}
self.include_menu = enable;
Task::batch(
ids.into_iter()
.map(|i| self.blur_rects(i))
.collect::<Vec<_>>(),
)
}
pub fn blur_rects(&mut self, id: SurfaceId) -> Task<M> {
if let Some(output) = self.subsurface_outputs.get(&id) {
if let Some(rect) = self.subsurface_rects.get(output) {
let x = rect.x;
let y = rect.y;
if let Some(rect) = self.rectangles.get(&(id, false)) {
let mut offset_rect = *rect;
offset_rect.x += x;
offset_rect.y += y;
let rad = CornerRadius {
top_left: 16,
top_right: 16,
bottom_left: 16,
bottom_right: 16,
};
let mut rects = rounded_rect_strips(offset_rect, rad);
if let Some(other_rect) = self.rectangles.get(&(id, true))
&& self.include_menu
{
let mut other = *other_rect;
other.x += x;
other.y += y;
let rad = CornerRadius {
top_left: 8,
top_right: 8,
bottom_left: 8,
bottom_right: 8,
};
rects.append(&mut rounded_rect_strips(other, rad));
}
return blur(id, Some(rects)).discard();
} else {
tracing::error!("no rectangle for surface {id:?}");
}
}
}
Task::none()
}
pub fn subscription(&self) -> Subscription<Message> {
let mut subscriptions = Vec::with_capacity(3);
subscriptions
.push(rectangle_tracker_subscription(0).map(|update| Message::Rectangle(update.1)));
subscriptions.push(event::listen_with(|event, status, id| match event {
iced::Event::Keyboard(KeyEvent::KeyPressed {
key,
@ -421,21 +351,12 @@ impl<M: From<Message> + Send + 'static> Common<M> {
WaylandEvent::SessionLock(lock_event) => {
Some(Message::SessionLockEvent(lock_event))
}
iced::event::wayland::Event::Subsurface(
iced::event::wayland::SubsurfaceEvent::Created,
) => Some(Message::SubsurfaceOpened(id)),
_ => None,
},
iced::Event::Window(iced::window::Event::Focused) => Some(Message::Focus(id)),
_ => None,
}));
if let Some(conn) = self.wayland_connection.clone() {
subscriptions.push(
keyboard_layout_wayland::subscription(conn).map(Message::KeyboardLayoutWayland),
);
}
#[cfg(feature = "networkmanager")]
{
subscriptions.push(crate::networkmanager::subscription().map(Message::NetworkIcon));

View file

@ -24,8 +24,7 @@ use cosmic::iced::platform_specific::shell::wayland::commands::subsurface::repos
use cosmic::iced::runtime::core::window::Id as SurfaceId;
use cosmic::iced::runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
use cosmic::iced::{
self, Alignment, Background, Border, Color, Length, Point, Rectangle, Size, Subscription,
window,
self, Alignment, Background, Border, Length, Point, Size, Subscription, window,
};
use cosmic::widget::{id_container, text};
use cosmic::{Element, executor, surface, theme, widget};
@ -280,8 +279,6 @@ pub fn main() -> Result<(), Box<dyn Error>> {
sessions
};
let logind_available = cfg!(feature = "logind") && crate::logind::is_available();
let flags = Flags {
user_icons: user_datas
.iter_mut()
@ -291,7 +288,6 @@ pub fn main() -> Result<(), Box<dyn Error>> {
sessions,
greeter_config,
greeter_config_handler,
logind_available,
};
let settings = Settings::default().no_main_window(true);
@ -308,7 +304,6 @@ pub struct Flags {
sessions: HashMap<String, (Vec<String>, Vec<String>)>,
greeter_config: CosmicGreeterConfig,
greeter_config_handler: Option<cosmic_config::Config>,
logind_available: bool,
}
#[derive(Clone, Debug)]
@ -542,7 +537,7 @@ impl App {
let dropdown_menu = |items: Vec<_>| {
let item_cnt = items.len();
let items = widget::menu::menu_column::MenuColumn::with_children(items);
let items = widget::column::with_children(items);
let items = if item_cnt > 7 {
Element::from(
widget::scrollable(items)
@ -552,12 +547,12 @@ impl App {
Element::from(items)
};
let menu = widget::container(items)
widget::container(items)
.padding(1)
//TODO: move style to libcosmic
.class(theme::Container::custom(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background(theme.transparent).component;
let component = &cosmic.background(false).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
@ -570,13 +565,7 @@ impl App {
..Default::default()
}
}))
.width(Length::Fixed(240.0));
if let Some(t) = self.common.rectangle_tracker.as_ref() {
Element::from(t.container((id, true), menu))
} else {
menu.into()
}
.width(Length::Fixed(240.0))
};
let mut input_button = widget::popover(
@ -590,7 +579,7 @@ impl App {
for (i, layout) in self.common.active_layouts.iter().enumerate() {
items.push(menu_checklist(
&layout.description,
i == self.common.current_keyboard_layout,
i == 0,
Message::KeyboardLayout(i),
));
}
@ -692,7 +681,7 @@ impl App {
let accessibility_button = accessibility_dropdown;
let mut button_row = iced::widget::row![
let button_row = iced::widget::row![
widget::tooltip(
accessibility_button,
text(fl!("accessibility")),
@ -713,32 +702,30 @@ impl App {
text(fl!("session")),
widget::tooltip::Position::Top
),
];
if self.flags.logind_available {
button_row = button_row
.push(widget::tooltip(
widget::button::custom(widget::icon::from_name("system-suspend-symbolic"))
.padding(12.0)
.on_press(Message::Suspend),
text(fl!("suspend")),
widget::tooltip::Position::Top,
))
.push(widget::tooltip(
widget::button::custom(widget::icon::from_name("system-reboot-symbolic"))
.padding(12.0)
.on_press(Message::Restart),
text(fl!("restart")),
widget::tooltip::Position::Top,
))
.push(widget::tooltip(
widget::button::custom(widget::icon::from_name("system-shutdown-symbolic"))
.padding(12.0)
.on_press(Message::Shutdown),
text(fl!("shutdown")),
widget::tooltip::Position::Top,
));
}
let button_row = button_row.padding([16.0, 0.0, 0.0, 0.0]).spacing(8.0);
widget::tooltip(
widget::button::custom(widget::icon::from_name("system-suspend-symbolic"))
.padding(12.0)
.on_press(Message::Suspend),
text(fl!("suspend")),
widget::tooltip::Position::Top
),
widget::tooltip(
widget::button::custom(widget::icon::from_name("system-reboot-symbolic"))
.padding(12.0)
.on_press(Message::Restart),
text(fl!("restart")),
widget::tooltip::Position::Top
),
widget::tooltip(
widget::button::custom(widget::icon::from_name("system-shutdown-symbolic"))
.padding(12.0)
.on_press(Message::Shutdown),
text(fl!("shutdown")),
widget::tooltip::Position::Top
)
]
.padding([16.0, 0.0, 0.0, 0.0])
.spacing(8.0);
widget::container(iced::widget::column![
date_time_column,
@ -941,37 +928,29 @@ impl App {
},
)
};
let menu = widget::layer_container(
iced::widget::row![left_element, right_element].align_y(Alignment::Start),
)
.layer(cosmic::cosmic_theme::Layer::Background)
.padding(16)
.class(cosmic::theme::Container::Custom(Box::new(
|theme: &cosmic::Theme| {
// Use background appearance as the base
let mut appearance =
widget::container::Catalog::style(theme, &cosmic::theme::Container::Background);
appearance.background = Some(iced::Background::Color(
// TODO if we can use popups instead of subsurfaces for the greeter and the lockscreen
// then we can allow transparency
theme.cosmic().background(theme.transparent).base.into(),
));
appearance.border = iced::Border::default().rounded(16);
appearance
},
)))
.width(Length::Fixed(800.0));
let menu = if let Some(t) = self.common.rectangle_tracker.as_ref() {
Element::from(t.container((id, false), menu))
} else {
menu.into()
};
let menu = widget::container(widget::column::with_children(vec![
widget::space::vertical()
.height(Length::FillPortion(1))
.into(),
menu,
widget::layer_container(
iced::widget::row![left_element, right_element].align_y(Alignment::Start),
)
.layer(cosmic::cosmic_theme::Layer::Background)
.padding(16)
.class(cosmic::theme::Container::Custom(Box::new(
|theme: &cosmic::Theme| {
// Use background appearance as the base
let mut appearance = widget::container::Catalog::style(
theme,
&cosmic::theme::Container::Background,
);
appearance.border = iced::Border::default().rounded(16);
appearance
},
)))
.class(cosmic::theme::Container::Background)
.width(Length::Fixed(800.0))
.into(),
widget::space::vertical()
.height(Length::FillPortion(4))
.into(),
@ -980,7 +959,6 @@ impl App {
.height(Length::Fill)
.align_x(Alignment::Center);
// TODO make these opaque?
let popover = widget::popover(menu).modal(true);
match self.dialog_page_opt {
Some(DialogPage::Restart(instant)) => {
@ -1035,6 +1013,19 @@ impl App {
}
}
fn set_xkb_config(&self) {
let user_data = match self
.selected_username
.data_idx
.and_then(|i| self.flags.user_datas.get(i))
{
Some(some) => some,
None => return,
};
self.common.set_xkb_config(user_data);
}
fn update_user_data(&mut self) -> Task<Message> {
let user_data = match self
.selected_username
@ -1049,6 +1040,9 @@ impl App {
self.common.update_user_data(user_data);
// Ensure that user's xkb config is used
self.common.set_xkb_config(user_data);
if let Some(builder) = &user_data.theme_builder_opt {
self.theme_builder = builder.clone();
}
@ -1096,8 +1090,7 @@ impl cosmic::Application for App {
}
/// Creates the application, and optionally emits command on initialize.
fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Message>) {
core.set_app_type(cosmic::core::AppType::System);
fn init(core: Core, flags: Self::Flags) -> (Self, Task<Message>) {
let mut tasks = Vec::new();
let (mut common, common_task) = Common::init(core);
common.on_output_event = Some(Box::new(|output_event, output| {
@ -1258,12 +1251,7 @@ impl cosmic::Application for App {
surface_id,
Size::new(unwrapped_size.0 as f32, unwrapped_size.1 as f32),
);
self.common
.subsurface_rects
.insert(output.clone(), Rectangle::new(loc, sub_size));
self.common
.subsurface_outputs
.insert(subsurface_id, output.clone());
let msg = cosmic::surface::action::subsurface(
move |_: &mut App| SctkSubsurfaceSettings {
parent: surface_id,
@ -1342,7 +1330,6 @@ impl cosmic::Application for App {
self.selected_session = selected_session;
if self.dropdown_opt == Some(Dropdown::Session) {
self.dropdown_opt = None;
return self.common.dropdown_blur_rects(false);
}
}
Message::EnterUser(focus_input, username) => {
@ -1359,10 +1346,7 @@ impl cosmic::Application for App {
username,
};
if focus_input {
return Task::batch([
self.common.dropdown_blur_rects(false),
widget::text_input::focus(USERNAME_ID.clone()),
]);
return widget::text_input::focus(USERNAME_ID.clone());
}
}
Message::Username(username) => {
@ -1400,12 +1384,9 @@ impl cosmic::Application for App {
self.send_request(Request::CancelSession);
}
if let Some(randr_list) = self.randr_list.as_ref() {
return Task::batch([
self.common.dropdown_blur_rects(false),
self.update(Message::RandrUpdate {
randr: Arc::new(Ok(randr_list.clone())),
}),
]);
return self.update(Message::RandrUpdate {
randr: Arc::new(Ok(randr_list.clone())),
});
}
}
}
@ -1486,9 +1467,6 @@ impl cosmic::Application for App {
}
}
Message::Auth(response) => {
if response.as_deref() == Some("") {
return Task::none();
}
self.common.error_opt = None;
self.authenticating = true;
self.send_request(Request::PostAuthMessageResponse { response });
@ -1520,9 +1498,6 @@ impl cosmic::Application for App {
if let Some(handle) = self.heartbeat_handle.take() {
handle.abort();
}
if self.dropdown_opt.is_some() {
return self.common.dropdown_blur_rects(true);
}
}
Message::DialogConfirm => match self.dialog_page_opt.take() {
Some(DialogPage::Restart(_)) => {
@ -1549,26 +1524,20 @@ impl cosmic::Application for App {
})
.discard();
}
None => {
if self.dropdown_opt.is_some() {
return self.common.dropdown_blur_rects(true);
}
}
None => {}
},
Message::DropdownToggle(dropdown) => {
if self.dropdown_opt == Some(dropdown) {
self.dropdown_opt = None;
return self.common.dropdown_blur_rects(false);
} else {
self.dropdown_opt = Some(dropdown);
return self.common.dropdown_blur_rects(true);
}
}
Message::KeyboardLayout(layout_i) => {
if let Some(keyboard_layout) = &self.common.keyboard_layout {
keyboard_layout.set_group(layout_i as u32);
if layout_i < self.common.active_layouts.len() {
self.common.active_layouts.swap(0, layout_i);
self.set_xkb_config();
}
self.common.current_keyboard_layout = layout_i;
if self.dropdown_opt == Some(Dropdown::Keyboard) {
self.dropdown_opt = None
}
@ -1614,8 +1583,7 @@ impl cosmic::Application for App {
.abortable();
self.heartbeat_handle = Some(handle);
self.common.include_menu = false;
return Task::batch(vec![self.common.dropdown_blur_rects(false), heartbeat]);
return heartbeat;
}
}
Message::Heartbeat => match self.dialog_page_opt {

View file

@ -1,157 +0,0 @@
use cctk::{
cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1,
keyboard_layout::{KeyboardLayoutHandler, KeyboardLayoutState},
sctk::{
self,
registry::{ProvidesRegistryState, RegistryState},
seat::{Capability, SeatHandler, SeatState},
},
wayland_client::{
Connection, QueueHandle, delegate_noop,
globals::registry_queue_init,
protocol::{wl_keyboard, wl_seat},
},
};
use cosmic::iced;
use cosmic::iced::futures::{SinkExt, channel::mpsc, executor::block_on};
use std::{hash::Hash, thread};
#[derive(Clone, Debug)]
pub enum Event {
KeyboardLayout(ZcosmicKeyboardLayoutV1),
Group(usize),
}
pub fn subscription(connection: Connection) -> iced::Subscription<Event> {
#[derive(Clone)]
struct WaylandSubscription(Connection);
impl Hash for WaylandSubscription {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.backend().display_id().hash(state);
}
}
iced::Subscription::run_with(
WaylandSubscription(connection),
|WaylandSubscription(connection)| {
let connection = connection.clone();
iced::stream::channel(8, move |sender| async move {
thread::spawn(move || thread(connection, sender));
})
},
)
}
struct Keyboard {
seat: wl_seat::WlSeat,
keyboard_layout: ZcosmicKeyboardLayoutV1,
}
impl Drop for Keyboard {
fn drop(&mut self) {
self.keyboard_layout.destroy();
}
}
struct AppData {
seat_state: SeatState,
registry_state: RegistryState,
keyboard_layout_state: KeyboardLayoutState,
keyboard: Option<Keyboard>,
sender: mpsc::Sender<Event>,
}
impl KeyboardLayoutHandler for AppData {
fn group(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &wl_keyboard::WlKeyboard,
_keyboard_layout: &ZcosmicKeyboardLayoutV1,
group: u32,
) {
let _ = block_on(self.sender.send(Event::Group(group as usize)));
}
}
impl ProvidesRegistryState for AppData {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
sctk::registry_handlers![SeatState,];
}
impl SeatHandler for AppData {
fn seat_state(&mut self) -> &mut SeatState {
&mut self.seat_state
}
fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
fn new_capability(
&mut self,
_conn: &Connection,
qh: &QueueHandle<Self>,
seat: wl_seat::WlSeat,
capability: Capability,
) {
if capability == Capability::Keyboard && self.keyboard.is_none() {
let keyboard = seat.get_keyboard(qh, ());
let keyboard_layout = self
.keyboard_layout_state
.get_keyboard_layout(&keyboard, qh);
keyboard.release();
if let Some(keyboard_layout) = keyboard_layout {
let _ = block_on(
self.sender
.send(Event::KeyboardLayout(keyboard_layout.clone())),
);
self.keyboard = Some(Keyboard {
seat,
keyboard_layout,
});
} else {
keyboard.release();
}
}
}
fn remove_capability(
&mut self,
_conn: &Connection,
_: &QueueHandle<Self>,
seat: wl_seat::WlSeat,
_capability: Capability,
) {
self.keyboard.take_if(|x| x.seat == seat);
}
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
self.keyboard.take_if(|x| x.seat == seat);
}
}
fn thread(conn: Connection, sender: mpsc::Sender<Event>) {
let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
let qh = event_queue.handle();
let registry_state = RegistryState::new(&globals);
let seat_state = SeatState::new(&globals, &qh);
let keyboard_layout_state = KeyboardLayoutState::new(&registry_state, &qh);
let mut app_data = AppData {
seat_state,
registry_state,
keyboard_layout_state,
keyboard: None,
sender,
};
loop {
event_queue.blocking_dispatch(&mut app_data).unwrap();
}
}
sctk::delegate_registry!(AppData);
sctk::delegate_seat!(AppData);
cctk::delegate_keyboard_layout!(AppData);
delegate_noop!(AppData: ignore wl_keyboard::WlKeyboard);

View file

@ -8,8 +8,6 @@ mod wayland;
mod common;
mod keyboard_layout_wayland;
mod localize;
#[cfg(feature = "logind")]

View file

@ -79,8 +79,6 @@ pub fn main(user: pwd::Passwd) -> Result<(), Box<dyn std::error::Error>> {
// We are already the user at this point
user_data.load_config_as_user();
let logind_available = cfg!(feature = "logind") && crate::logind::is_available();
let flags = Flags {
user_icon: user_data
.icon_opt
@ -88,7 +86,6 @@ pub fn main(user: pwd::Passwd) -> Result<(), Box<dyn std::error::Error>> {
.map(widget::image::Handle::from_bytes),
user_data,
lockfile_opt: lockfile_opt(),
logind_available,
};
let settings = Settings::default().no_main_window(true);
@ -250,7 +247,6 @@ pub struct Flags {
user_data: UserData,
user_icon: Option<widget::image::Handle>,
lockfile_opt: Option<PathBuf>,
logind_available: bool,
}
///TODO: this is custom code that should be better handled by libcosmic
@ -376,7 +372,7 @@ impl App {
let dropdown_menu = |items: Vec<_>| {
let item_cnt = items.len();
let items = widget::menu::menu_column::MenuColumn::with_children(items);
let items = widget::column::with_children(items);
let items = if item_cnt > 7 {
Element::from(
widget::scrollable(items)
@ -386,32 +382,25 @@ impl App {
Element::from(items)
};
let menu: widget::Container<'_, Message, cosmic::prelude::Theme> =
widget::container(items)
.padding(1)
//TODO: move style to libcosmic
.class(theme::Container::custom(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background(theme.transparent).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())),
border: Border {
radius: 8.0.into(),
width: 1.0,
color: component.divider.into(),
},
..Default::default()
}
}))
.width(Length::Fixed(240.0));
if let Some(t) = self.common.rectangle_tracker.as_ref() {
Element::from(t.container((surface_id, true), menu))
} else {
menu.into()
}
widget::container(items)
.padding(1)
//TODO: move style to libcosmic
.class(theme::Container::custom(|theme| {
let cosmic = theme.cosmic();
let component = &cosmic.background(false).component;
widget::container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
background: Some(Background::Color(component.base.into())),
border: Border {
radius: 8.0.into(),
width: 1.0,
color: component.divider.into(),
},
..Default::default()
}
}))
.width(Length::Fixed(240.0))
};
let mut input_button = widget::popover(
@ -425,7 +414,7 @@ impl App {
for (i, layout) in self.common.active_layouts.iter().enumerate() {
items.push(menu_checklist(
&layout.description,
i == self.common.current_keyboard_layout,
i == 0,
Message::KeyboardLayout(i),
));
}
@ -433,7 +422,7 @@ impl App {
}
//TODO: implement these buttons
let mut button_row = iced::widget::row![
let button_row = iced::widget::row![
/*TODO: greeter accessibility options
widget::button::custom(widget::icon::from_name(
"applications-accessibility-symbolic"
@ -446,17 +435,16 @@ impl App {
widget::text(fl!("keyboard-layout")),
widget::tooltip::Position::Top
),
];
if cfg!(feature = "logind") && self.flags.logind_available {
button_row = button_row.push(widget::tooltip(
widget::tooltip(
widget::button::custom(widget::icon::from_name("system-suspend-symbolic"))
.padding(12.0)
.on_press(Message::Suspend),
widget::text(fl!("suspend")),
widget::tooltip::Position::Top,
));
}
let button_row = button_row.padding([16.0, 0.0, 0.0, 0.0]).spacing(8.0);
widget::tooltip::Position::Top
),
]
.padding([16.0, 0.0, 0.0, 0.0])
.spacing(8.0);
widget::container(iced::widget::column![
date_time_column,
@ -597,34 +585,30 @@ impl App {
.align_x(Alignment::Center)
.width(Length::Fill)
};
let menu = widget::layer_container(
iced::widget::row![left_element, right_element].align_y(Alignment::Start),
)
.layer(cosmic::cosmic_theme::Layer::Background)
.padding(16)
.class(cosmic::theme::Container::Custom(Box::new(
|theme: &cosmic::Theme| {
// Use background appearance as the base
let mut appearance =
widget::container::Catalog::style(theme, &cosmic::theme::Container::Background);
let c: iced::Color = theme.cosmic().background(theme.transparent).base.into();
appearance.background = Some(iced::Background::Color(c));
appearance.border = iced::Border::default().rounded(16.0);
appearance
},
)))
.width(Length::Fill)
.height(Length::Shrink);
let menu = if let Some(t) = self.common.rectangle_tracker.as_ref() {
Element::from(t.container((surface_id, false), menu))
} else {
menu.into()
};
widget::container(widget::column::with_children(vec![
widget::space::vertical()
.height(Length::FillPortion(1))
.into(),
menu,
widget::layer_container(
iced::widget::row![left_element, right_element].align_y(Alignment::Start),
)
.layer(cosmic::cosmic_theme::Layer::Background)
.padding(16)
.class(cosmic::theme::Container::Custom(Box::new(
|theme: &cosmic::Theme| {
// Use background appearance as the base
let mut appearance = widget::container::Catalog::style(
theme,
&cosmic::theme::Container::Background,
);
appearance.border = iced::Border::default().rounded(16.0);
appearance
},
)))
.width(Length::Fill)
.height(Length::Shrink)
.into(),
widget::space::vertical()
.height(Length::FillPortion(4))
.into(),
@ -660,8 +644,7 @@ impl cosmic::Application for App {
}
/// Creates the application, and optionally emits command on initialize.
fn init(mut core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
core.set_app_type(cosmic::core::AppType::System);
fn init(core: Core, flags: Self::Flags) -> (Self, Task<Self::Message>) {
let (mut common, common_task) = Common::init(core);
common.on_output_event = Some(Box::new(|output_event, output| {
Message::OutputEvent(output_event, output)
@ -684,18 +667,18 @@ impl cosmic::Application for App {
authenticating: false,
};
let task = if cfg!(feature = "logind") && app.flags.logind_available {
let task = if cfg!(feature = "logind") {
if already_locked {
// Recover previously locked state
tracing::info!("recovering previous locked state");
app.state = State::Locking;
lock()
} else {
// When logind is available, wait for lock signal
// When logind feature is used, wait for lock signal
Task::none()
}
} else {
// When logind is not available, lock immediately
// When logind feature not used, lock immediately
tracing::info!("locking immediately");
app.state = State::Locking;
lock()
@ -731,10 +714,6 @@ impl cosmic::Application for App {
return Task::none();
}
self.common
.subsurface_outputs
.insert(subsurface_id, output.clone());
let size = if let Some((w, h)) =
output_info_opt.as_ref().and_then(|info| info.logical_size)
{
@ -812,11 +791,11 @@ impl cosmic::Application for App {
);
if matches!(self.state, State::Locked { .. }) {
return get_lock_surface(surface_id, output).chain({
return Task::batch([get_lock_surface(surface_id, output).chain({
cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(msg),
))
});
})]);
}
}
OutputEvent::Removed => {
@ -966,15 +945,11 @@ impl cosmic::Application for App {
.zip(self.common.output_names.get(output))
{
let subsurface_id = SurfaceId::unique();
self.common
.subsurface_outputs
.insert(subsurface_id, output.clone());
let surface_id = *surface_id;
self.common.surface_names.insert(surface_id, name.clone());
self.common
.surface_names
.insert(subsurface_id, name.clone());
let msg = cosmic::surface::action::subsurface(
move |_: &mut App| SctkSubsurfaceSettings {
parent: surface_id,
@ -995,7 +970,7 @@ impl cosmic::Application for App {
cosmic::app::Action::Surface(msg),
)));
} else {
tracing::error!("no rectangle for subsurface creation...");
tracing::error!("no rectangle for subsurface...");
}
}
return Task::batch(commands);
@ -1010,11 +985,11 @@ impl cosmic::Application for App {
self.common.window_size.remove(surface_id);
commands.push(destroy_lock_surface(*surface_id));
}
if cfg!(feature = "logind") && self.flags.logind_available {
if cfg!(feature = "logind") {
return Task::batch(commands);
// When logind is available, stick around for more lock signals
// When using logind feature, stick around for more lock signals
} else {
// When logind is not available, exit immediately after unlocking
// When not using logind feature, exit immediately after unlocking
//TODO: cleaner method to exit?
process::exit(0);
}
@ -1034,10 +1009,8 @@ impl cosmic::Application for App {
Message::DropdownToggle(dropdown) => {
if self.dropdown_opt == Some(dropdown) {
self.dropdown_opt = None;
return self.common.dropdown_blur_rects(false);
} else {
self.dropdown_opt = Some(dropdown);
return self.common.dropdown_blur_rects(true);
}
}
Message::Inhibit(inhibit) => match self.state {
@ -1049,19 +1022,15 @@ impl cosmic::Application for App {
}
},
Message::KeyboardLayout(layout_i) => {
if let Some(keyboard_layout) = &self.common.keyboard_layout {
keyboard_layout.set_group(layout_i as u32);
if layout_i < self.common.active_layouts.len() {
self.common.active_layouts.swap(0, layout_i);
self.common.set_xkb_config(&self.flags.user_data);
}
self.common.current_keyboard_layout = layout_i;
if self.dropdown_opt == Some(Dropdown::Keyboard) {
self.dropdown_opt = None;
return self.common.dropdown_blur_rects(false);
self.dropdown_opt = None
}
}
Message::Submit(value) => {
if value.is_empty() {
return Task::none();
}
self.common.error_opt = None;
self.authenticating = true;
match self.value_tx_opt.take() {
@ -1220,7 +1189,8 @@ impl cosmic::Application for App {
}),
);
if cfg!(feature = "logind") && self.flags.logind_available {
#[cfg(feature = "logind")]
{
subscriptions.push(crate::logind::subscription());
}

View file

@ -13,27 +13,6 @@ use zbus::Connection;
use crate::common;
use crate::locker::Message;
/// Checks whether logind (either elogind or systemd-logind) is reachable on the system bus.
pub fn is_available() -> bool {
fn ping() -> zbus::Result<()> {
let connection = zbus::blocking::Connection::system()?;
connection.call_method(
Some("org.freedesktop.login1"),
"/org/freedesktop/login1",
Some("org.freedesktop.DBus.Peer"),
"Ping",
&(),
)?;
Ok(())
}
if let Err(err) = ping() {
tracing::info!("logind not available: {}", err);
false
} else {
true
}
}
pub async fn power_off() -> zbus::Result<()> {
let connection = Connection::system().await?;
let manager = ManagerProxy::new(&connection).await?;

View file

@ -28,9 +28,6 @@ fn main() -> Result<(), Box<dyn Error>> {
}
}
// Enable jpeg-xl support for image-rs.
let _ = jxl_oxide::integration::register_image_decoding_hook();
match pwd::Passwd::current_user() {
Some(current_user) => match current_user.name.as_str() {
"cosmic-greeter" => greeter::main(),