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
68 changes: 29 additions & 39 deletions src/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2026, Nathan Gill

use std::{
os::{fd::AsFd, raw::c_void},
ptr::NonNull,
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
use std::sync::{
Arc,
atomic::{AtomicBool, Ordering},
};

use nix::{
sys::mman::{MapFlags, ProtFlags, mmap, munmap},
unistd::ftruncate,
};
use tracing::warn;
use tracing::{trace, warn};
use wayland_client::{
Dispatch, QueueHandle,
protocol::{wl_buffer, wl_shm, wl_surface},
};

use crate::{state::NLockState, util::open_shm};
use crate::{shm::NLockShm, state::NLockState, surface::NLockSurfaceTracking};

pub struct NLockBuffer {
buffer: wl_buffer::WlBuffer,
data: NonNull<c_void>,

// required to keep shm mapping alive
_shm: NLockShm,

pub width: i32,
pub height: i32,
pub size: usize,
pub state: Arc<NLockBufferState>,
pub surface: cairo::ImageSurface,
pub context: cairo::Context,
Expand All @@ -38,6 +31,13 @@ pub struct NLockBufferState {
pub in_use: AtomicBool,
}

pub struct NLockCommitArgs<'a> {
pub surface: &'a wl_surface::WlSurface,
pub scale: i32,
pub output: u32,
pub tracking: &'a mut NLockSurfaceTracking,
}

pub struct NLockBufferGuard<'a> {
wl_buffer: &'a wl_buffer::WlBuffer,
state: &'a Arc<NLockBufferState>,
Expand All @@ -47,13 +47,16 @@ pub struct NLockBufferGuard<'a> {
impl<'a> NLockBufferGuard<'a> {
/// Attaches, damages, and commits the current buffer onto the specified
/// surface.
pub fn commit_to(&mut self, surface: &wl_surface::WlSurface, scale: i32) {
surface.attach(Some(self.wl_buffer), 0, 0);
surface.set_buffer_scale(scale);
surface.damage(0, 0, i32::MAX, i32::MAX);
surface.commit();
pub fn commit_to(&mut self, args: NLockCommitArgs, qh: &QueueHandle<NLockState>) {
args.surface.set_buffer_scale(args.scale);
args.surface.attach(Some(self.wl_buffer), 0, 0);
args.surface.damage(0, 0, i32::MAX, i32::MAX);
args.surface.frame(qh, args.output);
args.surface.commit();

self.committed = true;
args.tracking.ready = false;
args.tracking.dirty = false;
}
}

Expand All @@ -67,7 +70,7 @@ impl<'a> Drop for NLockBufferGuard<'a> {

impl NLockBuffer {
pub fn new(
shm: &wl_shm::WlShm,
wl_shm: &wl_shm::WlShm,
width: i32,
height: i32,
format: wl_shm::Format,
Expand All @@ -84,26 +87,14 @@ impl NLockBuffer {
let stride = width * 4;
let size = stride * height;

let fd = open_shm()?;
ftruncate(&fd, size as i64).ok()?;

let data = unsafe {
mmap(
None,
std::num::NonZeroUsize::new(size as usize)?,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_SHARED,
&fd,
0,
)
.ok()?
};
let mut shm = NLockShm::new(size as i64)?;
let data = shm.map().ok()?;

let state = Arc::new(NLockBufferState {
in_use: AtomicBool::new(false),
});

let pool = shm.create_pool(fd.as_fd(), size, qh, ());
let pool = wl_shm.create_pool(shm.fd(), size, qh, ());
let buffer = pool.create_buffer(0, width, height, stride, format, qh, state.clone());

pool.destroy();
Expand All @@ -123,10 +114,9 @@ impl NLockBuffer {

Some(Self {
buffer,
data,
_shm: shm,
width,
height,
size: size as usize,
state,
surface,
context,
Expand All @@ -150,7 +140,6 @@ impl NLockBuffer {
impl Drop for NLockBuffer {
fn drop(&mut self) {
self.buffer.destroy();
let _ = unsafe { munmap(self.data, self.size) };
}
}

Expand All @@ -164,6 +153,7 @@ impl Dispatch<wl_buffer::WlBuffer, Arc<NLockBufferState>> for NLockState {
_: &QueueHandle<Self>,
) {
if let wl_buffer::Event::Release = event {
trace!("release {:p}", Arc::as_ptr(data),);
data.in_use.store(false, Ordering::Release);
}
}
Expand Down
26 changes: 13 additions & 13 deletions src/comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ where
let size_buf = msg_buf.len().to_ne_bytes();

write(&self.tx, &size_buf)?;
write(&self.tx, msg_buf)?;
write(&self.tx, &msg_buf)?;

Ok(())
}
Expand Down Expand Up @@ -93,37 +93,37 @@ where

pub trait AsBytes {
/// Convert to a bytes-like representation of the object
fn as_bytes(&self) -> &[u8];
fn as_bytes(&self) -> Vec<u8>;
}

impl AsBytes for String {
fn as_bytes(&self) -> &[u8] {
self.as_bytes()
fn as_bytes(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
}

impl AsBytes for bool {
fn as_bytes(&self) -> &[u8] {
fn as_bytes(&self) -> Vec<u8> {
match self {
false => &[0u8],
true => &[1u8],
false => vec![0u8],
true => vec![1u8],
}
}
}

impl AsBytes for AuthState {
fn as_bytes(&self) -> &[u8] {
fn as_bytes(&self) -> Vec<u8> {
match self {
Self::Idle => &[0u8],
Self::Success => &[1u8],
Self::Fail => &[2u8],
Self::Idle => vec![0u8],
Self::Success => vec![1u8],
Self::Fail => vec![2u8],
}
}
}

impl AsBytes for () {
fn as_bytes(&self) -> &[u8] {
&[0u8]
fn as_bytes(&self) -> Vec<u8> {
vec![0u8]
}
}

Expand Down
42 changes: 27 additions & 15 deletions src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub enum EventType {
KeyboardRepeat = 1,
AuthStateChanged = 2,
Debug = 3,
Interrupt = 4,
}

impl NLockState {
Expand Down Expand Up @@ -90,6 +91,10 @@ impl NLockState {
warn!("Received debug event, but not in debug mode, ignoring");
}
}
EventType::Interrupt => {
// interrupt the event poll, something happened
self.interrupt.read()?;
}
_ => {}
},
Event::Timeout { tag } => {
Expand All @@ -111,25 +116,32 @@ impl NLockState {
}

fn re_render(&mut self, qh: &QueueHandle<NLockState>) {
// Re-render only if state was updated
if self.state_changed.load(Ordering::Relaxed)
&& let Some(shm) = &self.shm
{
let auth_state = self.auth_state.clone().load(Ordering::Relaxed);

for i in 0..self.surfaces.len() {
self.surfaces[i].render(
&self.config,
auth_state,
self.password.chars().count(),
self.background_image.as_ref(),
shm,
qh,
);
if self.state_changed.load(Ordering::Relaxed) {
// mark as dirty when state changes
for (_, surface) in self.surfaces.iter_mut() {
surface.tracking.dirty = true;
}

self.state_changed.store(false, Ordering::Relaxed);
}

if let Some(shm) = &self.shm {
let auth_state = self.auth_state.clone().load(Ordering::Relaxed);

for (_, surface) in self.surfaces.iter_mut() {
// only render when surface advertised as available
if surface.tracking.dirty && surface.tracking.ready {
surface.render(
&self.config,
auth_state,
self.password.chars().count(),
self.background_image.as_ref(),
shm,
qh,
);
}
}
}
}

pub fn event_loop_cycle(&mut self, event_queue: &mut EventQueue<NLockState>) -> Result<()> {
Expand Down
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod event_loop;
pub mod font;
pub mod render;
pub mod seat;
pub mod shm;
pub mod signal;
pub mod state;
pub mod surface;
Expand Down Expand Up @@ -99,7 +100,7 @@ fn start(config: NLockConfig, debug: bool) -> Result<()> {
}
}

state.unlock(&qh);
state.unlock();
event_queue.roundtrip(&mut state)?;

if let Err(e) = auth_comm.stop.write(true) {
Expand Down
110 changes: 110 additions & 0 deletions src/shm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (c) 2026, Nathan Gill

use std::{
os::{
fd::{AsFd, BorrowedFd, OwnedFd},
raw::c_void,
},
ptr::NonNull,
sync::atomic::{AtomicU64, Ordering},
};

use anyhow::{Result, anyhow};
use nix::{
fcntl::OFlag,
sys::{
mman::{MapFlags, ProtFlags, mmap, munmap, shm_open, shm_unlink},
stat::Mode,
},
unistd::{ftruncate, getpid},
};
use tracing::{debug, warn};

static SHM_NUM: AtomicU64 = AtomicU64::new(0);

pub struct NLockShm {
fd: OwnedFd,
name: String,
size: usize,
data: Option<NonNull<c_void>>,
}

impl NLockShm {
pub fn new(size: i64) -> Option<Self> {
if size <= 0 {
return None;
}

let name = format!(
"/nlock-{}-{}",
getpid(),
SHM_NUM.fetch_add(1, Ordering::Relaxed),
);
debug!("Trying shm name '{}'", name);

let fd = match shm_open(
name.as_str(),
OFlag::O_RDWR | OFlag::O_CREAT | OFlag::O_EXCL,
Mode::S_IRUSR | Mode::S_IWUSR,
) {
Ok(f) => f,
Err(e) => {
warn!("Failed to open shm '{}': {:?}", name, e);
return None;
}
};

ftruncate(&fd, size).ok()?;

Some(Self {
fd,
name,
size: size as usize,
data: None,
})
}

pub fn map(&mut self) -> Result<NonNull<c_void>> {
if let Some(d) = self.data {
return Err(anyhow!("shm '{}' already mapped at {:p}", self.name, d));
}

let data = unsafe {
mmap(
None,
std::num::NonZeroUsize::new(self.size).ok_or(anyhow!("shm size was zero"))?,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_SHARED,
&self.fd,
0,
)
}?;

self.data = Some(data);

Ok(data)
}

pub fn data(&self) -> Option<NonNull<c_void>> {
self.data
}

pub fn fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}

impl Drop for NLockShm {
fn drop(&mut self) {
if let Some(data) = self.data
&& let Err(e) = unsafe { munmap(data, self.size) }
{
warn!("Failed to unmap shm '{}': {:?}", self.name, e);
}

if let Err(e) = shm_unlink(self.name.as_str()) {
warn!("Failed to unlink shm '{}': {:?}", self.name, e);
}
}
}
Loading
Loading