feat: add session-lock examples

This commit is contained in:
drendog 2026-01-18 17:45:20 +01:00
parent a2d6dd5f9f
commit 6264cca368
Signed by: dwenya
GPG key ID: 8DD77074645332D0
16 changed files with 587 additions and 1 deletions

27
Cargo.lock generated
View file

@ -3287,6 +3287,33 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "session-lock"
version = "0.2.0"
dependencies = [
"env_logger",
"layer-shika",
"log",
]
[[package]]
name = "session-lock-selectors"
version = "0.2.0"
dependencies = [
"env_logger",
"layer-shika",
"log",
]
[[package]]
name = "session-lock-standalone"
version = "0.2.0"
dependencies = [
"env_logger",
"layer-shika",
"log",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"

View file

@ -27,6 +27,9 @@ members = [
"examples/declarative-config", "examples/declarative-config",
"examples/event-loop", "examples/event-loop",
"examples/runtime-surface-config", "examples/runtime-surface-config",
"examples/session-lock",
"examples/session-lock-selectors",
"examples/session-lock-standalone",
"examples/simple-popup", "examples/simple-popup",
] ]

View file

@ -13,6 +13,8 @@ cargo run -p multi-surface
cargo run -p declarative-config cargo run -p declarative-config
cargo run -p runtime-surface-config cargo run -p runtime-surface-config
cargo run -p simple-popup cargo run -p simple-popup
cargo run -p session-lock
cargo run -p session-lock-standalone
# Or from the example directory # Or from the example directory
cd examples/simple-bar cd examples/simple-bar
@ -29,6 +31,8 @@ cargo run
4. **event-loop** - Explore event loop integration with timers and channels 4. **event-loop** - Explore event loop integration with timers and channels
5. **runtime-surface-config** - Surface configuration manipulation at runtime 5. **runtime-surface-config** - Surface configuration manipulation at runtime
6. **simple-popup** - Showing popups and content sizing 6. **simple-popup** - Showing popups and content sizing
7. **session-lock-standalone** - Minimal lock-only application without layer surfaces
8. **session-lock** - Lock screen with layer shell surfaces (status bar + lock)
## Common Patterns ## Common Patterns

View file

@ -0,0 +1,14 @@
[package]
name = "session-lock-selectors"
version.workspace = true
edition.workspace = true
license.workspace = true
publish = false
[lints]
workspace = true
[dependencies]
layer-shika = { path = "../.." }
env_logger = "0.11.7"
log.workspace = true

View file

@ -0,0 +1,50 @@
# Session Lock Selectors Example
This example demonstrates using **selectors** to configure session lock surfaces with different properties per output. It shows how to set different themes or configurations for lock screens on different monitors.
## Key Features
- Creates both a layer shell surface (status bar) and session lock surfaces
- Uses `select_lock()` to apply configurations to specific lock surfaces
- Demonstrates per-output theming (dark theme on primary output)
- Shows how to handle lock/unlock callbacks with selectors
## Selector Usage
```rust
// Apply to all lock surfaces
shell.select_lock(Surface::all())
.on_callback_with_args("unlock_requested", handler);
// Apply only to primary output's lock surface
shell.select_lock(Output::Primary)
.set_property("theme", &Value::from("dark"))?;
// Apply to regular layer surface
shell.select(Surface::named("Main"))
.on_callback("lock_requested", handler);
```
## Run
```bash
cargo run -p session-lock-selectors
```
## Usage Flow
1. Application starts with a status bar showing a "Lock" button
2. Click the "Lock" button to activate the session lock
3. Lock screens appear on all outputs
4. Primary output's lock screen uses dark theme
5. Enter any password and click "Unlock" to deactivate
6. Application returns to normal mode with status bar
## When to Use This Pattern
Use session lock selectors when you need to:
- Configure lock screens differently per output
- Apply different themes or properties to specific monitors
- Handle callbacks consistently across all lock surfaces
- Combine layer shell surfaces with session locks

View file

@ -0,0 +1,51 @@
use layer_shika::prelude::*;
use layer_shika::slint_interpreter::Value;
use std::path::PathBuf;
use std::rc::Rc;
fn main() -> Result<()> {
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.init();
let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/lock.slint");
let mut shell = Shell::from_file(ui_path)
.surface("Main")
.height(42)
.exclusive_zone(42)
.namespace("session-lock-selectors-example")
.build()?;
let lock = Rc::new(shell.create_session_lock("LockScreen")?);
let lock_clone = Rc::clone(&lock);
shell.select_lock(Surface::all()).on_callback_with_args(
"unlock_requested",
move |args, _ctx| {
if let Some(password) = args.first() {
log::info!("Password entered: {:?}", password);
}
lock_clone.deactivate().ok();
},
);
shell
.select_lock(Surface::all())
.on_callback("cancel_requested", |_ctx| {});
shell
.select_lock(Output::Primary)
.set_property("theme", &Value::from(slint::SharedString::from("dark")))?;
let lock_clone = Rc::clone(&lock);
shell
.select(Surface::named("Main"))
.on_callback("lock_requested", move |_ctx| {
lock_clone.activate().ok();
});
shell.run()?;
Ok(())
}

View file

@ -0,0 +1,118 @@
import {
Button,
LineEdit,
HorizontalBox,
VerticalBox,
} from "std-widgets.slint";
export component Main inherits Window {
background: transparent;
callback lock_requested();
default-font-size: 16px;
HorizontalLayout {
alignment: center;
Button {
text: "Lock Screen";
clicked => root.lock_requested();
}
}
}
export component LockScreen inherits Window {
out property <string> password;
in property <string> theme: "light";
property <bool> is_primary;
callback unlock_requested(string);
callback cancel_requested();
is_primary: theme == "dark" ? true : false;
background: theme == "dark" ? #1a1a1a : #f0f0f0;
Rectangle {
width: 100%;
height: 100%;
HorizontalLayout {
alignment: center;
VerticalLayout {
alignment: center;
spacing: 20px;
Text {
text: "Session Locked";
font-size: 32px;
color: theme == "dark" ? #ffffff : #000000;
horizontal-alignment: center;
}
HorizontalLayout {
alignment: center;
if is_primary: Rectangle {
width: self.preferred-width;
height: 30px;
background: #4a90e2;
border-radius: 15px;
HorizontalLayout {
padding: 8px;
Text {
text: "Primary Monitor";
color: #ffffff;
font-size: 12px;
}
}
}
}
Text {
text: "Enter password to unlock";
color: theme == "dark" ? #cccccc : #333333;
horizontal-alignment: center;
}
VerticalLayout {
width: 300px;
spacing: 12px;
LineEdit {
text: root.password;
placeholder-text: "Password";
edited => {
root.password = self.text;
}
}
HorizontalLayout {
spacing: 12px;
Button {
text: "Unlock";
primary: true;
clicked => {
root.unlock_requested(root.password);
}
}
Button {
text: "Cancel";
clicked => {
root.cancel_requested();
}
}
}
}
Text {
text: "Theme: " + theme;
color: theme == "dark" ? #888888 : #666666;
font-size: 10px;
horizontal-alignment: center;
}
}
}
}
}

View file

@ -0,0 +1,14 @@
[package]
name = "session-lock-standalone"
version.workspace = true
edition.workspace = true
license.workspace = true
publish = false
[lints]
workspace = true
[dependencies]
layer-shika = { path = "../.." }
env_logger = "0.11.7"
log.workspace = true

View file

@ -0,0 +1,53 @@
# Session Lock Standalone Example
This example demonstrates creating a **standalone session lock application** without any layer shell surfaces. This is useful for dedicated lock screen applications that don't need persistent UI elements.
## Key Differences from Regular Session Lock
### Regular Session Lock (`session-lock`)
- Creates a layer shell surface (status bar, panel, etc.)
- Requires `wlr-layer-shell` protocol
- Lock is activated via button click or external trigger
- Application has persistent UI even when unlocked
### Standalone Session Lock (this example)
- **No layer shell surfaces** - minimal mode
- Does NOT require `wlr-layer-shell` protocol
- Lock activates immediately on startup
- Application exits when lock is deactivated
- Lighter weight and simpler architecture
## Usage
```bash
cargo run --package session-lock-standalone
```
The lock screen will appear immediately on all outputs. Enter any password and click "Unlock" to deactivate and exit.
## Code Highlights
```rust
// Build shell WITHOUT calling .surface() - this creates minimal mode
let mut shell = Shell::from_file(ui_path).build()?;
// Create and activate the session lock immediately
let lock = Rc::new(shell.create_session_lock("LockScreen")?);
lock.activate()?;
// Shell runs until lock is deactivated
shell.run()?;
```
## When to Use This Pattern
Use standalone session locks for:
- Dedicated lock screen applications
- Screen locker daemons that only show UI when locking
- Simpler lock-only tools without status bars
- Testing and development of lock screens in isolation
Use regular session locks with layer surfaces when:
- You need persistent UI (status bar, panel, dock)
- Lock is one feature among others in your application
- You want to trigger lock activation from UI elements

View file

@ -0,0 +1,44 @@
use layer_shika::prelude::*;
use layer_shika::slint_interpreter::Value;
use std::path::PathBuf;
use std::rc::Rc;
fn main() -> Result<()> {
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.init();
let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/lock.slint");
let mut shell = Shell::from_file(ui_path).build()?;
let lock = Rc::new(shell.create_session_lock("LockScreen")?);
let lock_clone = Rc::clone(&lock);
shell.select_lock(Surface::all()).on_callback_with_args(
"unlock_requested",
move |args, _ctx| {
if let Some(password) = args.first() {
log::info!("Password entered: {:?}", password);
}
lock_clone.deactivate().ok();
},
);
shell
.select_lock(Surface::all())
.on_callback("cancel_requested", |_ctx| {
log::info!("Cancel requested button pressed");
});
shell
.select_lock(Surface::all())
.set_property("theme", &Value::from(slint::SharedString::from("dark")))
.ok();
lock.activate()?;
shell.run()?;
Ok(())
}

View file

@ -0,0 +1,79 @@
import { Button, LineEdit, HorizontalBox } from "std-widgets.slint";
// Standalone session lock - no layer surface needed!
// This component is only displayed when the lock is activated.
export component LockScreen inherits Window {
in-out property <string> password;
in-out property <string> theme: "dark";
callback unlock_requested(string);
callback cancel_requested();
background: theme == "dark" ? #1a1a1a : #f0f0f0;
Rectangle {
width: 100%;
height: 100%;
HorizontalLayout {
alignment: center;
VerticalLayout {
alignment: center;
spacing: 20px;
Text {
text: "🔒 Session Locked";
font-size: 32px;
color: theme == "dark" ? #ffffff : #000000;
}
Text {
text: "This is a standalone lock screen without any layer surfaces";
font-size: 14px;
color: theme == "dark" ? #cccccc : #666666;
}
Text {
text: "Enter password to unlock";
font-size: 16px;
color: theme == "dark" ? #ffffff : #000000;
}
VerticalLayout {
width: 300px;
spacing: 12px;
LineEdit {
placeholder-text: "Password";
text: root.password;
edited => {
root.password = self.text;
}
accepted => {
root.unlock_requested(root.password);
}
}
HorizontalLayout {
spacing: 12px;
Button {
text: "Unlock";
primary: true;
clicked => {
root.unlock_requested(root.password);
}
}
Button {
text: "Cancel";
clicked => {
root.cancel_requested();
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,14 @@
[package]
name = "session-lock"
version.workspace = true
edition.workspace = true
license.workspace = true
publish = false
[lints]
workspace = true
[dependencies]
layer-shika = { path = "../.." }
env_logger = "0.11.7"
log.workspace = true

View file

@ -0,0 +1,14 @@
# session-lock example
Demonstrates the ext-session-lock-v1 integration with a simple lock screen, activated by a bar button click.
## Run
```bash
cargo run -p session-lock
```
## Notes
- Requires a compositor that supports ext-session-lock-v1.
- The example unlocks when the password field is non-empty.

View file

@ -0,0 +1,41 @@
use layer_shika::prelude::*;
use std::path::PathBuf;
use std::rc::Rc;
fn main() -> Result<()> {
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.init();
let ui_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("ui/lock.slint");
let mut shell = Shell::from_file(ui_path)
.surface("Main")
.height(42)
.exclusive_zone(42)
.namespace("session-lock-example")
.build()?;
let lock = Rc::new(shell.create_session_lock("LockScreen")?);
let lock_clone = Rc::clone(&lock);
shell
.select_lock(Surface::named("LockScreen"))
.on_callback_with_args("unlock_requested", move |args, _ctx| {
if let Some(password) = args.first() {
log::info!("Password entered: {:?}", password);
}
lock_clone.deactivate().ok();
});
let lock_clone = Rc::clone(&lock);
shell
.select(Surface::named("Main"))
.on_callback("lock_requested", move |_ctx| {
lock_clone.activate().ok();
});
shell.run()?;
Ok(())
}

View file

@ -0,0 +1,60 @@
import { Button, LineEdit, HorizontalBox } from "std-widgets.slint";
export component Main inherits Window {
background: transparent;
callback lock_requested();
default-font-size: 16px;
HorizontalLayout {
alignment: center;
Button {
text: "Lock";
clicked => root.lock_requested();
}
}
}
export component LockScreen inherits Window {
in-out property <string> password;
callback unlock_requested(string);
Rectangle {
width: 100%;
height: 100%;
HorizontalLayout {
alignment: center;
VerticalLayout {
alignment: center;
Text {
text: "Session Locked";
font-size: 28px;
}
Text {
text: "Enter a password to unlock";
}
VerticalLayout {
width: 255px;
LineEdit {
text: root.password;
edited => {
root.password = self.text;
}
}
Button {
text: "Unlock";
clicked => {
root.unlock_requested(root.password);
}
}
}
}
}
}
}

View file

@ -1,7 +1,7 @@
import { VerticalBox, HorizontalBox } from "std-widgets.slint"; import { VerticalBox, HorizontalBox } from "std-widgets.slint";
export component Bar inherits Window { export component Bar inherits Window {
default-font-size: 16px; //default-font-size: 16px;
HorizontalBox { HorizontalBox {
// Left section // Left section