add evfbo (evdev + fbdev + stdout) backend

During a normal boot in the postmarketOS initramfs, the TTY is not
easily accessible. The on-screen keyboard is expected to send the
password to stdout instead of to a uinput device. Add the evfbo backend
to support full-disk encryption.
This commit is contained in:
Richard Acayan 2025-07-21 18:42:46 -04:00
parent 7b38edb656
commit c92b571c16
4 changed files with 185 additions and 0 deletions

View file

@ -37,6 +37,10 @@ bindgen = "0.71"
name = "ufkbd-evfb" name = "ufkbd-evfb"
path = "src/ufkbd_evfb.rs" path = "src/ufkbd_evfb.rs"
[[bin]]
name = "ufkbd-evfbo"
path = "src/ufkbd_evfbo.rs"
[[bin]] [[bin]]
name = "ufkbd-gnome" name = "ufkbd-gnome"
path = "src/ufkbd_gnome.rs" path = "src/ufkbd_gnome.rs"

8
src/stdio/mod.rs Normal file
View file

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2025, Richard Acayan. All rights reserved.
*/
mod output;
pub use self::output::Output;

92
src/stdio/output.rs Normal file
View file

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2025, Richard Acayan. All rights reserved.
*/
use crate::core::Keyboard;
use crate::core::Layout;
use std::iter::FromIterator;
use xkeysym::Keysym;
pub struct Output {
back: String,
front: String,
complete: bool,
}
impl Output {
pub fn new() -> Self
{
Self {
back: String::new(),
front: String::new(),
complete: false,
}
}
#[inline(always)]
pub fn complete(&self) -> bool
{
self.complete
}
}
impl Keyboard for Output {
fn key_supported(&self, sym: Keysym) -> bool
{
match sym {
Keysym::Return => true,
Keysym::BackSpace => true,
Keysym::Delete => true,
Keysym::Left => true,
Keysym::Right => true,
_ => false,
}
}
fn press(&mut self, sym: Keysym)
{
match sym {
Keysym::Return => {
let front = self.front.chars().rev();
let front = String::from_iter(front);
println!("{}{}", self.back, front);
self.complete = true;
},
Keysym::BackSpace => {
self.back.pop();
},
Keysym::Delete => {
self.front.pop();
},
Keysym::Left => {
if let Some(c) = self.back.pop() {
self.front.push(c);
}
},
Keysym::Right => {
if let Some(c) = self.front.pop() {
self.back.push(c);
}
},
_ => (),
}
}
fn release(&mut self, _: Keysym)
{
}
fn text(&mut self, text: &str)
{
for c in text.chars() {
self.back.push(c);
}
}
fn change_layout(&mut self, _: &Layout)
{
}
}

81
src/ufkbd_evfbo.rs Normal file
View file

@ -0,0 +1,81 @@
// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2025, Richard Acayan. All rights reserved.
*/
mod core;
mod evdev;
mod fbdev;
mod linux;
mod stdio;
use crate::core::Button;
use crate::core::Configuration;
use crate::core::Display;
use crate::core::Graphics;
use crate::core::Layout;
use crate::evdev::Touchscreen;
use crate::fbdev::Framebuffer;
use crate::stdio::Output;
use polling::Event;
use polling::Events;
use polling::Poller;
use signal_hook::consts::signal;
use signal_hook::iterator::Signals;
use std::sync::Arc;
use std::sync::Mutex;
use std::time::Instant;
fn main()
{
let cfg = Configuration::load().unwrap();
let disp = Framebuffer::new(&cfg).unwrap();
let gfx = Graphics::new(disp, &cfg);
let gfx = Mutex::new(gfx);
let gfx = Arc::new(gfx);
let kbd = Output::new();
let layout = Layout::load(&cfg).unwrap();
let mut btn = Button::new(&cfg, layout, kbd, gfx.clone());
btn.set_text_supported(true);
{
let mut gfx = gfx.lock().unwrap();
let (width, height) = gfx.display().size();
gfx.resize(btn.layout(), btn.mod_state(), width, height);
}
let mut touchscreen = Touchscreen::new(btn, &cfg).unwrap();
let mut events = Events::new();
let poller = Poller::new().unwrap();
let ts_evt = Event::readable(0);
let mut sigs = Signals::new([
signal::SIGHUP,
signal::SIGINT,
signal::SIGPIPE,
signal::SIGTERM,
]).unwrap();
unsafe {
poller.add(&touchscreen, ts_evt).unwrap();
}
while sigs.pending().next().is_none() && !touchscreen.button().keyboard().complete() {
let timer = touchscreen.button().next_time().map(|t| t - Instant::now());
events.clear();
poller.modify(&touchscreen, ts_evt).unwrap();
poller.wait(&mut events, timer).unwrap();
if !events.is_empty() {
touchscreen.handle_events();
}
touchscreen.dispatch_timers();
}
poller.delete(&touchscreen).unwrap();
}