Compare commits
2
Commits
c302251fef
...
0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1390140314 | ||
|
|
de6587d3b7 |
No files matched your search
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Wiktor A. Falkowski
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -33,9 +33,21 @@ Cage and a greeter account must be installed/configured separately. Gentoo's com
|
|||||||
|
|
||||||
The session picker is hidden by default; press **F2** to show or hide it. The login surface starts with only the username and login button; PAM prompts reveal the response field when needed. The picker reads `*.desktop` entries from `/usr/share/wayland-sessions` and `/usr/local/share/wayland-sessions`. If neither has entries, it offers a `Hyprland` fallback command. The selected entry's `Exec` is parsed into argv and passed to greetd directly, without shell expansion.
|
The session picker is hidden by default; press **F2** to show or hide it. The login surface starts with only the username and login button; PAM prompts reveal the response field when needed. The picker reads `*.desktop` entries from `/usr/share/wayland-sessions` and `/usr/local/share/wayland-sessions`. If neither has entries, it offers a `Hyprland` fallback command. The selected entry's `Exec` is parsed into argv and passed to greetd directly, without shell expansion.
|
||||||
|
|
||||||
|
## Colors
|
||||||
|
|
||||||
|
At startup the greeter reads `~/.config/wayter/colors.ini`. The tracked [`colors.ini.example`](colors.ini.example) contains the current dark steel-blue palette; copy it into place with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mkdir -p ~/.config/wayter
|
||||||
|
cp colors.ini.example ~/.config/wayter/colors.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit values in the `[colors]` section using six-digit hex colors such as `accent = #6C99BB`. Supported names are `background`, `panel`, `surface`, `hover`, `border`, `focus`, `text`, `muted`, `dim`, `emphasis`, `accent`, `accent_secondary`, and `warning`. Invalid values keep that color's built-in default, and a missing or unreadable file falls back to the full built-in palette.
|
||||||
|
|
||||||
## Design notes
|
## Design notes
|
||||||
|
|
||||||
- PAM prompts come from greetd IPC and are not assumed to be a single username/password exchange.
|
- PAM prompts come from greetd IPC and are not assumed to be a single username/password exchange.
|
||||||
- Secrets are never written to disk or logged.
|
- Secrets are never written to disk or logged.
|
||||||
- The card is centered by GTK's center container in both dimensions. Its UI colors come only from `~/.config/foot/foot.ini`; it uses Maple Mono and GTK4 system libraries.
|
- The card is centered by GTK's center container in both dimensions. GTK CSS colors come from `~/.config/wayter/colors.ini`; it uses Maple Mono and GTK4 system libraries.
|
||||||
- See the project skill `hyprland-greetd-greeter` for protocol, host, and maintenance guidance.
|
|
||||||
|
The project is available under the [MIT License](LICENSE).
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[colors]
|
||||||
|
background = #151515
|
||||||
|
panel = #181818
|
||||||
|
surface = #202020
|
||||||
|
hover = #282828
|
||||||
|
border = #2F2F2F
|
||||||
|
focus = #454545
|
||||||
|
text = #D0D0D0
|
||||||
|
muted = #757575
|
||||||
|
dim = #505050
|
||||||
|
emphasis = #F5F5F5
|
||||||
|
accent = #6C99BB
|
||||||
|
accent_secondary = #547C99
|
||||||
|
warning = #D3A04D
|
||||||
+148
-15
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -87,6 +87,121 @@ struct Session {
|
|||||||
command: Vec<String>,
|
command: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Palette {
|
||||||
|
background: String,
|
||||||
|
panel: String,
|
||||||
|
surface: String,
|
||||||
|
hover: String,
|
||||||
|
border: String,
|
||||||
|
focus: String,
|
||||||
|
text: String,
|
||||||
|
muted: String,
|
||||||
|
dim: String,
|
||||||
|
emphasis: String,
|
||||||
|
accent: String,
|
||||||
|
accent_secondary: String,
|
||||||
|
warning: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Palette {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
background: "#151515".into(),
|
||||||
|
panel: "#181818".into(),
|
||||||
|
surface: "#202020".into(),
|
||||||
|
hover: "#282828".into(),
|
||||||
|
border: "#2F2F2F".into(),
|
||||||
|
focus: "#454545".into(),
|
||||||
|
text: "#D0D0D0".into(),
|
||||||
|
muted: "#757575".into(),
|
||||||
|
dim: "#505050".into(),
|
||||||
|
emphasis: "#F5F5F5".into(),
|
||||||
|
accent: "#6C99BB".into(),
|
||||||
|
accent_secondary: "#547C99".into(),
|
||||||
|
warning: "#D3A04D".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Palette {
|
||||||
|
fn load() -> Self {
|
||||||
|
let Some(home) = std::env::var_os("HOME").map(PathBuf::from) else {
|
||||||
|
eprintln!("HOME is unset; using the built-in greeter palette");
|
||||||
|
return Self::default();
|
||||||
|
};
|
||||||
|
let path = home.join(".config/wayter/colors.ini");
|
||||||
|
match fs::read_to_string(&path) {
|
||||||
|
Ok(contents) => Self::parse(&contents, &path),
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!(
|
||||||
|
"Could not read {}: {error}; using the built-in palette",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(contents: &str, path: &Path) -> Self {
|
||||||
|
let mut palette = Self::default();
|
||||||
|
let mut in_colors = false;
|
||||||
|
for (index, raw_line) in contents.lines().enumerate() {
|
||||||
|
let line = raw_line.trim();
|
||||||
|
if line.is_empty() || line.starts_with('#') || line.starts_with(';') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if line.starts_with('[') && line.ends_with(']') {
|
||||||
|
in_colors = line == "[colors]";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !in_colors {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some((key, value)) = line.split_once('=') else {
|
||||||
|
eprintln!("{}:{}: expected key = #RRGGBB", path.display(), index + 1);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let key = key.trim();
|
||||||
|
let value = value.trim();
|
||||||
|
let Some(color) = Self::valid_color(value) else {
|
||||||
|
eprintln!(
|
||||||
|
"{}:{}: invalid color for {key}; keeping its default",
|
||||||
|
path.display(),
|
||||||
|
index + 1
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let target = match key {
|
||||||
|
"background" => &mut palette.background,
|
||||||
|
"panel" => &mut palette.panel,
|
||||||
|
"surface" => &mut palette.surface,
|
||||||
|
"hover" => &mut palette.hover,
|
||||||
|
"border" => &mut palette.border,
|
||||||
|
"focus" => &mut palette.focus,
|
||||||
|
"text" => &mut palette.text,
|
||||||
|
"muted" => &mut palette.muted,
|
||||||
|
"dim" => &mut palette.dim,
|
||||||
|
"emphasis" => &mut palette.emphasis,
|
||||||
|
"accent" => &mut palette.accent,
|
||||||
|
"accent_secondary" => &mut palette.accent_secondary,
|
||||||
|
"warning" => &mut palette.warning,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
*target = color;
|
||||||
|
}
|
||||||
|
palette
|
||||||
|
}
|
||||||
|
|
||||||
|
fn valid_color(value: &str) -> Option<String> {
|
||||||
|
let hex = value.strip_prefix('#')?;
|
||||||
|
if hex.len() != 6 || !hex.bytes().all(|byte| byte.is_ascii_hexdigit()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(format!("#{}", hex.to_ascii_uppercase()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let app = gtk::Application::builder()
|
let app = gtk::Application::builder()
|
||||||
.application_id("dev.wfalkowski.WaylandGreeter")
|
.application_id("dev.wfalkowski.WaylandGreeter")
|
||||||
@@ -536,21 +651,39 @@ fn split_exec(input: &str) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn install_style() {
|
fn install_style() {
|
||||||
let css = r#"
|
let palette = Palette::load();
|
||||||
.backdrop { background: #151515; color: #d0d0d0; font-family: "Maple Mono Normal"; font-size: 9pt; }
|
let css = format!(
|
||||||
.card { background: #151515; border: 1px solid #505050; border-radius: 1px; padding: 18px; }
|
r#"
|
||||||
.field-label { color: #505050; font-size: 9pt; }
|
.backdrop {{ background: {background}; color: {text}; font-family: "Maple Mono Normal"; font-size: 9pt; }}
|
||||||
entry, combobox button { color: #d0d0d0; background: #151515; border: 1px solid #505050; border-radius: 1px; min-height: 36px; padding: 0 10px; caret-color: #f5f5f5; }
|
.card {{ background: {panel}; border: 1px solid {border}; border-radius: 1px; padding: 18px; }}
|
||||||
entry:focus, combobox button:focus { border-color: #6c99bb; }
|
.field-label {{ color: {muted}; font-size: 9pt; }}
|
||||||
entry placeholder { color: #505050; }
|
entry, combobox button {{ color: {text}; background: {surface}; border: 1px solid {border}; border-radius: 1px; min-height: 36px; padding: 0 10px; caret-color: {emphasis}; }}
|
||||||
button.login-button { color: #d0d0d0; background: #151515; border: 1px solid #505050; border-radius: 1px; min-height: 36px; margin-top: 2px; }
|
entry:focus, combobox button:focus {{ border-color: {accent}; }}
|
||||||
button.login-button:hover { color: #f5f5f5; background: #547c99; }
|
entry placeholder {{ color: {dim}; }}
|
||||||
button.login-button:focus { border-color: #6c99bb; }
|
button.login-button {{ color: {text}; background: {surface}; border: 1px solid {border}; border-radius: 1px; min-height: 36px; margin-top: 2px; }}
|
||||||
button.login-button:disabled { color: #505050; background: #151515; border-color: #505050; }
|
button.login-button:hover {{ color: {emphasis}; background: {accent_secondary}; }}
|
||||||
.status { color: #d3a04d; font-size: 9pt; }
|
button.login-button:focus {{ border-color: {focus}; }}
|
||||||
"#;
|
button.login-button:disabled {{ color: {dim}; background: {panel}; border-color: {border}; }}
|
||||||
|
.status {{ color: {warning}; font-size: 9pt; }}
|
||||||
|
.field-label:disabled {{ color: {dim}; }}
|
||||||
|
entry:selected, combobox button:selected {{ background: {hover}; }}
|
||||||
|
"#,
|
||||||
|
background = palette.background,
|
||||||
|
panel = palette.panel,
|
||||||
|
surface = palette.surface,
|
||||||
|
hover = palette.hover,
|
||||||
|
border = palette.border,
|
||||||
|
focus = palette.focus,
|
||||||
|
text = palette.text,
|
||||||
|
muted = palette.muted,
|
||||||
|
dim = palette.dim,
|
||||||
|
emphasis = palette.emphasis,
|
||||||
|
accent = palette.accent,
|
||||||
|
accent_secondary = palette.accent_secondary,
|
||||||
|
warning = palette.warning,
|
||||||
|
);
|
||||||
let provider = gtk::CssProvider::new();
|
let provider = gtk::CssProvider::new();
|
||||||
provider.load_from_data(css);
|
provider.load_from_data(&css);
|
||||||
if let Some(display) = gtk::gdk::Display::default() {
|
if let Some(display) = gtk::gdk::Display::default() {
|
||||||
gtk::style_context_add_provider_for_display(
|
gtk::style_context_add_provider_for_display(
|
||||||
&display,
|
&display,
|
||||||
|
|||||||
Reference in new issue
Block a user