Compare commits
2
Commits
c302251fef
...
1390140314
| 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.
|
||||
|
||||
## 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
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
- See the project skill `hyprland-greetd-greeter` for protocol, host, and maintenance guidance.
|
||||
- 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.
|
||||
|
||||
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::io::{Read, Write};
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -87,6 +87,121 @@ struct Session {
|
||||
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() {
|
||||
let app = gtk::Application::builder()
|
||||
.application_id("dev.wfalkowski.WaylandGreeter")
|
||||
@@ -536,21 +651,39 @@ fn split_exec(input: &str) -> Vec<String> {
|
||||
}
|
||||
|
||||
fn install_style() {
|
||||
let css = r#"
|
||||
.backdrop { background: #151515; color: #d0d0d0; font-family: "Maple Mono Normal"; font-size: 9pt; }
|
||||
.card { background: #151515; border: 1px solid #505050; border-radius: 1px; padding: 18px; }
|
||||
.field-label { color: #505050; 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; }
|
||||
entry:focus, combobox button:focus { border-color: #6c99bb; }
|
||||
entry placeholder { color: #505050; }
|
||||
button.login-button { color: #d0d0d0; background: #151515; border: 1px solid #505050; border-radius: 1px; min-height: 36px; margin-top: 2px; }
|
||||
button.login-button:hover { color: #f5f5f5; background: #547c99; }
|
||||
button.login-button:focus { border-color: #6c99bb; }
|
||||
button.login-button:disabled { color: #505050; background: #151515; border-color: #505050; }
|
||||
.status { color: #d3a04d; font-size: 9pt; }
|
||||
"#;
|
||||
let palette = Palette::load();
|
||||
let css = format!(
|
||||
r#"
|
||||
.backdrop {{ background: {background}; color: {text}; font-family: "Maple Mono Normal"; font-size: 9pt; }}
|
||||
.card {{ background: {panel}; border: 1px solid {border}; border-radius: 1px; padding: 18px; }}
|
||||
.field-label {{ color: {muted}; font-size: 9pt; }}
|
||||
entry, combobox button {{ color: {text}; background: {surface}; border: 1px solid {border}; border-radius: 1px; min-height: 36px; padding: 0 10px; caret-color: {emphasis}; }}
|
||||
entry:focus, combobox button:focus {{ border-color: {accent}; }}
|
||||
entry placeholder {{ color: {dim}; }}
|
||||
button.login-button {{ color: {text}; background: {surface}; border: 1px solid {border}; border-radius: 1px; min-height: 36px; margin-top: 2px; }}
|
||||
button.login-button:hover {{ color: {emphasis}; background: {accent_secondary}; }}
|
||||
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();
|
||||
provider.load_from_data(css);
|
||||
provider.load_from_data(&css);
|
||||
if let Some(display) = gtk::gdk::Display::default() {
|
||||
gtk::style_context_add_provider_for_display(
|
||||
&display,
|
||||
|
||||
Reference in new issue
Block a user