Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ dirs = "6.0.0"
gdk-pixbuf = { version = "0.21.5", optional = true }
nix = { version = "0.31.3", features = ["event", "fs", "mman", "process", "time"] }
pam-rs = "0.9.5"
pango = "0.22.0"
pangocairo = "0.22.0"
pango = { version = "0.22.0", optional = true }
pangocairo = { version = "0.22.0", optional = true }
serde = { version = "1.0.228", features = [ "derive" ] }
tracing = "0.1.41"
tracing-subscriber = "0.3.20"
Expand All @@ -27,8 +27,9 @@ xkbcommon = "0.9.0"
zeroize = "1.8.2"

[features]
default = ["gdk-pixbuf"]
default = ["gdk-pixbuf", "pango"]
gdk-pixbuf = ["dep:gdk-pixbuf"]
pango = ["dep:pango", "dep:pangocairo"]

[build-dependencies]
time = { version = "0.3.47", features = ["formatting"] }
Expand Down
5 changes: 3 additions & 2 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ use clap::{
};
use clap_complete::{Shell, aot::generate as generate_completions};

use crate::util::{
BackgroundImageScale, BackgroundType, FontSlant, FontWeight, InputVisibility, LogLevel, Rgba,
use crate::{
font::{FontSlant, FontWeight},
util::{BackgroundImageScale, BackgroundType, InputVisibility, LogLevel, Rgba},
};

/// Customisable, minimalist screen locker for Wayland
Expand Down
3 changes: 2 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ use tracing::debug;

use crate::{
args::NLockArgs,
util::{BackgroundImageScale, BackgroundType, FontSlant, FontWeight, InputVisibility, Rgba},
font::{FontSlant, FontWeight},
util::{BackgroundImageScale, BackgroundType, InputVisibility, Rgba},
};

const CONFIG_FILE_NAME: &str = "nlock.toml";
Expand Down
307 changes: 307 additions & 0 deletions src/font.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
pub trait FontInfo {
fn font_height(&self) -> f64;
}

pub trait TextInfo {
fn text_height(&self) -> f64;
fn text_width(&self) -> f64;
fn text_x(&self) -> f64;
fn text_y(&self) -> f64;
}

pub trait ShowText {
fn show_text(&self, context: &cairo::Context) -> anyhow::Result<()>;
}

#[cfg(not(feature = "pango"))]
mod cairo_backend {
use anyhow::Result;
use clap::ValueEnum;
use serde::Deserialize;

use crate::{
config::NLockConfig,
font::{FontInfo, ShowText, TextInfo},
render::{DEFAULT_DPI, DEFAULT_SCALE},
};

pub struct NLockFont {
font_height: f64,
text: String,
extents: cairo::TextExtents,
}

impl NLockFont {
pub fn new(
config: &NLockConfig,
context: &cairo::Context,
dpi: Option<f64>,
scale: Option<f64>,
subpixel: Option<cairo::SubpixelOrder>,
) -> Result<Self> {
let dpi = dpi.unwrap_or(DEFAULT_DPI);
let scale = scale.unwrap_or(DEFAULT_SCALE);
let subpixel = subpixel.unwrap_or(cairo::SubpixelOrder::Default);

let mut fo = cairo::FontOptions::new()?;
fo.set_hint_style(cairo::HintStyle::Full);
fo.set_antialias(cairo::Antialias::Subpixel);
fo.set_subpixel_order(subpixel);

context.set_font_options(&fo);
context.select_font_face(
&config.font.family,
config.font.slant.into(),
config.font.weight.into(),
);
context.set_font_size((config.font.size / 72.0) * dpi * scale);

let fe = context.font_extents()?;
let extents = context.text_extents("")?;

Ok(Self {
font_height: fe.height(),
text: "".to_string(),
extents,
})
}

pub fn set_text<T>(&mut self, context: &cairo::Context, text: T) -> Result<()>
where
T: AsRef<str>,
{
self.text = text.as_ref().to_string();
self.extents = context.text_extents(text.as_ref())?;
Ok(())
}
}

impl FontInfo for NLockFont {
fn font_height(&self) -> f64 {
self.font_height
}
}

impl TextInfo for NLockFont {
fn text_height(&self) -> f64 {
self.extents.height()
}

fn text_width(&self) -> f64 {
self.extents.width()
}

fn text_x(&self) -> f64 {
self.extents.x_bearing()
}

fn text_y(&self) -> f64 {
self.extents.y_bearing()
}
}

impl ShowText for NLockFont {
fn show_text(&self, context: &cairo::Context) -> Result<()> {
context.show_text(&self.text)?;
Ok(())
}
}

#[derive(Debug, Deserialize, Copy, Clone, ValueEnum)]
#[serde(rename_all = "lowercase")]
pub enum FontSlant {
Normal,
Italic,
Oblique,
}

impl From<FontSlant> for cairo::FontSlant {
fn from(value: FontSlant) -> Self {
match value {
FontSlant::Normal => Self::Normal,
FontSlant::Italic => Self::Italic,
FontSlant::Oblique => Self::Oblique,
}
}
}

#[derive(Debug, Deserialize, Copy, Clone, ValueEnum)]
#[serde(rename_all = "lowercase")]
pub enum FontWeight {
Normal,
Bold,
}

impl From<FontWeight> for cairo::FontWeight {
fn from(value: FontWeight) -> Self {
match value {
FontWeight::Normal => Self::Normal,
FontWeight::Bold => Self::Bold,
}
}
}
}

#[cfg(feature = "pango")]
mod pango_backend {
use anyhow::Result;
use clap::ValueEnum;
use pango::Rectangle;
use pangocairo::functions::{create_layout, show_layout};
use serde::Deserialize;

use crate::{
config::NLockConfig,
font::{FontInfo, ShowText, TextInfo},
render::{DEFAULT_DPI, DEFAULT_SCALE},
};

pub struct NLockFont {
layout: pango::Layout,
metrics: pango::FontMetrics,
text: String,
extents: Rectangle,
}

impl NLockFont {
pub fn new(
config: &NLockConfig,
context: &cairo::Context,
dpi: Option<f64>,
scale: Option<f64>,
) -> Self {
let dpi = dpi.unwrap_or(DEFAULT_DPI);
let scale = scale.unwrap_or(DEFAULT_SCALE);

let mut fd = pango::FontDescription::new();
fd.set_family(&config.font.family);
fd.set_style(config.font.slant.into());
fd.set_weight(config.font.weight.into());
fd.set_absolute_size(((config.font.size / 72.0) * dpi * scale) * PANGO_SCALE as f64);

let layout = create_layout(context);
layout.set_font_description(Some(&fd));

let p_ctx = layout.context();
let metrics = p_ctx.metrics(Some(&fd), None);

layout.set_text("");
let extents = layout.pixel_extents().0;

Self {
layout,
metrics,
text: "".to_string(),
extents,
}
}

pub fn set_text<T>(&mut self, text: T)
where
T: AsRef<str>,
{
self.text = text.as_ref().to_string();
self.layout.set_text(text.as_ref());
self.extents = self.layout.pixel_extents().0;
}
}

impl FontInfo for NLockFont {
fn font_height(&self) -> f64 {
(pango_pixels(self.metrics.ascent()) + pango_pixels(self.metrics.descent())) as f64
}
}

impl TextInfo for NLockFont {
fn text_height(&self) -> f64 {
self.extents.height() as f64
}

fn text_width(&self) -> f64 {
self.extents.width() as f64
}

fn text_x(&self) -> f64 {
self.extents.x() as f64
}

fn text_y(&self) -> f64 {
self.extents.y() as f64
}
}

impl ShowText for NLockFont {
fn show_text(&self, context: &cairo::Context) -> Result<()> {
show_layout(context, &self.layout);
Ok(())
}
}

#[derive(Debug, Deserialize, Copy, Clone, ValueEnum)]
#[serde(rename_all = "lowercase")]
pub enum FontSlant {
Normal,
Italic,
Oblique,
}

impl From<FontSlant> for pango::Style {
fn from(value: FontSlant) -> Self {
match value {
FontSlant::Normal => Self::Normal,
FontSlant::Italic => Self::Italic,
FontSlant::Oblique => Self::Oblique,
}
}
}

#[derive(Debug, Deserialize, Copy, Clone, ValueEnum)]
#[serde(rename_all = "lowercase")]
pub enum FontWeight {
Thin,
Ultralight,
Light,
Semilight,
Book,
Normal,
Medium,
Semibold,
Bold,
Ultrabold,
Heavy,
Ultraheavy,
}

impl From<FontWeight> for pango::Weight {
fn from(value: FontWeight) -> Self {
match value {
FontWeight::Thin => Self::Thin,
FontWeight::Ultralight => Self::Ultralight,
FontWeight::Light => Self::Light,
FontWeight::Semilight => Self::Semilight,
FontWeight::Book => Self::Book,
FontWeight::Normal => Self::Normal,
FontWeight::Medium => Self::Medium,
FontWeight::Semibold => Self::Semibold,
FontWeight::Bold => Self::Bold,
FontWeight::Ultrabold => Self::Ultrabold,
FontWeight::Heavy => Self::Heavy,
FontWeight::Ultraheavy => Self::Ultraheavy,
}
}
}

// Pango scale factor
const PANGO_SCALE: i32 = 1024;

#[inline]
/// Convert Pango units to pixels
fn pango_pixels(d: i32) -> i32 {
(d + 512) >> 10
}
}

#[cfg(not(feature = "pango"))]
pub use cairo_backend::*;
#[cfg(feature = "pango")]
pub use pango_backend::*;
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod comm;
pub mod config;
pub mod event;
pub mod event_loop;
pub mod font;
pub mod render;
pub mod seat;
pub mod state;
Expand Down
Loading
Loading