Compare commits
10 Commits
f4f8e94e6e
...
v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
601c9677b7
|
|||
|
4d90e52c36
|
|||
|
55c8fc0a4f
|
|||
|
aa59067bb3
|
|||
|
c05d987ff1
|
|||
|
a5115c0a15
|
|||
|
20cc6e210e
|
|||
|
3038c29c57
|
|||
|
50c5de5371
|
|||
|
446426ea47
|
@@ -0,0 +1,17 @@
|
|||||||
|
name: Install system dependencies
|
||||||
|
description: Install system dependencies required for building rustlock
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Install system dependencies (Ubuntu/Debian)
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
curl \
|
||||||
|
llvm clang libclang-dev \
|
||||||
|
pkg-config \
|
||||||
|
libglib2.0-dev libcairo2-dev libpango1.0-dev libatk1.0-dev \
|
||||||
|
libgdk-pixbuf-2.0-dev libpam0g-dev libdbus-1-dev \
|
||||||
|
libwayland-dev libxkbcommon-dev
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
name: Multi-Distro Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["master", "main"]
|
|
||||||
pull_request:
|
|
||||||
branches: ["master", "main"]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-ubuntu:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- features: "default"
|
|
||||||
args: ""
|
|
||||||
- features: "no-default-features"
|
|
||||||
args: "--no-default-features"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Install Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y \
|
|
||||||
llvm clang libclang-dev \
|
|
||||||
pkg-config \
|
|
||||||
libglib2.0-dev libcairo2-dev libpango1.0-dev libatk1.0-dev \
|
|
||||||
libpam0g-dev libdbus-1-dev \
|
|
||||||
libwayland-dev libxkbcommon-dev
|
|
||||||
- name: Build (${{ matrix.features }})
|
|
||||||
run: cargo build --verbose ${{ matrix.args }}
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --verbose ${{ matrix.args }}
|
|
||||||
- name: Build release for size check
|
|
||||||
run: cargo build --release ${{ matrix.args }}
|
|
||||||
- name: Check binary size
|
|
||||||
run: |
|
|
||||||
size=$(wc -c < target/release/rustlock)
|
|
||||||
echo "Binary size (${{ matrix.features }}): $size bytes"
|
|
||||||
if [ "${{ matrix.features }}" = "no-default-features" ] && [ $size -gt 3000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 3MB threshold for no-networking build"
|
|
||||||
exit 1
|
|
||||||
elif [ "${{ matrix.features }}" = "default" ] && [ $size -gt 5000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 5MB threshold for networking build"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Binary size within limits"
|
|
||||||
|
|
||||||
build-debian:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: debian:stable-slim
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- features: "default"
|
|
||||||
args: ""
|
|
||||||
- features: "no-default-features"
|
|
||||||
args: "--no-default-features"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y \
|
|
||||||
curl \
|
|
||||||
llvm clang libclang-dev \
|
|
||||||
pkg-config \
|
|
||||||
libglib2.0-dev libcairo2-dev libpango1.0-dev libatk1.0-dev \
|
|
||||||
libgdk-pixbuf-2.0-dev libpam0g-dev libdbus-1-dev \
|
|
||||||
libwayland-dev libxkbcommon-dev
|
|
||||||
- name: Install Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Build (${{ matrix.features }})
|
|
||||||
run: cargo build --verbose ${{ matrix.args }}
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --verbose ${{ matrix.args }}
|
|
||||||
- name: Build release for size check
|
|
||||||
run: cargo build --release ${{ matrix.args }}
|
|
||||||
- name: Check binary size
|
|
||||||
run: |
|
|
||||||
size=$(wc -c < target/release/rustlock)
|
|
||||||
echo "Binary size (${{ matrix.features }}): $size bytes"
|
|
||||||
if [ "${{ matrix.features }}" = "no-default-features" ] && [ $size -gt 3000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 3MB threshold for no-networking build"
|
|
||||||
exit 1
|
|
||||||
elif [ "${{ matrix.features }}" = "default" ] && [ $size -gt 5000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 5MB threshold for networking build"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Binary size within limits"
|
|
||||||
|
|
||||||
build-fedora:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: fedora:latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- features: "default"
|
|
||||||
args: ""
|
|
||||||
- features: "no-default-features"
|
|
||||||
args: "--no-default-features"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
dnf install -y \
|
|
||||||
curl \
|
|
||||||
llvm clang clang-devel \
|
|
||||||
pkgconfig \
|
|
||||||
glib2-devel cairo-devel cairo-gobject-devel pango-devel atk-devel \
|
|
||||||
gdk-pixbuf2-devel pam-devel dbus-devel \
|
|
||||||
wayland-devel libxkbcommon-devel
|
|
||||||
- name: Install Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Build (${{ matrix.features }})
|
|
||||||
run: cargo build --verbose ${{ matrix.args }}
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --verbose ${{ matrix.args }}
|
|
||||||
- name: Build release for size check
|
|
||||||
run: cargo build --release ${{ matrix.args }}
|
|
||||||
- name: Check binary size
|
|
||||||
run: |
|
|
||||||
size=$(wc -c < target/release/rustlock)
|
|
||||||
echo "Binary size (${{ matrix.features }}): $size bytes"
|
|
||||||
if [ "${{ matrix.features }}" = "no-default-features" ] && [ $size -gt 3000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 3MB threshold for no-networking build"
|
|
||||||
exit 1
|
|
||||||
elif [ "${{ matrix.features }}" = "default" ] && [ $size -gt 5000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 5MB threshold for networking build"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Binary size within limits"
|
|
||||||
|
|
||||||
build-arch:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: archlinux:latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- features: "default"
|
|
||||||
args: ""
|
|
||||||
- features: "no-default-features"
|
|
||||||
args: "--no-default-features"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
pacman -Sy --noconfirm \
|
|
||||||
base-devel \
|
|
||||||
llvm clang pkgconf \
|
|
||||||
glib2 cairo pango atk gdk-pixbuf2 \
|
|
||||||
pam dbus \
|
|
||||||
wayland libxkbcommon
|
|
||||||
- name: Install Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Build (${{ matrix.features }})
|
|
||||||
run: cargo build --verbose ${{ matrix.args }}
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --verbose ${{ matrix.args }}
|
|
||||||
- name: Build release for size check
|
|
||||||
run: cargo build --release ${{ matrix.args }}
|
|
||||||
- name: Check binary size
|
|
||||||
run: |
|
|
||||||
size=$(wc -c < target/release/rustlock)
|
|
||||||
echo "Binary size (${{ matrix.features }}): $size bytes"
|
|
||||||
if [ "${{ matrix.features }}" = "no-default-features" ] && [ $size -gt 3000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 3MB threshold for no-networking build"
|
|
||||||
exit 1
|
|
||||||
elif [ "${{ matrix.features }}" = "default" ] && [ $size -gt 5000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 5MB threshold for networking build"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Binary size within limits"
|
|
||||||
|
|
||||||
build-opensuse:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: opensuse/tumbleweed:latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- features: "default"
|
|
||||||
args: ""
|
|
||||||
- features: "no-default-features"
|
|
||||||
args: "--no-default-features"
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
zypper install -y \
|
|
||||||
curl \
|
|
||||||
llvm clang-devel \
|
|
||||||
pkgconfig \
|
|
||||||
glib2-devel cairo-devel pango-devel atk-devel \
|
|
||||||
gdk-pixbuf-devel pam-devel dbus-1-devel \
|
|
||||||
wayland-devel libxkbcommon-devel
|
|
||||||
- name: Install Rust
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Build (${{ matrix.features }})
|
|
||||||
run: cargo build --verbose ${{ matrix.args }}
|
|
||||||
- name: Run tests
|
|
||||||
run: cargo test --verbose ${{ matrix.args }}
|
|
||||||
- name: Build release for size check
|
|
||||||
run: cargo build --release ${{ matrix.args }}
|
|
||||||
- name: Check binary size
|
|
||||||
run: |
|
|
||||||
size=$(wc -c < target/release/rustlock)
|
|
||||||
echo "Binary size (${{ matrix.features }}): $size bytes"
|
|
||||||
if [ "${{ matrix.features }}" = "no-default-features" ] && [ $size -gt 3000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 3MB threshold for no-networking build"
|
|
||||||
exit 1
|
|
||||||
elif [ "${{ matrix.features }}" = "default" ] && [ $size -gt 5000000 ]; then
|
|
||||||
echo "❌ Binary size exceeds 5MB threshold for networking build"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "✅ Binary size within limits"
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
name: Metrics Collection
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 0 * * 1' # Weekly on Monday at midnight
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches: [main, master]
|
|
||||||
paths:
|
|
||||||
- 'src/**'
|
|
||||||
- 'Cargo.toml'
|
|
||||||
- '.github/workflows/metrics.yml'
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
collect-metrics:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
|
|
||||||
- name: Install Rust with tools
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
components: rustfmt, clippy
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- name: Generate test coverage
|
|
||||||
run: |
|
|
||||||
cargo install cargo-tarpaulin
|
|
||||||
cargo tarpaulin --out Html --out Lcov --output-dir ./coverage
|
|
||||||
|
|
||||||
- name: Upload coverage report
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: coverage-report
|
|
||||||
path: ./coverage
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Count lines of code
|
|
||||||
run: |
|
|
||||||
cargo install tokei
|
|
||||||
tokei --output json > loc.json
|
|
||||||
echo "Lines of code:"
|
|
||||||
tokei --type=Rust
|
|
||||||
|
|
||||||
- name: Upload LOC metrics
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: loc-metrics
|
|
||||||
path: loc.json
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Check compile times
|
|
||||||
run: |
|
|
||||||
echo "=== Compilation Metrics ==="
|
|
||||||
echo "Note: For accurate compile times, run: cargo clean && cargo build --timings"
|
|
||||||
echo ""
|
|
||||||
echo "To optimize compile times:"
|
|
||||||
echo "1. Check for large dependencies with: cargo tree --depth 1"
|
|
||||||
echo "2. Consider feature flag optimization"
|
|
||||||
echo "3. Review workspace structure"
|
|
||||||
|
|
||||||
- name: Dependency analysis
|
|
||||||
run: |
|
|
||||||
cargo install cargo-depgraph
|
|
||||||
cargo depgraph --all-features --dedup-transitive --format dot > deps.dot
|
|
||||||
echo "Dependency graph generated: deps.dot"
|
|
||||||
echo "Total dependencies: $(cargo tree --depth 0 | wc -l)"
|
|
||||||
|
|
||||||
- name: Upload dependency graph
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: dependency-graph
|
|
||||||
path: deps.dot
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
- name: Metrics summary
|
|
||||||
run: |
|
|
||||||
echo "=== Build Metrics Summary ==="
|
|
||||||
echo ""
|
|
||||||
echo "📊 Coverage: HTML and Lcov reports generated"
|
|
||||||
echo "📈 LOC: Lines of code counted and saved"
|
|
||||||
echo "🔗 Dependencies: Graph visualization available"
|
|
||||||
echo ""
|
|
||||||
echo "Artifacts available for 30 days:"
|
|
||||||
echo " - coverage-report/"
|
|
||||||
echo " - loc-metrics/loc.json"
|
|
||||||
echo " - dependency-graph/deps.dot"
|
|
||||||
echo ""
|
|
||||||
echo "Next steps:"
|
|
||||||
echo "1. Review coverage reports to identify untested code"
|
|
||||||
echo "2. Monitor LOC growth over time"
|
|
||||||
echo "3. Analyze dependency graph for optimization opportunities"
|
|
||||||
@@ -17,15 +17,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
|
||||||
- name: Install system dependencies
|
- uses: ./.github/actions/deps
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y \
|
|
||||||
llvm clang libclang-dev \
|
|
||||||
pkg-config \
|
|
||||||
libglib2.0-dev libcairo2-dev libpango1.0-dev libatk1.0-dev \
|
|
||||||
libpam0g-dev libdbus-1-dev \
|
|
||||||
libwayland-dev libxkbcommon-dev
|
|
||||||
|
|
||||||
- name: Install Rust with tools
|
- name: Install Rust with tools
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
@@ -38,12 +30,6 @@ jobs:
|
|||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: cargo clippy -- -D warnings
|
run: cargo clippy -- -D warnings
|
||||||
|
|
||||||
- name: Install cargo-deny
|
|
||||||
run: cargo install cargo-deny
|
|
||||||
|
|
||||||
- name: Check dependencies with cargo-deny
|
|
||||||
run: cargo deny check
|
|
||||||
|
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: cargo test --quiet
|
run: cargo test --quiet
|
||||||
|
|
||||||
|
|||||||
@@ -94,25 +94,20 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: artifacts
|
path: artifacts
|
||||||
|
|
||||||
- name: List artifacts
|
|
||||||
run: ls -la artifacts/
|
|
||||||
|
|
||||||
- name: Determine tag
|
- name: Determine tag
|
||||||
id: tag
|
id: tag
|
||||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
tag_name: ${{ github.ref_name }}
|
tag_name: ${{ github.ref_name }}
|
||||||
name: RustLock v${{ steps.tag.outputs.VERSION }}
|
name: RustLock v${{ steps.tag.outputs.VERSION }}
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
|
||||||
files: |
|
files: |
|
||||||
artifacts/rustlock-default-ubuntu/rustlock
|
artifacts/rustlock-default-ubuntu/rustlock/rustlock:rustlock-default-ubuntu
|
||||||
artifacts/rustlock-no-networking-ubuntu/rustlock
|
artifacts/rustlock-no-networking-ubuntu/rustlock/rustlock:rustlock-no-networking-ubuntu
|
||||||
artifacts/rustlock-default-debian/rustlock
|
artifacts/rustlock-default-debian/rustlock/rustlock:rustlock-default-debian
|
||||||
artifacts/rustlock-default-fedora/rustlock
|
artifacts/rustlock-default-fedora/rustlock/rustlock:rustlock-default-fedora
|
||||||
artifacts/rustlock-default-arch/rustlock
|
artifacts/rustlock-default-arch/rustlock/rustlock:rustlock-default-arch
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ jobs:
|
|||||||
- name: Run cargo audit
|
- name: Run cargo audit
|
||||||
run: cargo audit
|
run: cargo audit
|
||||||
|
|
||||||
- name: Run cargo deny (advisories only)
|
- name: Run cargo deny
|
||||||
run: cargo deny check advisories
|
|
||||||
|
|
||||||
- name: Run cargo deny (full check)
|
|
||||||
run: cargo deny check
|
run: cargo deny check
|
||||||
|
|
||||||
- name: Generate Software Bill of Materials (SBOM)
|
- name: Generate Software Bill of Materials (SBOM)
|
||||||
@@ -65,4 +62,3 @@ jobs:
|
|||||||
echo "✅ Outdated dependencies checked"
|
echo "✅ Outdated dependencies checked"
|
||||||
echo ""
|
echo ""
|
||||||
echo "Next scheduled scan: Weekly (Sunday 00:00 UTC)"
|
echo "Next scheduled scan: Weekly (Sunday 00:00 UTC)"
|
||||||
echo "Manual trigger: Click 'Run workflow' in GitHub Actions"
|
|
||||||
Generated
+45
-11
@@ -1571,6 +1571,18 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
"libc",
|
||||||
|
"plain",
|
||||||
|
"redox_syscall",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@@ -1861,6 +1873,12 @@ version = "0.3.32"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plain"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.18.1"
|
version = "0.18.1"
|
||||||
@@ -2041,6 +2059,15 @@ dependencies = [
|
|||||||
"getrandom 0.2.17",
|
"getrandom 0.2.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.3"
|
version = "1.12.3"
|
||||||
@@ -2213,10 +2240,10 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"toml 1.0.6+spec-1.1.0",
|
"toml 1.0.6+spec-1.1.0",
|
||||||
"upower_dbus",
|
"upower_dbus",
|
||||||
"users",
|
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
"wayland-protocols",
|
"wayland-protocols",
|
||||||
"wayland-protocols-wlr",
|
"wayland-protocols-wlr",
|
||||||
|
"whoami",
|
||||||
"xkbcommon",
|
"xkbcommon",
|
||||||
"zbus",
|
"zbus",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
@@ -2874,16 +2901,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "users"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8_iter"
|
name = "utf8_iter"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -2947,6 +2964,12 @@ dependencies = [
|
|||||||
"wit-bindgen",
|
"wit-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasite"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.114"
|
version = "0.2.114"
|
||||||
@@ -3148,6 +3171,17 @@ version = "0.25.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "whoami"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d"
|
||||||
|
dependencies = [
|
||||||
|
"libredox",
|
||||||
|
"wasite",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ zeroize = "1.7"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = { version = "0.11", default-features = false, features = ["color", "humantime"] }
|
env_logger = { version = "0.11", default-features = false, features = ["color", "humantime"] }
|
||||||
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
|
chrono = { version = "0.4", default-features = false, features = ["clock", "std"] }
|
||||||
users = "0.11"
|
whoami = "1.0"
|
||||||
zbus = { version = "3.15", default-features = false, features = ["tokio"] }
|
zbus = { version = "3.15", default-features = false, features = ["tokio"] }
|
||||||
mpris = "2.0"
|
mpris = "2.0"
|
||||||
upower_dbus = "0.3"
|
upower_dbus = "0.3"
|
||||||
|
|||||||
@@ -19,5 +19,7 @@ pkgs.rustPlatform.buildRustPackage {
|
|||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
pkgs.pkg-config
|
pkgs.pkg-config
|
||||||
pkgs.rustPlatform.bindgenHook
|
pkgs.rustPlatform.bindgenHook
|
||||||
|
pkgs.rustfmt
|
||||||
|
pkgs.clippy
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +1,10 @@
|
|||||||
# Cargo-deny configuration for RustLock
|
|
||||||
# See: https://embarkstudios.github.io/cargo-deny/
|
|
||||||
|
|
||||||
[advisories]
|
[advisories]
|
||||||
# Check for security vulnerabilities
|
|
||||||
vulnerability = "deny"
|
vulnerability = "deny"
|
||||||
# Check for unmaintained crates
|
ignore = []
|
||||||
unmaintained = "warn"
|
|
||||||
# Check for unsound crates
|
|
||||||
unsound = "deny"
|
|
||||||
# Check for yanked crates
|
|
||||||
yanked = "deny"
|
|
||||||
# Ignore specific advisories
|
|
||||||
ignore = [
|
|
||||||
# Add specific advisory IDs to ignore here if needed
|
|
||||||
]
|
|
||||||
|
|
||||||
# Sources for advisory database
|
|
||||||
[advisories.db]
|
|
||||||
# Use GitHub advisory database
|
|
||||||
github-data = "https://github.com/rustsec/advisory-db"
|
|
||||||
# Update frequency (daily)
|
|
||||||
update-frequency = "daily"
|
|
||||||
|
|
||||||
[bans]
|
[bans]
|
||||||
# Multiple versions of the same crate
|
|
||||||
multiple-versions = "allow"
|
multiple-versions = "allow"
|
||||||
# Wildcard dependencies (e.g., "1.*")
|
|
||||||
wildcards = "deny"
|
|
||||||
|
|
||||||
# Skip specific crates from checks
|
|
||||||
skip = [
|
|
||||||
# Add crate names to skip here if needed
|
|
||||||
]
|
|
||||||
|
|
||||||
[licenses]
|
|
||||||
# Default license policy
|
|
||||||
default = "deny"
|
|
||||||
# Allow these licenses
|
|
||||||
allow = [
|
|
||||||
"MIT",
|
|
||||||
"Apache-2.0",
|
|
||||||
"BSD-2-Clause",
|
|
||||||
"BSD-3-Clause",
|
|
||||||
"ISC",
|
|
||||||
"Zlib",
|
|
||||||
"Unlicense",
|
|
||||||
"CC0-1.0",
|
|
||||||
"MPL-2.0",
|
|
||||||
"AGPL-3.0-or-later", # Our own license
|
|
||||||
]
|
|
||||||
# Copyleft licenses that require special attention
|
|
||||||
copyleft = "warn"
|
|
||||||
# Unknown licenses
|
|
||||||
unlicensed = "deny"
|
|
||||||
|
|
||||||
# Skip specific crates from license checks
|
|
||||||
skip = [
|
|
||||||
# Add crate names to skip here if needed
|
|
||||||
]
|
|
||||||
|
|
||||||
[sources]
|
[sources]
|
||||||
# Allow only these registries
|
|
||||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||||
# Deny git repositories (except for specific cases)
|
|
||||||
allow-git = []
|
allow-git = []
|
||||||
|
|
||||||
[metadata]
|
|
||||||
# Minimum severity level to display
|
|
||||||
severity-threshold = "low"
|
|
||||||
+16
-18
@@ -4,7 +4,7 @@ use std::thread;
|
|||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use pam_client::{Context, ErrorCode, Flag};
|
use pam_client::{Context, ErrorCode, Flag};
|
||||||
use smithay_client_toolkit::reexports::{calloop::channel, calloop::EventLoop};
|
use smithay_client_toolkit::reexports::{calloop::channel, calloop::EventLoop};
|
||||||
use users::get_current_username;
|
use whoami::username;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
const SERVICE_NAME: &str = "rustlock";
|
const SERVICE_NAME: &str = "rustlock";
|
||||||
@@ -35,12 +35,9 @@ impl pam_client::ConversationHandler for LockConversation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_and_run_auth_loop() -> Option<(channel::Sender<Zeroizing<String>>, channel::Channel<bool>)> {
|
pub fn create_and_run_auth_loop(
|
||||||
let username = get_current_username()
|
) -> Option<(channel::Sender<Zeroizing<String>>, channel::Channel<bool>)> {
|
||||||
.expect("Failed to get username")
|
let username = username();
|
||||||
.to_str()
|
|
||||||
.expect("Failed to get non-unicode username")
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let conversation = LockConversation { password: None };
|
let conversation = LockConversation { password: None };
|
||||||
match Context::new(SERVICE_NAME, Some(username.as_str()), conversation) {
|
match Context::new(SERVICE_NAME, Some(username.as_str()), conversation) {
|
||||||
@@ -49,7 +46,10 @@ pub fn create_and_run_auth_loop() -> Option<(channel::Sender<Zeroizing<String>>,
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to initialize PAM context: {:?}", err);
|
error!("Failed to initialize PAM context: {:?}", err);
|
||||||
error!("Ensure that the PAM service '{}' is correctly configured.", SERVICE_NAME);
|
error!(
|
||||||
|
"Ensure that the PAM service '{}' is correctly configured.",
|
||||||
|
SERVICE_NAME
|
||||||
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,17 +67,15 @@ pub fn create_and_run_auth_loop() -> Option<(channel::Sender<Zeroizing<String>>,
|
|||||||
password: Some(password),
|
password: Some(password),
|
||||||
};
|
};
|
||||||
match Context::new(SERVICE_NAME, Some(username.as_str()), conversation) {
|
match Context::new(SERVICE_NAME, Some(username.as_str()), conversation) {
|
||||||
Ok(mut context) => {
|
Ok(mut context) => match context.authenticate(Flag::NONE) {
|
||||||
match context.authenticate(Flag::NONE) {
|
Ok(()) => {
|
||||||
Ok(()) => {
|
auth_res_send.send(true).unwrap();
|
||||||
auth_res_send.send(true).unwrap();
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("Pam authenticate failed with {:?}", err);
|
|
||||||
auth_res_send.send(false).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
Err(err) => {
|
||||||
|
error!("Pam authenticate failed with {:?}", err);
|
||||||
|
auth_res_send.send(false).unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to re-initialize PAM context: {:?}", err);
|
error!("Failed to re-initialize PAM context: {:?}", err);
|
||||||
auth_res_send.send(false).unwrap();
|
auth_res_send.send(false).unwrap();
|
||||||
|
|||||||
+48
-29
@@ -135,15 +135,14 @@ pub struct Config {
|
|||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Self {
|
pub fn load() -> Self {
|
||||||
use clap::CommandFactory;
|
use clap::CommandFactory;
|
||||||
|
|
||||||
let mut config = Config::parse();
|
let mut config = Config::parse();
|
||||||
let cmd = Config::command();
|
let cmd = Config::command();
|
||||||
let matches = cmd.get_matches();
|
let matches = cmd.get_matches();
|
||||||
|
|
||||||
// Helper to check if a value was explicitly set on command line
|
// Helper to check if a value was explicitly set on command line
|
||||||
let is_cli = |key: &str| {
|
let is_cli =
|
||||||
matches.value_source(key) == Some(clap::parser::ValueSource::CommandLine)
|
|key: &str| matches.value_source(key) == Some(clap::parser::ValueSource::CommandLine);
|
||||||
};
|
|
||||||
|
|
||||||
// 1. Config file layer (overrides defaults and themes)
|
// 1. Config file layer (overrides defaults and themes)
|
||||||
let config_path = config.config.clone().unwrap_or_else(|| {
|
let config_path = config.config.clone().unwrap_or_else(|| {
|
||||||
@@ -156,7 +155,7 @@ impl Config {
|
|||||||
if let Ok(file_content) = std::fs::read_to_string(&config_path) {
|
if let Ok(file_content) = std::fs::read_to_string(&config_path) {
|
||||||
if let Ok(table) = toml::from_str::<toml::Table>(&file_content) {
|
if let Ok(table) = toml::from_str::<toml::Table>(&file_content) {
|
||||||
log::debug!("Loaded configuration from {:?}", config_path);
|
log::debug!("Loaded configuration from {:?}", config_path);
|
||||||
|
|
||||||
let merge_bool = |val: &mut bool, key: &str| {
|
let merge_bool = |val: &mut bool, key: &str| {
|
||||||
if !is_cli(key) {
|
if !is_cli(key) {
|
||||||
if let Some(toml::Value::Boolean(b)) = table.get(key) {
|
if let Some(toml::Value::Boolean(b)) = table.get(key) {
|
||||||
@@ -199,55 +198,55 @@ impl Config {
|
|||||||
merge_f32(&mut config.grace, "grace");
|
merge_f32(&mut config.grace, "grace");
|
||||||
merge_f32(&mut config.fade_in, "fade_in");
|
merge_f32(&mut config.fade_in, "fade_in");
|
||||||
merge_string(&mut config.pam_service, "pam_service");
|
merge_string(&mut config.pam_service, "pam_service");
|
||||||
merge_bool(&mut config.show_media, "show_media");
|
merge_bool(&mut config.show_media, "show_media");
|
||||||
merge_bool(&mut config.show_battery, "show_battery");
|
merge_bool(&mut config.show_battery, "show_battery");
|
||||||
merge_bool(&mut config.show_network, "show_network");
|
merge_bool(&mut config.show_network, "show_network");
|
||||||
merge_bool(&mut config.show_bluetooth, "show_bluetooth");
|
merge_bool(&mut config.show_bluetooth, "show_bluetooth");
|
||||||
merge_bool(&mut config.show_album_art, "show_album_art");
|
merge_bool(&mut config.show_album_art, "show_album_art");
|
||||||
merge_bool(&mut config.show_keyboard_layout, "show_keyboard_layout");
|
merge_bool(&mut config.show_keyboard_layout, "show_keyboard_layout");
|
||||||
|
|
||||||
if !is_cli("image") {
|
if !is_cli("image") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("image") {
|
if let Some(toml::Value::String(s)) = table.get("image") {
|
||||||
config.image = Some(std::path::PathBuf::from(s));
|
config.image = Some(std::path::PathBuf::from(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("wifi_icon") {
|
if !is_cli("wifi_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("wifi_icon") {
|
if let Some(toml::Value::String(s)) = table.get("wifi_icon") {
|
||||||
config.wifi_icon = Some(s.clone());
|
config.wifi_icon = Some(s.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("bluetooth_icon") {
|
if !is_cli("bluetooth_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("bluetooth_icon") {
|
if let Some(toml::Value::String(s)) = table.get("bluetooth_icon") {
|
||||||
config.bluetooth_icon = Some(s.clone());
|
config.bluetooth_icon = Some(s.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("battery_icon") {
|
if !is_cli("battery_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("battery_icon") {
|
if let Some(toml::Value::String(s)) = table.get("battery_icon") {
|
||||||
config.battery_icon = Some(s.clone());
|
config.battery_icon = Some(s.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("media_prev_icon") {
|
if !is_cli("media_prev_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_prev_icon") {
|
if let Some(toml::Value::String(s)) = table.get("media_prev_icon") {
|
||||||
config.media_prev_icon = Some(s.clone());
|
config.media_prev_icon = Some(s.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("media_stop_icon") {
|
if !is_cli("media_stop_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_stop_icon") {
|
if let Some(toml::Value::String(s)) = table.get("media_stop_icon") {
|
||||||
config.media_stop_icon = Some(s.clone());
|
config.media_stop_icon = Some(s.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("media_play_icon") {
|
if !is_cli("media_play_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_play_icon") {
|
if let Some(toml::Value::String(s)) = table.get("media_play_icon") {
|
||||||
config.media_play_icon = Some(s.clone());
|
config.media_play_icon = Some(s.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !is_cli("media_next_icon") {
|
if !is_cli("media_next_icon") {
|
||||||
if let Some(toml::Value::String(s)) = table.get("media_next_icon") {
|
if let Some(toml::Value::String(s)) = table.get("media_next_icon") {
|
||||||
config.media_next_icon = Some(s.clone());
|
config.media_next_icon = Some(s.clone());
|
||||||
@@ -261,20 +260,40 @@ impl Config {
|
|||||||
if let Some(theme) = &config.theme {
|
if let Some(theme) = &config.theme {
|
||||||
match theme.as_str() {
|
match theme.as_str() {
|
||||||
"modern" => {
|
"modern" => {
|
||||||
if config.effect_blur.is_none() && !is_cli("effect_blur") { config.effect_blur = Some((10, 3)); }
|
if config.effect_blur.is_none() && !is_cli("effect_blur") {
|
||||||
if config.effect_vignette.is_none() && !is_cli("effect_vignette") { config.effect_vignette = Some((0.5, 0.5)); }
|
config.effect_blur = Some((10, 3));
|
||||||
if !is_cli("indicator_radius") { config.indicator_radius = 120; }
|
}
|
||||||
if !is_cli("ring_color") { config.ring_color = (0.2, 0.6, 0.8, 1.0); }
|
if config.effect_vignette.is_none() && !is_cli("effect_vignette") {
|
||||||
|
config.effect_vignette = Some((0.5, 0.5));
|
||||||
|
}
|
||||||
|
if !is_cli("indicator_radius") {
|
||||||
|
config.indicator_radius = 120;
|
||||||
|
}
|
||||||
|
if !is_cli("ring_color") {
|
||||||
|
config.ring_color = (0.2, 0.6, 0.8, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"pixel" => {
|
"pixel" => {
|
||||||
if config.effect_pixelate.is_none() && !is_cli("effect_pixelate") { config.effect_pixelate = Some(10); }
|
if config.effect_pixelate.is_none() && !is_cli("effect_pixelate") {
|
||||||
if !is_cli("indicator_radius") { config.indicator_radius = 80; }
|
config.effect_pixelate = Some(10);
|
||||||
if !is_cli("ring_color") { config.ring_color = (0.8, 0.2, 0.2, 1.0); }
|
}
|
||||||
|
if !is_cli("indicator_radius") {
|
||||||
|
config.indicator_radius = 80;
|
||||||
|
}
|
||||||
|
if !is_cli("ring_color") {
|
||||||
|
config.ring_color = (0.8, 0.2, 0.2, 1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"glass" => {
|
"glass" => {
|
||||||
if config.effect_blur.is_none() && !is_cli("effect_blur") { config.effect_blur = Some((20, 5)); }
|
if config.effect_blur.is_none() && !is_cli("effect_blur") {
|
||||||
if !is_cli("inside_color") { config.inside_color = (1.0, 1.0, 1.0, 0.1); }
|
config.effect_blur = Some((20, 5));
|
||||||
if !is_cli("ring_color") { config.ring_color = (1.0, 1.0, 1.0, 0.5); }
|
}
|
||||||
|
if !is_cli("inside_color") {
|
||||||
|
config.inside_color = (1.0, 1.0, 1.0, 0.1);
|
||||||
|
}
|
||||||
|
if !is_cli("ring_color") {
|
||||||
|
config.ring_color = (1.0, 1.0, 1.0, 0.5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::warn!("Unknown theme: {}", theme);
|
log::warn!("Unknown theme: {}", theme);
|
||||||
|
|||||||
+1
-2
@@ -100,8 +100,7 @@ impl InputHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update timers (should be called periodically)
|
/// Update timers (should be called periodically)
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {}
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current Caps Lock state
|
/// Get the current Caps Lock state
|
||||||
pub fn caps_lock(&self) -> bool {
|
pub fn caps_lock(&self) -> bool {
|
||||||
|
|||||||
+4
-6
@@ -69,7 +69,7 @@ impl LockedSurface {
|
|||||||
use wayland_client::Proxy;
|
use wayland_client::Proxy;
|
||||||
self.wayland_surface
|
self.wayland_surface
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |ws| ws.id() == surface.id())
|
.is_some_and(|ws| ws.id() == surface.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the surface state (called on each frame)
|
/// Update the surface state (called on each frame)
|
||||||
@@ -165,11 +165,9 @@ impl LockedSurface {
|
|||||||
event: smithay_client_toolkit::seat::keyboard::KeyEvent,
|
event: smithay_client_toolkit::seat::keyboard::KeyEvent,
|
||||||
modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
|
modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
|
||||||
) -> Option<InputAction> {
|
) -> Option<InputAction> {
|
||||||
let action = self.input_handler.handle_key_event(
|
let action = self
|
||||||
event.keysym,
|
.input_handler
|
||||||
event.utf8,
|
.handle_key_event(event.keysym, event.utf8, modifiers);
|
||||||
modifiers,
|
|
||||||
);
|
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
InputAction::PasswordChanged => {
|
InputAction::PasswordChanged => {
|
||||||
|
|||||||
+22
-14
@@ -10,12 +10,12 @@ mod util;
|
|||||||
use config::Config;
|
use config::Config;
|
||||||
use lock::LockManager;
|
use lock::LockManager;
|
||||||
use screenshot::{CaptureData, Screenshot, ScreenshotManager};
|
use screenshot::{CaptureData, Screenshot, ScreenshotManager};
|
||||||
use system::SystemManager;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use system::SystemManager;
|
||||||
use wayland_client::protocol::wl_output::WlOutput;
|
use wayland_client::protocol::wl_output::WlOutput;
|
||||||
use wayland_client::protocol::wl_surface::WlSurface;
|
use wayland_client::protocol::wl_surface::WlSurface;
|
||||||
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle};
|
use wayland_client::{Connection, Dispatch, Proxy, QueueHandle};
|
||||||
@@ -160,7 +160,7 @@ impl WaylandLock {
|
|||||||
if let Some(info) = self.output_state.info(output) {
|
if let Some(info) = self.output_state.info(output) {
|
||||||
if let Some(mode) = info.modes.first() {
|
if let Some(mode) = info.modes.first() {
|
||||||
let (w, h) = mode.dimensions;
|
let (w, h) = mode.dimensions;
|
||||||
return (w as i32, h as i32);
|
return (w, h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(1920, 1080)
|
(1920, 1080)
|
||||||
@@ -489,7 +489,7 @@ impl Dispatch<ZwlrScreencopyFrameV1, CaptureData> for WaylandLock {
|
|||||||
stride,
|
stride,
|
||||||
} => {
|
} => {
|
||||||
let format = format.into_result().unwrap();
|
let format = format.into_result().unwrap();
|
||||||
|
|
||||||
let mut info = data.info.lock().unwrap();
|
let mut info = data.info.lock().unwrap();
|
||||||
*info = Some(screenshot::BufferInfo {
|
*info = Some(screenshot::BufferInfo {
|
||||||
width,
|
width,
|
||||||
@@ -503,7 +503,8 @@ impl Dispatch<ZwlrScreencopyFrameV1, CaptureData> for WaylandLock {
|
|||||||
let size = (stride as usize) * (height as usize);
|
let size = (stride as usize) * (height as usize);
|
||||||
match SlotPool::new(size, &state.shm_state) {
|
match SlotPool::new(size, &state.shm_state) {
|
||||||
Ok(mut pool) => {
|
Ok(mut pool) => {
|
||||||
match pool.create_buffer(width as i32, height as i32, stride as i32, format) {
|
match pool.create_buffer(width as i32, height as i32, stride as i32, format)
|
||||||
|
{
|
||||||
Ok((buffer, _canvas)) => {
|
Ok((buffer, _canvas)) => {
|
||||||
frame.copy(buffer.wl_buffer());
|
frame.copy(buffer.wl_buffer());
|
||||||
*data.buffer.lock().unwrap() = Some(buffer);
|
*data.buffer.lock().unwrap() = Some(buffer);
|
||||||
@@ -525,8 +526,10 @@ impl Dispatch<ZwlrScreencopyFrameV1, CaptureData> for WaylandLock {
|
|||||||
let info = data.info.lock().unwrap().take();
|
let info = data.info.lock().unwrap().take();
|
||||||
let flags = data.flags.lock().unwrap().take();
|
let flags = data.flags.lock().unwrap().take();
|
||||||
let pool = data.pool.lock().unwrap().take();
|
let pool = data.pool.lock().unwrap().take();
|
||||||
|
|
||||||
if let (Some(buffer), Some(info), Some(flags), Some(mut pool)) = (buffer, info, flags, pool) {
|
if let (Some(buffer), Some(info), Some(flags), Some(mut pool)) =
|
||||||
|
(buffer, info, flags, pool)
|
||||||
|
{
|
||||||
let handle = screenshot::ScreencopyBufferHandle {
|
let handle = screenshot::ScreencopyBufferHandle {
|
||||||
buffer,
|
buffer,
|
||||||
info,
|
info,
|
||||||
@@ -584,6 +587,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug))?;
|
log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug))?;
|
||||||
|
|
||||||
log::info!("Starting rustlock v{}", env!("CARGO_PKG_VERSION"));
|
log::info!("Starting rustlock v{}", env!("CARGO_PKG_VERSION"));
|
||||||
|
#[allow(clippy::arc_with_non_send_sync)]
|
||||||
let lock_manager = Arc::new(Mutex::new(LockManager::new(config.clone())));
|
let lock_manager = Arc::new(Mutex::new(LockManager::new(config.clone())));
|
||||||
let ctrlc_exit = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
let ctrlc_exit = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||||
|
|
||||||
@@ -608,7 +612,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
// Initialize state without pool first
|
// Initialize state without pool first
|
||||||
// We'll create the pool after we know the output dimensions
|
// We'll create the pool after we know the output dimensions
|
||||||
|
|
||||||
// Initialize pool with a minimal size, it will be resized once outputs are detected
|
// Initialize pool with a minimal size, it will be resized once outputs are detected
|
||||||
let pool = SlotPool::new(1, &shm_state)?;
|
let pool = SlotPool::new(1, &shm_state)?;
|
||||||
|
|
||||||
@@ -623,7 +627,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
registry_state: RegistryState::new(&globals),
|
registry_state: RegistryState::new(&globals),
|
||||||
session_lock_state: SessionLockState::new(&globals, &qh),
|
session_lock_state: SessionLockState::new(&globals, &qh),
|
||||||
seat_state: SeatState::new(&globals, &qh),
|
seat_state: SeatState::new(&globals, &qh),
|
||||||
shm_state: shm_state,
|
shm_state,
|
||||||
pool,
|
pool,
|
||||||
session_lock: None,
|
session_lock: None,
|
||||||
lock_surfaces: Vec::new(),
|
lock_surfaces: Vec::new(),
|
||||||
@@ -648,7 +652,8 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
if let Ok(img) = image::open(image_path) {
|
if let Ok(img) = image::open(image_path) {
|
||||||
let img = img.to_rgba8();
|
let img = img.to_rgba8();
|
||||||
let (w, h) = img.dimensions();
|
let (w, h) = img.dimensions();
|
||||||
let mut surface = cairo::ImageSurface::create(cairo::Format::ARgb32, w as i32, h as i32).unwrap();
|
let mut surface =
|
||||||
|
cairo::ImageSurface::create(cairo::Format::ARgb32, w as i32, h as i32).unwrap();
|
||||||
{
|
{
|
||||||
let mut surface_data = surface.data().unwrap();
|
let mut surface_data = surface.data().unwrap();
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
@@ -662,18 +667,21 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ss = Screenshot::new(surface);
|
let mut ss = Screenshot::new(surface);
|
||||||
let _ = ss.apply_effects(&state.config);
|
let _ = ss.apply_effects(&state.config);
|
||||||
let surface = ss.into_inner();
|
let surface = ss.into_inner();
|
||||||
|
|
||||||
let num_outputs = state.output_state.outputs().count();
|
let num_outputs = state.output_state.outputs().count();
|
||||||
state.captured_backgrounds = vec![Some(surface); num_outputs];
|
state.captured_backgrounds = vec![Some(surface); num_outputs];
|
||||||
|
|
||||||
// Disable screenshots if image was successfully loaded
|
// Disable screenshots if image was successfully loaded
|
||||||
state.config.screenshots = false;
|
state.config.screenshots = false;
|
||||||
} else {
|
} else {
|
||||||
log::error!("Failed to load custom background image from {:?}", image_path);
|
log::error!(
|
||||||
|
"Failed to load custom background image from {:?}",
|
||||||
|
image_path
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,7 +732,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut status = state.system_manager.get_status();
|
let mut status = state.system_manager.get_status();
|
||||||
status.keyboard_layout = Some(state.current_layout.to_string());
|
status.keyboard_layout = Some(state.current_layout.to_string());
|
||||||
|
|
||||||
if let Ok(mut lm) = state.lock_manager.lock() {
|
if let Ok(mut lm) = state.lock_manager.lock() {
|
||||||
lm.set_system_status(status);
|
lm.set_system_status(status);
|
||||||
|
|||||||
+95
-61
@@ -351,18 +351,18 @@ impl Renderer {
|
|||||||
self.context.stroke().unwrap();
|
self.context.stroke().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use caps lock color when caps lock is on and indicator is enabled, otherwise ring color
|
// Use caps lock color when caps lock is on and indicator is enabled, otherwise ring color
|
||||||
let (r, g, b, a) = if self.caps_lock {
|
let (r, g, b, a) = if self.caps_lock {
|
||||||
self.config.caps_lock_color
|
self.config.caps_lock_color
|
||||||
} else {
|
} else {
|
||||||
self.config.ring_color
|
self.config.ring_color
|
||||||
};
|
};
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(r, g, b, a * self.fade_alpha);
|
self.context.set_source_rgba(r, g, b, a * self.fade_alpha);
|
||||||
self.context.set_line_width(thickness);
|
self.context.set_line_width(thickness);
|
||||||
self.context
|
self.context
|
||||||
.arc(center_x, center_y, radius, 0.0, 2.0 * std::f64::consts::PI);
|
.arc(center_x, center_y, radius, 0.0, 2.0 * std::f64::consts::PI);
|
||||||
self.context.stroke().unwrap();
|
self.context.stroke().unwrap();
|
||||||
|
|
||||||
let (r, g, b, a) = self.config.separator_color;
|
let (r, g, b, a) = self.config.separator_color;
|
||||||
if a > 0.0 {
|
if a > 0.0 {
|
||||||
@@ -499,36 +499,39 @@ impl Renderer {
|
|||||||
let art_size = 56.0;
|
let art_size = 56.0;
|
||||||
let spacing = 80.0;
|
let spacing = 80.0;
|
||||||
|
|
||||||
if self.config.show_album_art {
|
if self.config.show_album_art && self.system_status.media_art_url != self.last_art_url {
|
||||||
if self.system_status.media_art_url != self.last_art_url {
|
self.last_art_url = self.system_status.media_art_url.clone();
|
||||||
self.last_art_url = self.system_status.media_art_url.clone();
|
self.media_art_surface = None;
|
||||||
self.media_art_surface = None;
|
if let Some(ref data) = self.system_status.media_art_data {
|
||||||
if let Some(ref data) = self.system_status.media_art_data {
|
if let Ok(img) = image::load_from_memory(data) {
|
||||||
if let Ok(img) = image::load_from_memory(data) {
|
let img = img.to_rgba8();
|
||||||
let img = img.to_rgba8();
|
let (w, h) = img.dimensions();
|
||||||
let (w, h) = img.dimensions();
|
let mut surface =
|
||||||
let mut surface = ImageSurface::create(Format::ARgb32, w as i32, h as i32).unwrap();
|
ImageSurface::create(Format::ARgb32, w as i32, h as i32).unwrap();
|
||||||
{
|
{
|
||||||
let mut surface_data = surface.data().unwrap();
|
let mut surface_data = surface.data().unwrap();
|
||||||
for y in 0..h {
|
for y in 0..h {
|
||||||
for x in 0..w {
|
for x in 0..w {
|
||||||
let pixel = img.get_pixel(x, y);
|
let pixel = img.get_pixel(x, y);
|
||||||
let idx = ((y * w + x) * 4) as usize;
|
let idx = ((y * w + x) * 4) as usize;
|
||||||
surface_data[idx] = pixel[2];
|
surface_data[idx] = pixel[2];
|
||||||
surface_data[idx + 1] = pixel[1];
|
surface_data[idx + 1] = pixel[1];
|
||||||
surface_data[idx + 2] = pixel[0];
|
surface_data[idx + 2] = pixel[0];
|
||||||
surface_data[idx + 3] = pixel[3];
|
surface_data[idx + 3] = pixel[3];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.media_art_surface = Some(surface);
|
|
||||||
}
|
}
|
||||||
|
self.media_art_surface = Some(surface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let has_art = self.config.show_album_art && self.media_art_surface.is_some();
|
let has_art = self.config.show_album_art && self.media_art_surface.is_some();
|
||||||
let text_x = if has_art { center_x - spacing / 2.0 } else { center_x };
|
let text_x = if has_art {
|
||||||
|
center_x - spacing / 2.0
|
||||||
|
} else {
|
||||||
|
center_x
|
||||||
|
};
|
||||||
let art_x = center_x - spacing - art_size / 2.0;
|
let art_x = center_x - spacing - art_size / 2.0;
|
||||||
|
|
||||||
if has_art {
|
if has_art {
|
||||||
@@ -544,7 +547,8 @@ impl Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.9);
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.9);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
|
|
||||||
let display_text = if let Some(ref artist) = self.system_status.media_artist {
|
let display_text = if let Some(ref artist) = self.system_status.media_artist {
|
||||||
@@ -554,14 +558,19 @@ impl Renderer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let te = self.context.text_extents(&display_text).unwrap();
|
let te = self.context.text_extents(&display_text).unwrap();
|
||||||
self.context.move_to(text_x - te.width() / 2.0, start_y + 20.0);
|
self.context
|
||||||
|
.move_to(text_x - te.width() / 2.0, start_y + 20.0);
|
||||||
self.context.show_text(&display_text).unwrap();
|
self.context.show_text(&display_text).unwrap();
|
||||||
|
|
||||||
let status_text = if self.system_status.media_playing {
|
let status_text = if self.system_status.media_playing {
|
||||||
if let Some(ref icon) = self.media_play_icon_surface {
|
if let Some(ref icon) = self.media_play_icon_surface {
|
||||||
// Draw play icon instead of text
|
// Draw play icon instead of text
|
||||||
let play_y = start_y + 40.0;
|
let play_y = start_y + 40.0;
|
||||||
self.draw_icon_at(center_x - icon.width() as f64 / 2.0, play_y - icon.height() as f64 / 2.0, icon);
|
self.draw_icon_at(
|
||||||
|
center_x - icon.width() as f64 / 2.0,
|
||||||
|
play_y - icon.height() as f64 / 2.0,
|
||||||
|
icon,
|
||||||
|
);
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
"▶ Playing"
|
"▶ Playing"
|
||||||
@@ -569,51 +578,57 @@ impl Renderer {
|
|||||||
} else {
|
} else {
|
||||||
"⏸ Paused"
|
"⏸ Paused"
|
||||||
};
|
};
|
||||||
|
|
||||||
if !status_text.is_empty() {
|
if !status_text.is_empty() {
|
||||||
self.context.set_font_size(12.0);
|
self.context.set_font_size(12.0);
|
||||||
let se = self.context.text_extents(status_text).unwrap();
|
let se = self.context.text_extents(status_text).unwrap();
|
||||||
self.context.move_to(text_x - se.width() / 2.0, start_y + 40.0);
|
self.context
|
||||||
|
.move_to(text_x - se.width() / 2.0, start_y + 40.0);
|
||||||
self.context.show_text(status_text).unwrap();
|
self.context.show_text(status_text).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let controls_y = start_y + 65.0;
|
let controls_y = start_y + 65.0;
|
||||||
|
|
||||||
// Draw media control icons
|
// Draw media control icons
|
||||||
let icon_size = 20.0;
|
let icon_size = 20.0;
|
||||||
let icon_spacing = 40.0;
|
let icon_spacing = 40.0;
|
||||||
let controls_center_x = center_x;
|
let controls_center_x = center_x;
|
||||||
|
|
||||||
// Previous icon
|
// Previous icon
|
||||||
if let Some(ref icon) = self.media_prev_icon_surface {
|
if let Some(ref icon) = self.media_prev_icon_surface {
|
||||||
let ix = controls_center_x - icon_spacing;
|
let ix = controls_center_x - icon_spacing;
|
||||||
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
||||||
} else {
|
} else {
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
self.context.move_to(controls_center_x - icon_spacing - 8.0, controls_y);
|
self.context
|
||||||
|
.move_to(controls_center_x - icon_spacing - 8.0, controls_y);
|
||||||
self.context.show_text("⏮").unwrap();
|
self.context.show_text("⏮").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop icon
|
// Stop icon
|
||||||
if let Some(ref icon) = self.media_stop_icon_surface {
|
if let Some(ref icon) = self.media_stop_icon_surface {
|
||||||
let ix = controls_center_x;
|
let ix = controls_center_x;
|
||||||
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
||||||
} else {
|
} else {
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
self.context.move_to(controls_center_x - 8.0, controls_y);
|
self.context.move_to(controls_center_x - 8.0, controls_y);
|
||||||
self.context.show_text("⏹").unwrap();
|
self.context.show_text("⏹").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next icon
|
// Next icon
|
||||||
if let Some(ref icon) = self.media_next_icon_surface {
|
if let Some(ref icon) = self.media_next_icon_surface {
|
||||||
let ix = controls_center_x + icon_spacing;
|
let ix = controls_center_x + icon_spacing;
|
||||||
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
self.draw_icon_at(ix - icon_size / 2.0, controls_y - icon_size / 2.0, icon);
|
||||||
} else {
|
} else {
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.7);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
self.context.move_to(controls_center_x + icon_spacing - 8.0, controls_y);
|
self.context
|
||||||
|
.move_to(controls_center_x + icon_spacing - 8.0, controls_y);
|
||||||
self.context.show_text("⏭").unwrap();
|
self.context.show_text("⏭").unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -637,19 +652,17 @@ impl Renderer {
|
|||||||
self.context.move_to(text_x, y);
|
self.context.move_to(text_x, y);
|
||||||
self.context.show_text(ssid).unwrap();
|
self.context.show_text(ssid).unwrap();
|
||||||
} else {
|
} else {
|
||||||
let strength = self.system_status.wifi_strength.unwrap_or(0);
|
|
||||||
let icon = if strength > 75 { "📶" } else if strength > 50 { "📶" } else if strength > 25 { "📶" } else { "📶" };
|
|
||||||
let text = format!("{} {}", icon, ssid);
|
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
self.context.move_to(x, y);
|
self.context.move_to(x, y);
|
||||||
self.context.show_text(&text).unwrap();
|
self.context.show_text(ssid).unwrap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let text = "📵 No WiFi";
|
let text = "No WiFi";
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.5);
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.5);
|
||||||
self.context.set_font_size(16.0);
|
self.context.set_font_size(16.0);
|
||||||
self.context.move_to(x, y);
|
self.context.move_to(x, y);
|
||||||
self.context.show_text(text).unwrap();
|
self.context.show_text(text).unwrap();
|
||||||
@@ -673,7 +686,14 @@ impl Renderer {
|
|||||||
self.context.move_to(text_x, y);
|
self.context.move_to(text_x, y);
|
||||||
self.context.show_text(&battery_text).unwrap();
|
self.context.show_text(&battery_text).unwrap();
|
||||||
} else {
|
} else {
|
||||||
self.draw_battery_icon_at(x, y - 12.0, icon_width, 15.0, percent, self.system_status.is_charging);
|
self.draw_battery_icon_at(
|
||||||
|
x,
|
||||||
|
y - 12.0,
|
||||||
|
icon_width,
|
||||||
|
15.0,
|
||||||
|
percent,
|
||||||
|
self.system_status.is_charging,
|
||||||
|
);
|
||||||
let battery_text = format!("{:.0}%", percent);
|
let battery_text = format!("{:.0}%", percent);
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
||||||
@@ -703,7 +723,10 @@ impl Renderer {
|
|||||||
self.context.move_to(text_x, y);
|
self.context.move_to(text_x, y);
|
||||||
self.context.show_text(&devices).unwrap();
|
self.context.show_text(&devices).unwrap();
|
||||||
} else {
|
} else {
|
||||||
let text = format!("🔵 {} device(s)", self.system_status.bluetooth_devices.len());
|
let text = format!(
|
||||||
|
"🔵 {} device(s)",
|
||||||
|
self.system_status.bluetooth_devices.len()
|
||||||
|
);
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha);
|
||||||
self.context.set_font_size(14.0);
|
self.context.set_font_size(14.0);
|
||||||
@@ -713,7 +736,8 @@ impl Renderer {
|
|||||||
} else {
|
} else {
|
||||||
let text = "🔴 Bluetooth off";
|
let text = "🔴 Bluetooth off";
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.5);
|
self.context
|
||||||
|
.set_source_rgba(1.0, 1.0, 1.0, self.fade_alpha * 0.5);
|
||||||
self.context.set_font_size(14.0);
|
self.context.set_font_size(14.0);
|
||||||
self.context.move_to(x, y);
|
self.context.move_to(x, y);
|
||||||
self.context.show_text(text).unwrap();
|
self.context.show_text(text).unwrap();
|
||||||
@@ -750,7 +774,15 @@ impl Renderer {
|
|||||||
self.context.restore().unwrap();
|
self.context.restore().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_battery_icon_at(&self, x: f64, y: f64, width: f64, height: f64, percent: f64, charging: bool) {
|
fn draw_battery_icon_at(
|
||||||
|
&self,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
width: f64,
|
||||||
|
height: f64,
|
||||||
|
percent: f64,
|
||||||
|
charging: bool,
|
||||||
|
) {
|
||||||
let alpha = self.fade_alpha;
|
let alpha = self.fade_alpha;
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.set_source_rgba(1.0, 1.0, 1.0, alpha * 0.5);
|
self.context.set_source_rgba(1.0, 1.0, 1.0, alpha * 0.5);
|
||||||
@@ -758,7 +790,8 @@ impl Renderer {
|
|||||||
self.context.rectangle(x, y, width, height);
|
self.context.rectangle(x, y, width, height);
|
||||||
self.context.stroke().unwrap();
|
self.context.stroke().unwrap();
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
self.context.rectangle(x + width, y + height / 4.0, 3.0, height / 2.0);
|
self.context
|
||||||
|
.rectangle(x + width, y + height / 4.0, 3.0, height / 2.0);
|
||||||
self.context.fill().unwrap();
|
self.context.fill().unwrap();
|
||||||
let fill_width = (width - 4.0) * (percent / 100.0);
|
let fill_width = (width - 4.0) * (percent / 100.0);
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
@@ -767,7 +800,8 @@ impl Renderer {
|
|||||||
} else {
|
} else {
|
||||||
self.context.set_source_rgba(0.2, 1.0, 0.2, alpha * 0.8);
|
self.context.set_source_rgba(0.2, 1.0, 0.2, alpha * 0.8);
|
||||||
}
|
}
|
||||||
self.context.rectangle(x + 2.0, y + 2.0, fill_width, height - 4.0);
|
self.context
|
||||||
|
.rectangle(x + 2.0, y + 2.0, fill_width, height - 4.0);
|
||||||
self.context.fill().unwrap();
|
self.context.fill().unwrap();
|
||||||
if charging {
|
if charging {
|
||||||
self.context.new_path();
|
self.context.new_path();
|
||||||
|
|||||||
+6
-6
@@ -115,10 +115,10 @@ impl Screenshot {
|
|||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
melt_amount += rng.gen_range(0.0..factor);
|
melt_amount += rng.gen_range(0.0..factor);
|
||||||
let src_y = (y as f32 - melt_amount).max(0.0) as i32;
|
let src_y = (y as f32 - melt_amount).max(0.0) as i32;
|
||||||
|
|
||||||
let src_idx = (src_y as usize * stride) + (x as usize * 4);
|
let src_idx = (src_y as usize * stride) + (x as usize * 4);
|
||||||
let dst_idx = (y as usize * stride) + (x as usize * 4);
|
let dst_idx = (y as usize * stride) + (x as usize * 4);
|
||||||
|
|
||||||
// Copy the pixel from above to create a smear
|
// Copy the pixel from above to create a smear
|
||||||
let pixel = [
|
let pixel = [
|
||||||
data[src_idx],
|
data[src_idx],
|
||||||
@@ -394,24 +394,24 @@ impl ScreenshotManager {
|
|||||||
flipped[dst_offset..dst_offset + src_stride]
|
flipped[dst_offset..dst_offset + src_stride]
|
||||||
.copy_from_slice(&converted_data[src_offset..src_offset + src_stride]);
|
.copy_from_slice(&converted_data[src_offset..src_offset + src_stride]);
|
||||||
}
|
}
|
||||||
return Ok(ImageSurface::create_for_data(
|
return ImageSurface::create_for_data(
|
||||||
flipped,
|
flipped,
|
||||||
cairo::Format::ARgb32,
|
cairo::Format::ARgb32,
|
||||||
info.width as i32,
|
info.width as i32,
|
||||||
info.height as i32,
|
info.height as i32,
|
||||||
src_stride as i32,
|
src_stride as i32,
|
||||||
)
|
)
|
||||||
.context("Failed to create flipped Cairo surface")?);
|
.context("Failed to create flipped Cairo surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ImageSurface::create_for_data(
|
ImageSurface::create_for_data(
|
||||||
converted_data,
|
converted_data,
|
||||||
cairo::Format::ARgb32,
|
cairo::Format::ARgb32,
|
||||||
info.width as i32,
|
info.width as i32,
|
||||||
info.height as i32,
|
info.height as i32,
|
||||||
pixel_width as i32,
|
pixel_width as i32,
|
||||||
)
|
)
|
||||||
.context("Failed to create Cairo surface")?)
|
.context("Failed to create Cairo surface")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -1,8 +1,8 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use log::{debug, error};
|
||||||
use mpris::PlayerFinder;
|
use mpris::PlayerFinder;
|
||||||
use zbus::Connection;
|
use std::sync::{Arc, Mutex};
|
||||||
use log::{error, debug};
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use zbus::Connection;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct SystemStatus {
|
pub struct SystemStatus {
|
||||||
@@ -89,7 +89,7 @@ impl SystemManager {
|
|||||||
new_status.media_title = metadata.title().map(|s| s.to_string());
|
new_status.media_title = metadata.title().map(|s| s.to_string());
|
||||||
new_status.media_artist = metadata.artists().map(|a| a.join(", "));
|
new_status.media_artist = metadata.artists().map(|a| a.join(", "));
|
||||||
new_status.media_art_url = metadata.art_url().map(|u| u.to_string());
|
new_status.media_art_url = metadata.art_url().map(|u| u.to_string());
|
||||||
|
|
||||||
if new_status.media_art_url != last_art_url {
|
if new_status.media_art_url != last_art_url {
|
||||||
last_art_url = new_status.media_art_url.clone();
|
last_art_url = new_status.media_art_url.clone();
|
||||||
last_art_data = None;
|
last_art_data = None;
|
||||||
@@ -216,7 +216,7 @@ impl SystemManager {
|
|||||||
SystemCommand::Reboot => "Reboot",
|
SystemCommand::Reboot => "Reboot",
|
||||||
SystemCommand::Suspend => "Suspend",
|
SystemCommand::Suspend => "Suspend",
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("Executing system command: {}", method);
|
debug!("Executing system command: {}", method);
|
||||||
// Set a timeout for the DBus call to prevent hanging the background thread
|
// Set a timeout for the DBus call to prevent hanging the background thread
|
||||||
let result = tokio::time::timeout(
|
let result = tokio::time::timeout(
|
||||||
@@ -230,7 +230,7 @@ impl SystemManager {
|
|||||||
)
|
)
|
||||||
).await;
|
).await;
|
||||||
|
|
||||||
if let Err(_) = result {
|
if result.is_err() {
|
||||||
error!("System command {} timed out", method);
|
error!("System command {} timed out", method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user