Compare commits

...

28 commits

Author SHA1 Message Date
57bc2dc3dc chore: fige Cargo.lock (epoch-1.9.0, [patch] locaux)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 21:03:54 +02:00
7ac6ece2ba fix: cosmic-osk-config depuis le checkout local ../cosmic-osk
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 21:03:54 +02:00
647332f1a4 merge: epoch-1.9.0 d'upstream
# Conflicts:
#	Cargo.lock
#	Cargo.toml
2026-09-24 20:48:00 +02:00
Ashley Wulber
b87331f4eb fix: for exclusive keyboard interactivity layer must be >= Top
moves osk to overlay
2026-09-22 17:10:59 -06:00
Jeremy Soller
4c1e532b69 Reduce keyboard layout max width 2026-09-22 17:10:59 -06:00
Jeremy Soller
04d61e30c5 Move greeter surface to bottom to allow OSK to show on top 2026-09-22 17:10:59 -06:00
Jeremy Soller
ce726d8470 OSK integration 2026-09-22 17:10:59 -06:00
Jeremy Soller
db39ee661d Remove zed settings and use stable rustfmt 2026-09-22 17:10:59 -06:00
Nara Díaz Viñolas
bbf553e284 fix: compilation error on greeter bin 2026-09-21 22:55:38 +02:00
Nara Díaz Viñolas
b9587deadb chore: put systemd-homed extra support behind 'systemd' feature 2026-09-21 22:55:38 +02:00
Nara Díaz Viñolas
8ff96e94f7 feat: load user icon and background from systemd-homed paths as a
fallback
2026-09-21 22:55:38 +02:00
Nara Díaz Viñolas
63c2812959 fix: add systemd-homed reported uids to user filter 2026-09-21 22:55:38 +02:00
Ashley Wulber
0b0d2925ff chore: blur updates 2026-09-16 11:12:18 -06:00
Michael Murphy
bb5c7e12a4
i18n: translation update from Hosted Weblate (#529)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pop OS/COSMIC
Greeter](https://hosted.weblate.org/projects/pop-os/cosmic-greeter/).



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pop-os/cosmic-greeter/matrix-auto.svg)
2026-09-15 21:55:52 +02:00
Hosted Weblate
31ca2e67b3
i18n: translation updates from weblate
Co-authored-by: Amadɣas <massiin@proton.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kurt Harren Catindig <kurtharrencatindig03@gmail.com>
Co-authored-by: Quentin PAGÈS <quentinantonin@free.fr>
Co-authored-by: Satakun Utama <galaxytomostore@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-greeter/kab/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-greeter/oc/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-greeter/th/
Translation: Pop OS/COSMIC Greeter
2026-09-12 13:51:38 +02:00
Ian Douglas Scott
06a762caca locker: Rely on cosmic-bg for showing background
Requires https://github.com/pop-os/cosmic-comp/pull/2792 and
https://github.com/pop-os/cosmic-bg/pull/144.
2026-09-11 14:38:19 -06:00
Hojjat
601e16af0e chore: adapt to the changes in libcosmic 2026-09-11 21:49:05 +02:00
672e07b4ad Merge tag 'epoch-1.8.0' into local/yoda-main
Epoch 1.8.0
2026-09-10 17:56:29 +02:00
Jeremy Soller
ff31b59b7d
Fix error when compiling without logind feature (#536)
`crate::logind::is_available()` function was used despite `logind`
module being gated behind `logind` feature, `cfg!(feature = "logind")`
was used wrongly here as it doesn't remove surrounding code from
compilation.

Additionally initialized nix development shell and a nix package with
support for flakes and legacy nix, let me know if it's too complicated
for this repo.

- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.
2026-09-09 10:01:53 -06:00
r0chd
14cc125fcd
fix: compile without the logind feature 2026-09-09 10:10:52 +02:00
r0chd
499f13a634
chore: add nix 2026-09-09 10:10:52 +02:00
Frederic Laing
0a16d339a4 fix: remove unused Color import 2026-09-01 15:10:56 -06:00
demi
cde1a13140
fix: handle sequential PAM prompts in greeter and locker 2026-09-01 17:04:46 +02:00
4c995b533c chore: fige Cargo.lock tel que resolu apres le merge 1.7.0
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-27 07:58:50 +02:00
117990202a Merge tag 'epoch-1.7.0' into local/yoda-main
Epoch 1.7.0
2026-08-27 07:49:24 +02:00
chris-010
935891e36b fix(greeter): do not cancel the session on a non-fatal PAM error message
greetd delivers PAM_ERROR_MSG as an auth message of type Error, and ipc.rs
turned that into Message::Error, which cancels the greetd session. But such a
message describes the current attempt, not the end of the conversation:
pam_fprintd reports "Failed to match fingerprint" this way and then retries
until max-tries is reached.

The consequence at the login screen is that a single failed fingerprint press
ends the conversation. The greeter reconnects and immediately creates a new
session, whose PAM worker calls fprintd's Claim 14 ms later while the previous
worker still holds it, so the claim is denied and no fingerprint prompt ever
appears again. The previous worker only exits when it next tries to talk to the
greeter, which can be arbitrarily far in the future, so delaying the new session
does not help either.

Per the greetd protocol every auth message has to be acknowledged with
PostAuthMessageResponse, and for a non-interactive one the response is None,
which is what the Info arm already does. Route the Error message to its own
Message::AuthError that shows the text and acknowledges it. Message::Error and
the Response::Error path stay as they are, so a failed authentication still
cancels the session.

Drafted with AI assistance (Claude Code); the change and the measurements behind
it were reviewed and tested by me on real hardware.

Signed-off-by: chris-010 <10660568+chris-010@users.noreply.github.com>
2026-08-25 02:33:15 +02:00
Michael Murphy
a133abe686
i18n: translation update from Hosted Weblate (#518)
Translations update from [Hosted Weblate](https://hosted.weblate.org)
for [Pop OS/COSMIC
Greeter](https://hosted.weblate.org/projects/pop-os/cosmic-greeter/).



Current translation status:

![Weblate translation
status](https://hosted.weblate.org/widget/pop-os/cosmic-greeter/matrix-auto.svg)
2026-08-24 20:51:07 +02:00
Hosted Weblate
c35a64e704
i18n: translation updates from weblate
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Jens Peter Nielsen <mail@jp-nielsen.dk>
Co-authored-by: lorduskordus <lorduskordus@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-greeter/cs/
Translate-URL: https://hosted.weblate.org/projects/pop-os/cosmic-greeter/da/
Translation: Pop OS/COSMIC Greeter
2026-08-22 19:35:58 +02:00
25 changed files with 1442 additions and 1040 deletions

View file

@ -1,15 +0,0 @@
{
"format_on_save": "on",
"lsp": {
"rust-analyzer": {
"initialization_options": {
"check": {
"command": "clippy",
},
"rustfmt": {
"extraArgs": ["+nightly"],
},
},
},
},
}

1680
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,7 @@ cosmic-comp-config.workspace = true
cosmic-config = { workspace = true, features = ["calloop", "macro"] }
cosmic-greeter-config.workspace = true
cosmic-greeter-daemon = { path = "daemon" }
cosmic-osk-config = { git = "https://github.com/pop-os/cosmic-osk" }
dirs = "6"
cosmic = { package = "libcosmic-yoda", path = "../libcosmic", default-features = false, features = [
"advanced-shaping",
@ -89,6 +90,7 @@ image = { version = "0.25", default-features = false, features = [
] }
# JPEG-XL support
jxl-oxide = { version = "0.12.6", features = ["image"] }
enumflags2 = "0.7.12"
[dependencies.greetd_ipc]
version = "0.10.3"
@ -171,8 +173,8 @@ iced_widget = { path = "../libcosmic/iced/widget" }
iced_winit = { path = "../libcosmic/iced/winit" }
[patch."https://github.com/pop-os/cosmic-protocols"]
cosmic-protocols = { path = "../cosmic-protocols" }
cosmic-client-toolkit = { path = "../cosmic-protocols/client-toolkit" }
cosmic-protocols = { path = "../cosmic-protocols" }
[patch."https://github.com/pop-os/cosmic-settings-daemon"]
cosmic-settings-config = { path = "../cosmic-settings-daemon/config" }
@ -187,3 +189,7 @@ dnd = { path = "../window_clipboard/dnd" }
mime = { path = "../window_clipboard/mime" }
clipboard_wayland = { path = "../window_clipboard/wayland" }
clipboard_macos = { path = "../window_clipboard/macos" }
# Yoda: cosmic-osk-config depuis le checkout local (meme source que le paquet cosmic-osk).
[patch."https://github.com/pop-os/cosmic-osk"]
cosmic-osk-config = { path = "../cosmic-osk/config" }

View file

@ -28,7 +28,8 @@ whitespace-conf = "1"
#TODO: reduce features
tokio = { workspace = true, features = ["full"] }
xdg = "3.0"
zbus_systemd = { version = "0.26000.0", features = ["home1"], optional = true }
[features]
default = ["systemd"]
systemd = ["tracing-journald"]
systemd = ["tracing-journald", "zbus_systemd"]

View file

@ -1,11 +1,12 @@
use cosmic_comp_config::output::randr;
use cosmic_config::CosmicConfigEntry;
use kdl::KdlDocument;
use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};
use std::fs;
use std::io::Read;
use std::os::unix::fs::OpenOptionsExt;
use std::path::{Path, PathBuf};
use zbus::Connection;
pub use cosmic_applets_config::time::TimeAppletConfig;
pub use cosmic_bg_config::state::State as BgState;
@ -16,12 +17,41 @@ pub use cosmic_theme::{Theme, ThemeBuilder};
pub struct UserFilter {
uid_min: u32,
uid_max: u32,
homed_uids: BTreeSet<u32>,
}
impl Default for UserFilter {
fn default() -> Self {
impl UserFilter {
#[cfg(feature = "systemd")]
async fn get_homed_uids() -> Result<BTreeSet<u32>, zbus::Error> {
use zbus_systemd::home1;
let connection = Connection::system().await?;
let homed = home1::ManagerProxy::new(&connection).await?;
let homed_uids = homed
.list_homes()
.await?
.iter()
.map(|(_, uid, ..)| *uid)
.collect();
Ok(homed_uids)
}
pub async fn new() -> Self {
let login_defs_data = fs::read_to_string("/etc/login.defs").unwrap_or_default();
let login_defs = whitespace_conf::parse(&login_defs_data);
#[cfg(feature = "systemd")]
let homed_uids = Self::get_homed_uids()
.await
.inspect_err(|e| {
tracing::warn!("failed to list dynamic UIDs from systemd-homed: {e:?}")
})
.unwrap_or_default();
#[cfg(not(feature = "systemd"))]
let homed_uids = BTreeSet::new();
Self {
uid_min: login_defs
.get("UID_MIN")
@ -31,17 +61,14 @@ impl Default for UserFilter {
.get("UID_MAX")
.and_then(|x| x.parse::<u32>().ok())
.unwrap_or(65000),
homed_uids,
}
}
}
impl UserFilter {
pub fn new() -> Self {
Self::default()
}
pub fn filter(&self, user: &pwd::Passwd) -> bool {
if user.uid < self.uid_min || user.uid > self.uid_max {
if (user.uid < self.uid_min || user.uid > self.uid_max)
&& !self.homed_uids.contains(&user.uid)
{
// Skip system accounts
return false;
}
@ -101,6 +128,48 @@ impl UserData {
}
}
fn load_icon_as_user(&mut self) {
//TODO: use accountsservice?
let icon_paths = [
//IMPORTANT: This file is owned by root and safe to read (it won't be a link to /etc/shadow for example)
// It may not exist if the user uses one of the system icons. In that case, we should read the
// information in /var/lib/AccountsService/users, and then read the icon path as the user
Path::new("/var/lib/AccountsService/icons").join(&self.name),
// systemd-homed cache
Path::new("/var/cache/systemd/home")
.join(&self.name)
.join("avatar"),
];
for icon_path in icon_paths {
match fs::OpenOptions::new()
.read(true)
// Do not follow symlinks
.custom_flags(libc::O_NOFOLLOW)
.open(&icon_path)
{
Ok(mut icon_file) => {
let mut icon_data = Vec::new();
match icon_file.read_to_end(&mut icon_data) {
Ok(count) => {
icon_data.truncate(count);
self.icon_opt = Some(icon_data);
return;
}
Err(err) => {
tracing::error!("failed to read icon data {:?}: {:?}", icon_path, err);
}
}
}
Err(err) => {
tracing::warn!("failed to open icon {:?}: {:?}", icon_path, err);
}
}
}
tracing::error!("failed to load icon for user {:?}", self.name)
}
pub fn load_config_as_user(&mut self) {
self.icon_opt = None;
self.theme_opt = None;
@ -109,33 +178,7 @@ impl UserData {
self.xkb_config_opt = None;
self.time_applet_config = Default::default();
//TODO: use accountsservice?
//IMPORTANT: This file is owned by root and safe to read (it won't be a link to /etc/shadow for example)
// It may not exist if the user uses one of the system icons. In that case, we should read the
// information in /var/lib/AccountsService/users, and then read the icon path as the user
let icon_path = Path::new("/var/lib/AccountsService/icons").join(&self.name);
match fs::OpenOptions::new()
.read(true)
// Do not follow symlinks
.custom_flags(libc::O_NOFOLLOW)
.open(&icon_path)
{
Ok(mut icon_file) => {
let mut icon_data = Vec::new();
match icon_file.read_to_end(&mut icon_data) {
Ok(count) => {
icon_data.truncate(count);
self.icon_opt = Some(icon_data);
}
Err(err) => {
tracing::error!("failed to read icon data {:?}: {:?}", icon_path, err);
}
}
}
Err(err) => {
tracing::error!("failed to open icon {:?}: {:?}", icon_path, err);
}
}
self.load_icon_as_user();
let mut is_dark = true;
match cosmic_theme::ThemeMode::config() {
@ -209,6 +252,16 @@ impl UserData {
tracing::error!("failed to create cosmic-bg state helper: {:?}", err);
}
}
if self.bg_state.wallpapers.is_empty() {
self.bg_state.wallpapers.push((
String::new(),
BgSource::Path(
Path::new("/var/cache/systemd/home")
.join(&self.name)
.join("login-background"),
),
));
}
self.load_wallpapers_as_user();
match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) {

View file

@ -70,8 +70,8 @@ struct GreeterProxy;
#[zbus::interface(name = "com.system76.CosmicGreeter")]
impl GreeterProxy {
fn get_user_data(&mut self) -> Result<String, GreeterError> {
let user_filter = UserFilter::new();
async fn get_user_data(&mut self) -> Result<String, GreeterError> {
let user_filter = UserFilter::new().await;
// The pwd::Passwd method is unsafe (but not labelled as such) due to using global state (libc pwent functions).
// To prevent issues, this should only be called once in the entire process space at a time

37
default.nix Normal file
View file

@ -0,0 +1,37 @@
{
rust-overlay ? import (import ./nix/flake-parse.nix "rust-overlay"),
pkgs ? import (import ./nix/flake-parse.nix "nixpkgs") {
system = builtins.currentSystem;
overlays = [ rust-overlay ];
},
}:
let
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
extensions = [
"rust-analyzer"
"rust-src"
"llvm-tools"
];
};
in
{
shell = import ./nix/shell.nix {
inherit pkgs rustToolchain;
};
formatter = import ./nix/formatter.nix { inherit pkgs rustToolchain; };
packages =
let
rustPlatform = pkgs.makeRustPlatform {
cargo = rustToolchain;
rustc = rustToolchain;
};
in
rec {
default = pkgs.callPackage ./nix/package.nix {
inherit rustPlatform;
};
sessiond-seat = default;
};
}

45
flake.lock generated Normal file
View file

@ -0,0 +1,45 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1788881743,
"narHash": "sha256-151taSq/21cxagiIu7hyMVl68+FbHfwBP4lh6TB6KOM=",
"rev": "d6524aaca2ff07876657ae2b323f24be4874944b",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.11pre1069856.d6524aaca2ff/nixexprs.tar.xz"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1788851328,
"narHash": "sha256-YOwlUD0Lt/3SulKhZ7z0Jmgbq/DWh8SjnGwUlFSw+YM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "6ae57a71bcb0bebc7a66cc2bd76942c4cf649167",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

84
flake.nix Normal file
View file

@ -0,0 +1,84 @@
{
inputs = {
nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
{
self,
nixpkgs,
rust-overlay,
...
}:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
pkgsFor =
system:
import nixpkgs {
inherit system;
overlays = [ rust-overlay.overlays.default ];
};
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f (pkgsFor system));
in
{
devShells = forAllSystems (pkgs: {
default =
let
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
extensions = [
"rust-analyzer"
"rust-src"
"llvm-tools"
];
};
in
import ./nix/shell.nix { inherit pkgs rustToolchain; };
});
packages = forAllSystems (
pkgs:
let
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
extensions = [
"rust-analyzer"
"rust-src"
"llvm-tools"
];
};
rustPlatform = pkgs.makeRustPlatform {
cargo = rustToolchain;
rustc = rustToolchain;
};
in
{
cosmic-greeter = pkgs.callPackage ./nix/package.nix {
inherit rustPlatform;
};
default = self.packages.${pkgs.stdenv.hostPlatform.system}.cosmic-greeter;
}
);
formatter = forAllSystems (
pkgs:
let
rustToolchain = pkgs.rust-bin.stable.latest.default.override {
extensions = [
"rust-analyzer"
"rust-src"
"llvm-tools"
];
};
in
import ./nix/formatter.nix { inherit pkgs rustToolchain; }
);
};
}

View file

@ -2,7 +2,7 @@ accessibility = Přístupnost
.screen-reader = Čtečka obrazovky
.magnifier = Lupa
.high-contrast = Vysoký kontrast
.invert-colors = Převrátit barvy
.invert-colors = Inverze barev
cancel = Zrušit
caps-lock = Caps Lock je aktivní.
enter-user = Zadat jméno ručně...

View file

@ -1,4 +1,4 @@
cancel = Annuller
cancel = Afbryd
restart = Genstart
caps-lock = Skiftelås er aktiv.
enter-user = Indtast navn manuelt...

View file

@ -9,6 +9,7 @@ caps-lock = Caps Lock is active.
enter-user = Enter name manually...
type-username = Username:
keyboard-layout = Keyboard layout
on-screen-keyboard = On-screen keyboard
restart = Restart
restart-now = Restart now?
restart-timeout = The system will restart automatically

View file

View file

@ -1,4 +1,4 @@
cancel = Sefsex
cancel = Semmet
restart-timeout =
Anagraw ad yales tanekra s wudem awurman
{ $seconds ->

View file

@ -13,3 +13,5 @@ shutdown-timeout =
[1] d’aquí 1 segondas.
*[other] d’aquí { $seconds } segondas.
}
cancel = Anullar
user = Utilizaire

View file

@ -2,10 +2,10 @@ cancel = ยกเลิก
keyboard-layout = ผังคีย์บอร์ด
restart = รีสตาร์ท
restart-now = รีสตาร์ทตอนนี้เลยหรือไม่
restart-timeout = ระบบจะรีสตาร์ทโดยอัตโนมัติภายใน {$seconds} วินาที
restart-timeout = ระบบจะรีสตาร์ทโดยอัตโนมัติภายใน { $seconds } วินาที
session = เซสชั่น
shutdown = ปิดระบบ
shutdown-now = ปิดระบบตอนนี้เลยหรือไม่
shutdown-timeout = ระบบจะปิดลงโดยอัตโนมัติภายใน {$seconds} วินาที
shutdown-timeout = ระบบจะปิดลงโดยอัตโนมัติภายใน { $seconds } วินาที
suspend = ระงับ
user = ผู้ใช้

12
nix/flake-parse.nix Normal file
View file

@ -0,0 +1,12 @@
inputName:
let
lock = builtins.fromJSON (builtins.readFile ../flake.lock);
namedNode = lock.nodes.${lock.nodes.root.inputs.${inputName}}.locked;
githubTarball = fetchTarball {
url =
namedNode.url
or "https://github.com/${namedNode.owner}/${namedNode.repo}/archive/${namedNode.rev}.tar.gz";
sha256 = namedNode.narHash;
};
in
githubTarball

17
nix/formatter.nix Normal file
View file

@ -0,0 +1,17 @@
{ pkgs, rustToolchain }:
pkgs.writeShellApplication {
name = "nix3-fmt-wrapper";
runtimeInputs = builtins.attrValues {
inherit (pkgs)
nixfmt
taplo
fd
;
inherit rustToolchain;
};
text = ''
fd "$@" -t f -e nix -x nixfmt -q '{}'
fd "$@" -t f -e toml -x taplo format '{}'
cargo fmt
'';
}

108
nix/package.nix Normal file
View file

@ -0,0 +1,108 @@
{
lib,
stdenv,
fetchFromGitHub,
rustPlatform,
libcosmicAppHook,
cmake,
just,
cosmic-randr,
dav1d,
libinput,
linux-pam,
udev,
coreutils,
xkeyboard_config,
nixosTests,
orca,
withLogind ? true,
withUpower ? true,
withNetworkManager ? true,
}:
rustPlatform.buildRustPackage (finalAttrs: {
pname = "cosmic-greeter";
version = "1.6.0";
src = fetchFromGitHub {
owner = "pop-os";
repo = "cosmic-greeter";
tag = "epoch-${finalAttrs.version}";
hash = "sha256-hDVdl2+7NVLA+YxO2HToni57IEr0i4OGTsYDc3YGTuw=";
};
postPatch = ''
substituteInPlace src/greeter.rs --replace-fail '/usr/bin/env' '${lib.getExe' coreutils "env"}'
substituteInPlace src/greeter.rs --replace-fail '/usr/bin/orca' '${lib.getExe orca}'
'';
cargoHash = "sha256-vHR9go8/iVUT7oBV8h+mmBvhi2oSKNBKtV0uoDOr6go=";
buildNoDefaultFeatures = true;
buildFeatures =
lib.optionals withLogind [ "logind" ]
++ lib.optionals withUpower [ "upower" ]
++ lib.optionals withNetworkManager [ "networkmanager" ];
separateDebugInfo = true;
__structuredAttrs = true;
env.VERGEN_GIT_SHA = finalAttrs.src.tag;
nativeBuildInputs = [
rustPlatform.bindgenHook
cmake
just
libcosmicAppHook
];
buildInputs = [
cosmic-randr
dav1d
libinput
linux-pam
udev
orca
];
dontUseJustBuild = true;
dontUseJustCheck = true;
justFlags = [
"--set"
"prefix"
(placeholder "out")
"--set"
"cargo-target-dir"
"target/${stdenv.hostPlatform.rust.cargoShortTarget}"
];
preFixup = ''
libcosmicAppWrapperArgs+=(
--prefix PATH : ${lib.makeBinPath [ cosmic-randr ]}
--set-default X11_BASE_RULES_XML ${xkeyboard_config}/share/X11/xkb/rules/base.xml
--set-default X11_BASE_EXTRA_RULES_XML ${xkeyboard_config}/share/X11/xkb/rules/extra.xml
)
'';
passthru = {
tests = {
inherit (nixosTests)
cosmic
cosmic-autologin
cosmic-noxwayland
cosmic-autologin-noxwayland
;
};
};
meta = {
homepage = "https://github.com/pop-os/cosmic-greeter";
description = "Greeter for the COSMIC Desktop Environment";
mainProgram = "cosmic-greeter";
license = lib.licenses.gpl3Only;
teams = [ lib.teams.cosmic ];
platforms = lib.platforms.linux;
};
})

31
nix/shell.nix Normal file
View file

@ -0,0 +1,31 @@
{ pkgs, rustToolchain }:
pkgs.mkShell {
name = "cosmic-greeter-devshell";
packages = builtins.attrValues {
inherit
rustToolchain
;
inherit (pkgs)
rust-analyzer-unwrapped
nixd
libxkbcommon
dav1d
libinput
linux-pam
gcc
glibc
cmake
;
inherit (pkgs.llvmPackages)
libclang
llvm
;
};
nativeBuildInputs = [ pkgs.pkg-config ];
RUST_SRC_PATH = "${rustToolchain}/lib/rustlib/src/rust/library";
LLVM_CONFIG_PATH = "${pkgs.llvmPackages.llvm}/bin/llvm-config";
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
BINDGEN_EXTRA_CLANG_ARGS = "-I${pkgs.linux-pam}/include -I${pkgs.glibc.dev}/include";
}

View file

@ -1 +0,0 @@
imports_granularity = "Module"

View file

@ -10,6 +10,7 @@ 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_config::CosmicConfigEntry;
use cosmic_greeter_daemon::{BgSource, UserData};
use cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1;
use std::collections::{HashMap, HashSet};
@ -19,6 +20,7 @@ use wayland_client::{Connection, Proxy};
use crate::keyboard_layout_wayland;
pub const MAX_WIDTH: f32 = 866.;
pub const DEFAULT_MENU_ITEM_HEIGHT: f32 = 36.;
#[derive(Debug, Clone, Eq, PartialEq)]
@ -28,6 +30,18 @@ pub struct ActiveLayout {
pub variant: String,
}
impl ActiveLayout {
pub fn name(&self) -> String {
let mut name = self.layout.clone();
if !self.variant.is_empty() {
name.push_str(" (");
name.push_str(&self.variant);
name.push_str(")");
}
name
}
}
pub struct Common<M> {
pub wayland_connection: Option<Connection>,
pub keyboard_layout: Option<ZcosmicKeyboardLayoutV1>,
@ -67,6 +81,7 @@ pub enum Message {
Focus(SurfaceId),
Key(Modifiers, Key, Option<SmolStr>),
NetworkIcon(Option<&'static str>),
OnScreenKeyboard,
SubsurfaceOpened(SurfaceId),
OutputEvent(OutputEvent, WlOutput),
PowerInfo(Option<(f64, bool, bool)>),
@ -276,6 +291,26 @@ impl<M: From<Message> + Send + 'static> Common<M> {
self.network_icon_opt =
network_icon_opt.map(|name| widget::icon::from_name(name).into());
}
Message::OnScreenKeyboard => {
use cosmic_osk_config::Config;
match Config::handler() {
Ok(handler) => {
let mut config = match Config::get_entry(&handler) {
Ok(config) => config,
Err((errs, config)) => {
tracing::warn!("failed to parse OSK config: {:?}", errs);
config
}
};
if let Err(err) = config.set_always_shown(&handler, !config.always_shown) {
tracing::error!("failed to set OSK always_shown config: {}", err);
}
}
Err(err) => {
tracing::error!("failed to create OSK config handler: {}", err);
}
}
}
Message::OutputEvent(output_event, output) => {
if self.wayland_connection.is_none() {
if let Some(backend) = output.backend().upgrade() {

View file

@ -11,6 +11,7 @@ use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity
use cosmic::cosmic_config::{self, ConfigSet};
use cosmic::cosmic_theme::{self, CosmicPalette};
use cosmic::desktop::fde::{DesktopEntry, get_languages_from_env};
use cosmic::iced::core::text::{Ellipsize, EllipsizeHeightLimit};
use cosmic::iced::event::listen_with;
use cosmic::iced::event::wayland::OutputEvent;
use cosmic::iced::futures::SinkExt;
@ -18,15 +19,15 @@ use cosmic::iced::platform_specific::runtime::wayland::layer_surface::{
IcedMargin, IcedOutput, SctkLayerSurfaceSettings,
};
use cosmic::iced::platform_specific::shell::wayland::commands::layer_surface::{
Anchor, KeyboardInteractivity, Layer, destroy_layer_surface, get_layer_surface,
Anchor, KeyboardInteractivity, Layer, destroy_layer_surface,
};
use cosmic::iced::platform_specific::shell::wayland::commands::subsurface::reposition_subsurface;
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, Rectangle, Size, Subscription, window,
};
use cosmic::surface::action::{LiveSettings, app_layer_shell};
use cosmic::widget::{id_container, text};
use cosmic::{Element, executor, surface, theme, widget};
use cosmic_greeter_config::Config as CosmicGreeterConfig;
@ -41,7 +42,7 @@ use std::num::NonZeroU32;
use std::process::Stdio;
use std::sync::{Arc, LazyLock};
use std::time::{Duration, Instant};
use std::{fs, io, process};
use std::{fs, io, process, thread};
use tokio::process::Child;
use tokio::time;
use tracing::metadata::LevelFilter;
@ -52,7 +53,7 @@ use wayland_client::Proxy;
use wayland_client::protocol::wl_output::WlOutput;
use zbus::{Connection, proxy};
use crate::common::{self, Common, DEFAULT_MENU_ITEM_HEIGHT};
use crate::common::{self, Common, DEFAULT_MENU_ITEM_HEIGHT, MAX_WIDTH};
use crate::fl;
static USERNAME_ID: LazyLock<iced::id::Id> = LazyLock::new(|| iced::id::Id::new("username-id"));
@ -77,8 +78,8 @@ async fn user_data_dbus() -> Result<Vec<UserData>, Box<dyn Error>> {
Ok(user_datas)
}
fn user_data_fallback() -> Vec<UserData> {
let user_filter = UserFilter::new();
async fn user_data_fallback() -> Vec<UserData> {
let user_filter = UserFilter::new().await;
// The pwd::Passwd method is unsafe (but not labelled as such) due to using global state (libc pwent functions).
/* unsafe */
@ -130,7 +131,7 @@ pub fn main() -> Result<(), Box<dyn Error>> {
Ok(ok) => ok,
Err(err) => {
tracing::error!("failed to load user data from daemon: {}", err);
user_data_fallback()
runtime.block_on(user_data_fallback())
}
};
@ -280,7 +281,21 @@ pub fn main() -> Result<(), Box<dyn Error>> {
sessions
};
let logind_available = cfg!(feature = "logind") && crate::logind::is_available();
match process::Command::new("cosmic-osk")
.arg("overlay")
.env("RUST_LOG", "cosmic_osk=info")
.spawn()
{
Ok(mut child) => {
thread::spawn(move || match child.wait() {
Ok(status) => tracing::warn!("cosmic-osk exited: {}", status),
Err(err) => tracing::error!("failed to wait on cosmic-osk: {}", err),
});
}
Err(err) => {
tracing::error!("failed to spawn cosmic-osk: {}", err);
}
}
let flags = Flags {
user_icons: user_datas
@ -291,7 +306,10 @@ pub fn main() -> Result<(), Box<dyn Error>> {
sessions,
greeter_config,
greeter_config_handler,
logind_available,
#[cfg(feature = "logind")]
logind_available: crate::logind::is_available(),
#[cfg(not(feature = "logind"))]
logind_available: false,
};
let settings = Settings::default().no_main_window(true);
@ -363,6 +381,9 @@ pub enum Message {
Common(common::Message),
OutputEvent(OutputEvent, WlOutput),
Auth(Option<String>),
/// Non-fatal error message from PAM (`PAM_ERROR_MSG`), reported by greetd as an auth
/// message of type `Error`. Unlike [`Message::Error`] the conversation stays alive.
AuthError(String),
ConfigUpdateUser,
DialogCancel,
DialogConfirm,
@ -386,7 +407,7 @@ pub enum Message {
Session(String),
Shutdown,
Socket(SocketState),
Surface(surface::Action),
Surface(surface::Action<Message>),
Suspend,
Username(String),
EnterUser(bool, String),
@ -478,9 +499,9 @@ impl App {
.window_size
.get(&id)
.map(|s| s.width)
.unwrap_or(800.);
let menu_width = if window_width > 800. {
800.
.unwrap_or(MAX_WIDTH);
let menu_width = if window_width > MAX_WIDTH {
MAX_WIDTH
} else {
window_width
};
@ -580,9 +601,19 @@ impl App {
};
let mut input_button = widget::popover(
widget::button::custom(widget::icon::from_name("input-keyboard-symbolic"))
widget::container(
widget::button::custom(
widget::text(
self.common.active_layouts[self.common.current_keyboard_layout].name(),
)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1)))
.height(16)
.center(),
)
.padding(12.0)
.on_press(Message::DropdownToggle(Dropdown::Keyboard)),
)
.max_width(80),
)
.position(widget::popover::Position::Bottom);
if matches!(self.dropdown_opt, Some(Dropdown::Keyboard)) {
@ -698,6 +729,13 @@ impl App {
text(fl!("accessibility")),
widget::tooltip::Position::Top
),
widget::tooltip(
widget::button::custom(widget::icon::from_name("input-keyboard-symbolic"))
.padding(12.0)
.on_press(Message::Common(common::Message::OnScreenKeyboard)),
text(fl!("on-screen-keyboard")),
widget::tooltip::Position::Top,
),
widget::tooltip(
input_button,
text(fl!("keyboard-layout")),
@ -961,7 +999,7 @@ impl App {
appearance
},
)))
.width(Length::Fixed(800.0));
.width(Length::Fixed(MAX_WIDTH));
let menu = if let Some(t) = self.common.rectangle_tracker.as_ref() {
Element::from(t.container((id, false), menu))
} else {
@ -1178,6 +1216,12 @@ impl cosmic::Application for App {
fn update(&mut self, message: Self::Message) -> Task<Message> {
match message {
Message::Common(common_message) => {
if matches!(&common_message, common::Message::Prompt(_, _, Some(_)))
&& self.authenticating
{
self.authenticating = false;
self.common.prompt_opt = None;
}
// In greetd's IPC protocol, the greeter must acknowledge auth messages by
// sending PostAuthMessageResponse. For non-interactive "info" messages
// (fingerprint prompts typically come through here), the correct response
@ -1243,10 +1287,10 @@ impl cosmic::Application for App {
let unwrapped_size = size
.map(|s| (s.0.unwrap_or(1920), s.1.unwrap_or(1080)))
.unwrap_or((1920, 1080));
let (loc, sub_size) = if unwrapped_size.0 > 800 {
let (loc, sub_size) = if unwrapped_size.0 as f32 > MAX_WIDTH {
(
Point::new(unwrapped_size.0 as f32 / 2. - 400., 32.),
Size::new(800., unwrapped_size.1 as f32 - 32.),
Point::new((unwrapped_size.0 as f32 - MAX_WIDTH) / 2., 32.),
Size::new(MAX_WIDTH, unwrapped_size.1 as f32 - 32.),
)
} else {
(
@ -1282,27 +1326,41 @@ impl cosmic::Application for App {
);
return Task::batch([
self.update_user_data(),
get_layer_surface(SctkLayerSurfaceSettings {
id: surface_id,
layer: Layer::Overlay,
keyboard_interactivity: KeyboardInteractivity::Exclusive,
input_zone: None,
anchor: Anchor::TOP | Anchor::LEFT | Anchor::BOTTOM | Anchor::RIGHT,
output: IcedOutput::Output(output),
namespace: "cosmic-locker".into(),
size: Some((None, None)),
margin: IcedMargin {
top: 0,
bottom: 0,
left: 0,
right: 0,
cosmic::surface::surface_task(app_layer_shell(
|_: &App| LiveSettings {
padding: None,
corners: None,
blur: Some(false),
},
exclusive_zone: -1,
size_limits: iced::Limits::NONE.min_width(1.0).min_height(1.0),
}),
cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(msg),
move |_: &mut App| {
let output = output.clone();
SctkLayerSurfaceSettings {
id: surface_id,
layer: Layer::Top,
keyboard_interactivity: KeyboardInteractivity::Exclusive,
input_zone: None,
anchor: Anchor::TOP
| Anchor::LEFT
| Anchor::BOTTOM
| Anchor::RIGHT,
output: IcedOutput::Output(output),
namespace: "cosmic-greeter".into(),
size: Some((None, None)),
margin: IcedMargin {
top: 0,
bottom: 0,
left: 0,
right: 0,
},
exclusive_zone: -1,
size_limits: iced::Limits::NONE
.min_width(1.0)
.min_height(1.0),
}
},
None,
)),
cosmic::task::message(cosmic::Action::Surface(msg)),
]);
}
OutputEvent::Removed => {
@ -1493,6 +1551,13 @@ impl cosmic::Application for App {
self.authenticating = true;
self.send_request(Request::PostAuthMessageResponse { response });
}
Message::AuthError(error) => {
// The conversation continues, so acknowledge like any other
// non-interactive auth message rather than cancelling the session.
self.common.error_opt = Some(error);
self.authenticating = false;
self.send_request(Request::PostAuthMessageResponse { response: None });
}
Message::Login => {
self.common.prompt_opt = None;
self.common.error_opt = None;
@ -1643,9 +1708,7 @@ impl cosmic::Application for App {
self.greetd_sender = Some(sender);
}
Message::Surface(a) => {
return cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(a),
));
return cosmic::task::message(cosmic::Action::Surface(a));
}
Message::ScreenReader(enabled) => {
if enabled
@ -1843,8 +1906,8 @@ impl cosmic::Application for App {
tracing::error!("Failed to find subsurface menu id");
return Task::none();
};
let loc = if size.width > 800. {
Point::new(size.width / 2. - 400., 32.)
let loc = if size.width > MAX_WIDTH {
Point::new((size.width - MAX_WIDTH) / 2., 32.)
} else {
Point::new(0., 32.)
};

View file

@ -115,7 +115,8 @@ pub fn subscription() -> Subscription<Message> {
.await;
}
greetd_ipc::AuthMessageType::Error => {
_ = sender.send(Message::Error(auth_message)).await;
// PAM_ERROR_MSG: a failed attempt, not a dead session.
_ = sender.send(Message::AuthError(auth_message)).await;
}
},
greetd_ipc::Response::Error {

View file

@ -4,16 +4,18 @@
use color_eyre::eyre::WrapErr;
use cosmic::app::{Core, Settings, Task};
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity;
use cosmic::iced::core::text::{Ellipsize, EllipsizeHeightLimit};
use cosmic::iced::event::wayland::{OutputEvent, SessionLockEvent};
use cosmic::iced::futures::{self, SinkExt};
use cosmic::iced::platform_specific::shell::wayland::commands::session_lock::{
destroy_lock_surface, get_lock_surface, lock, unlock,
destroy_lock_surface, lock, unlock,
};
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, Length, Point, Rectangle, Size, Subscription,
};
use cosmic::surface::action::LiveSettings;
use cosmic::{Element, executor, surface, theme, widget};
use cosmic_config::CosmicConfigEntry;
use cosmic_greeter_daemon::{TimeAppletConfig, UserData};
@ -33,7 +35,7 @@ use tracing_subscriber::{EnvFilter, fmt};
use wayland_client::Proxy;
use wayland_client::protocol::wl_output::WlOutput;
use crate::common::{self, Common, DEFAULT_MENU_ITEM_HEIGHT};
use crate::common::{self, Common, DEFAULT_MENU_ITEM_HEIGHT, MAX_WIDTH};
use crate::fl;
fn lockfile_opt() -> Option<PathBuf> {
@ -79,8 +81,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 +88,10 @@ 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,
#[cfg(feature = "logind")]
logind_available: crate::logind::is_available(),
#[cfg(not(feature = "logind"))]
logind_available: false,
};
let settings = Settings::default().no_main_window(true);
@ -272,7 +275,7 @@ pub enum Message {
KeyboardLayout(usize),
Inhibit(Arc<OwnedFd>),
Submit(String),
Surface(surface::Action),
Surface(surface::Action<Message>),
Suspend,
TimeAppletConfig(TimeAppletConfig),
Error(String),
@ -324,9 +327,9 @@ impl App {
.window_size
.get(&surface_id)
.map(|s| s.width)
.unwrap_or(800.);
let menu_width = if window_width > 800. {
800.
.unwrap_or(MAX_WIDTH);
let menu_width = if window_width > MAX_WIDTH {
MAX_WIDTH
} else {
window_width
};
@ -415,9 +418,19 @@ impl App {
};
let mut input_button = widget::popover(
widget::button::custom(widget::icon::from_name("input-keyboard-symbolic"))
widget::container(
widget::button::custom(
widget::text(
self.common.active_layouts[self.common.current_keyboard_layout].name(),
)
.ellipsize(Ellipsize::End(EllipsizeHeightLimit::Lines(1)))
.height(16)
.center(),
)
.padding(12.0)
.on_press(Message::DropdownToggle(Dropdown::Keyboard)),
)
.max_width(80),
)
.position(widget::popover::Position::Bottom);
if matches!(self.dropdown_opt, Some(Dropdown::Keyboard)) {
@ -441,6 +454,13 @@ impl App {
.padding(12.0)
.on_press(Message::None),
*/
widget::tooltip(
widget::button::custom(widget::icon::from_name("input-keyboard-symbolic"))
.padding(12.0)
.on_press(Message::Common(common::Message::OnScreenKeyboard)),
widget::text(fl!("on-screen-keyboard")),
widget::tooltip::Position::Top,
),
widget::tooltip(
input_button,
widget::text(fl!("keyboard-layout")),
@ -662,6 +682,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);
let (mut common, common_task) = Common::init(core);
common.on_output_event = Some(Box::new(|output_event, output| {
Message::OutputEvent(output_event, output)
@ -709,6 +730,12 @@ impl cosmic::Application for App {
match message {
Message::None => {}
Message::Common(common_message) => {
if matches!(&common_message, common::Message::Prompt(_, _, Some(_)))
&& self.authenticating
{
self.authenticating = false;
self.common.prompt_opt = None;
}
return self.common.update(common_message);
}
Message::OutputEvent(output_event, output) => {
@ -774,10 +801,10 @@ impl cosmic::Application for App {
let unwrapped_size = size
.map(|s| (s.0.unwrap_or(1920), s.1.unwrap_or(1080)))
.unwrap_or((1920, 1080));
let (loc, sub_size) = if unwrapped_size.0 > 800 {
let (loc, sub_size) = if unwrapped_size.0 as f32 > MAX_WIDTH {
(
Point::new(unwrapped_size.0 as f32 / 2. - 400., 32.),
Size::new(800., unwrapped_size.1 as f32 - 32.),
Size::new(MAX_WIDTH, unwrapped_size.1 as f32 - 32.),
)
} else {
(
@ -812,11 +839,19 @@ impl cosmic::Application for App {
);
if matches!(self.state, State::Locked { .. }) {
return get_lock_surface(surface_id, output).chain({
cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(msg),
))
});
return cosmic::task::message(cosmic::Action::Surface(
cosmic::surface::action::lock(
|| LiveSettings {
padding: None,
corners: None,
blur: Some(false),
},
surface_id,
output,
None::<fn() -> cosmic::Element<'static, cosmic::Action<Message>>>,
),
))
.chain(cosmic::task::message(cosmic::Action::Surface(msg)));
}
}
OutputEvent::Removed => {
@ -847,10 +882,10 @@ impl cosmic::Application for App {
let unwrapped_size = size
.map(|s| (s.0.unwrap_or(1920), s.1.unwrap_or(1080)))
.unwrap_or((1920, 1080));
let (loc, sub_size) = if unwrapped_size.0 > 800 {
let (loc, sub_size) = if unwrapped_size.0 as f32 > MAX_WIDTH {
(
Point::new(unwrapped_size.0 as f32 / 2. - 400., 32.),
Size::new(800., unwrapped_size.1 as f32 - 32.),
Size::new(MAX_WIDTH, unwrapped_size.1 as f32 - 32.),
)
} else {
(Point::ORIGIN, Size::new(1920., 1080.))
@ -956,7 +991,18 @@ impl cosmic::Application for App {
// Create lock surfaces
for (output, surface_id) in self.common.surface_ids.iter() {
commands.push(get_lock_surface(*surface_id, output.clone()));
commands.push(cosmic::task::message(cosmic::Action::Surface(
cosmic::surface::action::lock(
|| LiveSettings {
padding: None,
corners: None,
blur: Some(false),
},
*surface_id,
output.clone(),
None::<fn() -> cosmic::Element<'static, cosmic::Action<Message>>>,
),
)));
if let Some((rect, name)) = self
.common
@ -991,9 +1037,7 @@ impl cosmic::Application for App {
app.menu(subsurface_id).map(cosmic::Action::App)
})),
);
commands.push(cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(msg),
)));
commands.push(cosmic::task::message(cosmic::Action::Surface(msg)));
} else {
tracing::error!("no rectangle for subsurface creation...");
}
@ -1158,9 +1202,7 @@ impl cosmic::Application for App {
}
}
Message::Surface(a) => {
return cosmic::task::message(cosmic::Action::Cosmic(
cosmic::app::Action::Surface(a),
));
return cosmic::task::message(cosmic::Action::Surface(a));
}
}
Task::none()
@ -1172,17 +1214,8 @@ impl cosmic::Application for App {
}
/// Creates a view after each update.
fn view_window(&self, surface_id: SurfaceId) -> Element<'_, Self::Message> {
let img = self
.common
.surface_images
.get(&surface_id)
.unwrap_or(&self.common.fallback_background);
widget::image(img)
.content_fit(iced::ContentFit::Cover)
.width(Length::Fill)
.height(Length::Fill)
.into()
fn view_window(&self, _surface_id: SurfaceId) -> Element<'_, Self::Message> {
widget::space::horizontal().into()
}
fn subscription(&self) -> Subscription<Self::Message> {
@ -1220,7 +1253,8 @@ impl cosmic::Application for App {
}),
);
if cfg!(feature = "logind") && self.flags.logind_available {
#[cfg(feature = "logind")]
if self.flags.logind_available {
subscriptions.push(crate::logind::subscription());
}