rebrand: rename project and components to scythe

This commit is contained in:
katR 2026-09-05 15:21:30 +05:30
parent 354b3e9558
commit 8ed53faefd
20 changed files with 373 additions and 279 deletions

60
Cargo.lock generated
View file

@ -3698,6 +3698,36 @@ dependencies = [
"tiny-skia",
]
[[package]]
name = "scythe"
version = "0.1.0"
dependencies = [
"ashpd",
"cpal",
"crossbeam-channel",
"dirs",
"eframe",
"egui",
"ffmpeg-next",
"ffmpeg-sys-next",
"gdk",
"glib 0.22.9",
"global-hotkey",
"gtk",
"gtk-layer-shell",
"libc",
"memmap2",
"mp4",
"pipewire",
"ringbuf",
"rubato",
"serde",
"serde_json",
"tokio",
"windows 0.58.0",
"xcb",
]
[[package]]
name = "semver"
version = "1.0.28"
@ -4480,36 +4510,6 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "vrec"
version = "0.1.0"
dependencies = [
"ashpd",
"cpal",
"crossbeam-channel",
"dirs",
"eframe",
"egui",
"ffmpeg-next",
"ffmpeg-sys-next",
"gdk",
"glib 0.22.9",
"global-hotkey",
"gtk",
"gtk-layer-shell",
"libc",
"memmap2",
"mp4",
"pipewire",
"ringbuf",
"rubato",
"serde",
"serde_json",
"tokio",
"windows 0.58.0",
"xcb",
]
[[package]]
name = "walkdir"
version = "2.5.0"

View file

@ -1,8 +1,16 @@
[package]
name = "vrec"
name = "scythe"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "scythe-daemon"
path = "src/bin/scythe-daemon.rs"
[[bin]]
name = "scythe-ui"
path = "src/bin/scythe-ui.rs"
[dependencies]
crossbeam-channel = "0.5.16"
ffmpeg-next = "9.0.0"

View file

@ -2,7 +2,7 @@
set -e
echo "============================================="
echo " Installing vrec Screen Recorder "
echo " Installing Scythe Screen Recorder "
echo "============================================="
# Compile release binaries
@ -16,22 +16,30 @@ mkdir -p "$BIN_DIR"
mkdir -p "$APP_DIR"
echo "[2/4] Installing binaries to $BIN_DIR..."
install -m 755 target/release/vrec-daemon "$BIN_DIR/vrec-daemon"
install -m 755 target/release/vrec-ui "$BIN_DIR/vrec-ui"
install -m 755 target/release/scythe-daemon "$BIN_DIR/scythe-daemon"
install -m 755 target/release/scythe-ui "$BIN_DIR/scythe-ui"
ln -sf "$BIN_DIR/scythe-ui" "$BIN_DIR/scythe"
# Backward-compatible symlinks
ln -sf "$BIN_DIR/scythe-daemon" "$BIN_DIR/vrec-daemon"
ln -sf "$BIN_DIR/scythe-ui" "$BIN_DIR/vrec-ui"
ln -sf "$BIN_DIR/scythe-ui" "$BIN_DIR/vrec"
echo "[3/4] Creating application launcher..."
cat > "$APP_DIR/vrec.desktop" << EOF
cat > "$APP_DIR/scythe.desktop" << EOF
[Desktop Entry]
Type=Application
Name=vrec Screen Recorder
Name=Scythe Screen Recorder
Comment=Fast GPU Hardware Screen Recorder & Instant Replay
Exec=vrec-ui --menu
Exec=scythe-ui --menu
Icon=media-record
Terminal=false
Categories=AudioVideo;Recorder;
Keywords=screen;recorder;replay;capture;
Keywords=screen;recorder;replay;capture;scythe;shadowplay;
EOF
ln -sf "$APP_DIR/scythe.desktop" "$APP_DIR/vrec.desktop"
echo "[4/4] Verifying PATH..."
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
echo "Notice: $BIN_DIR is not in your PATH. Consider adding:"
@ -39,15 +47,15 @@ if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
fi
echo "============================================="
echo " vrec successfully installed! "
echo " Scythe successfully installed! "
echo "============================================="
echo "You can now run:"
echo " vrec-daemon # Starts background recording engine"
echo " vrec-ui --menu # Opens the HUD overlay menu"
echo " vrec-ui --record # Toggles normal recording"
echo " vrec-ui --save # Saves instant replay"
echo " scythe-daemon # Starts background recording engine"
echo " scythe-ui --menu # Opens the HUD overlay menu"
echo " scythe-ui --record # Toggles normal recording"
echo " scythe-ui --save # Saves instant replay"
echo ""
echo "Tip: You can bind 'vrec-ui --menu' or 'vrec-ui --save' in Hyprland config (hyprland.conf):"
echo ' bind = ALT, Z, exec, vrec-ui --menu'
echo ' bind = CONTROL SHIFT, R, exec, vrec-ui --save'
echo "Tip: You can bind 'scythe-ui --menu' or 'scythe-ui --save' in Hyprland config (hyprland.conf):"
echo ' bind = ALT, Z, exec, scythe-ui --menu'
echo ' bind = CONTROL SHIFT, R, exec, scythe-ui --save'
echo "============================================="

View file

@ -1,31 +1,36 @@
# Maintainer: oiupoyt
pkgname=vrec-git
pkgname=scythe-git
pkgver=0.1.0
pkgrel=1
pkgdesc="High-performance GPU hardware screen recorder and instant replay overlay for Wayland and X11"
arch=('x86_64')
url="https://github.com/oiupoyt/vrec"
url="https://github.com/oiupoyt/scythe"
license=('MIT' 'Apache-2.0')
depends=('ffmpeg' 'pipewire' 'gtk3' 'gtk-layer-shell' 'libpulse' 'libva' 'libxcb')
makedepends=('cargo' 'git' 'pkgconf' 'clang')
provides=('vrec')
conflicts=('vrec')
source=("git+https://github.com/oiupoyt/vrec.git")
provides=('scythe' 'vrec' 'vrec-git')
conflicts=('scythe' 'vrec')
source=("git+https://github.com/oiupoyt/scythe.git")
sha256sums=('SKIP')
pkgver() {
cd "$srcdir/vrec"
cd "$srcdir/scythe"
git describe --long --tags 2>/dev/null | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || echo "0.1.0"
}
build() {
cd "$srcdir/vrec"
cd "$srcdir/scythe"
cargo build --release --locked
}
package() {
cd "$srcdir/vrec"
install -Dm755 target/release/vrec-daemon "$pkgdir/usr/bin/vrec-daemon"
install -Dm755 target/release/vrec-ui "$pkgdir/usr/bin/vrec-ui"
install -Dm644 packaging/vrec.desktop "$pkgdir/usr/share/applications/vrec.desktop"
cd "$srcdir/scythe"
install -Dm755 target/release/scythe-daemon "$pkgdir/usr/bin/scythe-daemon"
install -Dm755 target/release/scythe-ui "$pkgdir/usr/bin/scythe-ui"
install -Dm755 target/release/scythe "$pkgdir/usr/bin/scythe"
ln -sf "/usr/bin/scythe-daemon" "$pkgdir/usr/bin/vrec-daemon"
ln -sf "/usr/bin/scythe-ui" "$pkgdir/usr/bin/vrec-ui"
ln -sf "/usr/bin/scythe" "$pkgdir/usr/bin/vrec"
install -Dm644 packaging/scythe.desktop "$pkgdir/usr/share/applications/scythe.desktop"
ln -sf "/usr/share/applications/scythe.desktop" "$pkgdir/usr/share/applications/vrec.desktop"
}

View file

@ -1,10 +1,10 @@
app-id: com.vrec.Vrec
app-id: com.scythe.Scythe
runtime: org.gnome.Platform
runtime-version: '46'
sdk: org.gnome.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
command: vrec-ui
command: scythe-ui
finish-args:
- --share=ipc
- --socket=fallback-x11
@ -19,17 +19,21 @@ finish-args:
build-options:
append-path: /usr/lib/sdk/rust-stable/bin
env:
CARGO_HOME: /run/build/vrec/cargo
CARGO_HOME: /run/build/scythe/cargo
modules:
- name: vrec
- name: scythe
buildsystem: simple
build-commands:
- cargo build --release --verbose
- install -Dm755 target/release/vrec-daemon /app/bin/vrec-daemon
- install -Dm755 target/release/vrec-ui /app/bin/vrec-ui
- install -Dm644 packaging/vrec.desktop /app/share/applications/com.vrec.Vrec.desktop
- install -Dm755 target/release/scythe-daemon /app/bin/scythe-daemon
- install -Dm755 target/release/scythe-ui /app/bin/scythe-ui
- install -Dm755 target/release/scythe /app/bin/scythe
- ln -sf /app/bin/scythe-daemon /app/bin/vrec-daemon
- ln -sf /app/bin/scythe-ui /app/bin/vrec-ui
- ln -sf /app/bin/scythe /app/bin/vrec
- install -Dm644 packaging/scythe.desktop /app/share/applications/com.scythe.Scythe.desktop
sources:
- type: git
url: https://github.com/oiupoyt/vrec.git
url: https://github.com/oiupoyt/scythe.git
branch: main

View file

@ -1,10 +1,10 @@
Name: vrec
Name: scythe
Version: 0.1.0
Release: 1%{?dist}
Summary: High-performance GPU hardware screen recorder and instant replay overlay
License: MIT
URL: https://github.com/oiupoyt/vrec
URL: https://github.com/oiupoyt/scythe
Source0: %{name}-%{version}.tar.gz
BuildRequires: cargo
@ -32,15 +32,25 @@ cargo build --release
%install
rm -rf $RPM_BUILD_ROOT
install -D -m 0755 target/release/vrec-daemon %{buildroot}%{_bindir}/vrec-daemon
install -D -m 0755 target/release/vrec-ui %{buildroot}%{_bindir}/vrec-ui
install -D -m 0644 packaging/vrec.desktop %{buildroot}%{_datadir}/applications/vrec.desktop
install -D -m 0755 target/release/scythe-daemon %{buildroot}%{_bindir}/scythe-daemon
install -D -m 0755 target/release/scythe-ui %{buildroot}%{_bindir}/scythe-ui
install -D -m 0755 target/release/scythe %{buildroot}%{_bindir}/scythe
ln -sf %{_bindir}/scythe-daemon %{buildroot}%{_bindir}/vrec-daemon
ln -sf %{_bindir}/scythe-ui %{buildroot}%{_bindir}/vrec-ui
ln -sf %{_bindir}/scythe %{buildroot}%{_bindir}/vrec
install -D -m 0644 packaging/scythe.desktop %{buildroot}%{_datadir}/applications/scythe.desktop
ln -sf %{_datadir}/applications/scythe.desktop %{buildroot}%{_datadir}/applications/vrec.desktop
%files
%{_bindir}/scythe-daemon
%{_bindir}/scythe-ui
%{_bindir}/scythe
%{_bindir}/vrec-daemon
%{_bindir}/vrec-ui
%{_bindir}/vrec
%{_datadir}/applications/scythe.desktop
%{_datadir}/applications/vrec.desktop
%changelog
* Wed Sep 02 2026 oiupoyt - 0.1.0-1
- Initial release of vrec
- Initial release of scythe

View file

@ -1,11 +1,11 @@
[Desktop Entry]
Type=Application
Name=vrec Screen Recorder
Name=Scythe Screen Recorder
GenericName=Screen Recorder & Instant Replay
Comment=High-performance GPU hardware screen recorder and instant replay overlay
Exec=vrec-ui --menu
Exec=scythe-ui --menu
Icon=media-record
Terminal=false
Categories=AudioVideo;Recorder;
Keywords=screen;recorder;replay;capture;game;shadowplay;
Keywords=screen;recorder;replay;capture;game;shadowplay;scythe;
StartupNotify=false

View file

@ -1,9 +1,9 @@
!include "MUI2.nsh"
!include "FileFunc.nsh"
Name "vrec"
OutFile "..\..\dist\vrec-setup.exe"
InstallDir "$LOCALAPPDATA\Programs\vrec"
Name "Scythe"
OutFile "..\..\dist\scythe-setup.exe"
InstallDir "$LOCALAPPDATA\Programs\scythe"
RequestExecutionLevel user
!define MUI_ABORTWARNING
@ -12,9 +12,9 @@ RequestExecutionLevel user
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN "$INSTDIR\vrec-ui.exe"
!define MUI_FINISHPAGE_RUN "$INSTDIR\scythe-ui.exe"
!define MUI_FINISHPAGE_RUN_PARAMETERS "--menu"
!define MUI_FINISHPAGE_RUN_TEXT "Launch vrec"
!define MUI_FINISHPAGE_RUN_TEXT "Launch Scythe"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
@ -22,26 +22,26 @@ RequestExecutionLevel user
!insertmacro MUI_LANGUAGE "English"
Section "vrec" SecMain
Section "Scythe" SecMain
SetOutPath "$INSTDIR"
File /r "..\..\dist\bundle\*.*"
; Create Start Menu and Desktop shortcuts
CreateDirectory "$SMPROGRAMS\vrec"
CreateShortcut "$SMPROGRAMS\vrec\vrec.lnk" "$INSTDIR\vrec-ui.exe" "--menu"
CreateShortcut "$DESKTOP\vrec.lnk" "$INSTDIR\vrec-ui.exe" "--menu"
CreateDirectory "$SMPROGRAMS\scythe"
CreateShortcut "$SMPROGRAMS\scythe\scythe.lnk" "$INSTDIR\scythe-ui.exe" "--menu"
CreateShortcut "$DESKTOP\scythe.lnk" "$INSTDIR\scythe-ui.exe" "--menu"
; Register uninstaller with Windows Control Panel / Settings
WriteUninstaller "$INSTDIR\uninstall.exe"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\vrec" "DisplayName" "vrec Screen Recorder"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\vrec" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\vrec" "DisplayIcon" "$INSTDIR\vrec-ui.exe"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\vrec" "Publisher" "oiupoyt"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\scythe" "DisplayName" "Scythe Screen Recorder"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\scythe" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\scythe" "DisplayIcon" "$INSTDIR\scythe-ui.exe"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\scythe" "Publisher" "oiupoyt"
SectionEnd
Section "Uninstall"
Delete "$DESKTOP\vrec.lnk"
RMDir /r "$SMPROGRAMS\vrec"
Delete "$DESKTOP\scythe.lnk"
RMDir /r "$SMPROGRAMS\scythe"
RMDir /r "$INSTDIR"
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\vrec"
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\scythe"
SectionEnd

View file

@ -0,0 +1,22 @@
!include "FileFunc.nsh"
!include "LogicLib.nsh"
Name "Scythe"
OutFile "..\..\dist\scythe.exe"
Caption "Scythe Screen Recorder"
SilentInstall silent
RequestExecutionLevel user
Section
; Extract self-contained binaries and all runtime DLLs directly to user app data
SetOutPath "$LOCALAPPDATA\scythe"
File /r "..\..\dist\bundle\*.*"
; Execute scythe-ui with any forwarded command-line arguments, or default to overlay menu
${GetParameters} $R0
${If} $R0 == ""
Exec '"$LOCALAPPDATA\scythe\scythe-ui.exe" --menu'
${Else}
Exec '"$LOCALAPPDATA\scythe\scythe-ui.exe" $R0'
${EndIf}
SectionEnd

View file

@ -1,22 +0,0 @@
!include "FileFunc.nsh"
!include "LogicLib.nsh"
Name "vrec"
OutFile "..\..\dist\vrec.exe"
Caption "vrec Screen Recorder"
SilentInstall silent
RequestExecutionLevel user
Section
; Extract self-contained binaries and all runtime DLLs directly to user app data
SetOutPath "$LOCALAPPDATA\vrec"
File /r "..\..\dist\bundle\*.*"
; Execute vrec-ui with any forwarded command-line arguments, or default to overlay menu
${GetParameters} $R0
${If} $R0 == ""
Exec '"$LOCALAPPDATA\vrec\vrec-ui.exe" --menu'
${Else}
Exec '"$LOCALAPPDATA\vrec\vrec-ui.exe" $R0'
${EndIf}
SectionEnd

View file

@ -1,10 +1,10 @@
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
use vrec::capture::{Frame, FrameSource};
use vrec::encoder::VideoEncoder;
use vrec::ring::Packet;
use vrec::muxer::Muxer;
use vrec::ipc::{Command, DaemonStatus};
use scythe::capture::{Frame, FrameSource};
use scythe::encoder::VideoEncoder;
use scythe::ring::Packet;
use scythe::muxer::Muxer;
use scythe::ipc::{Command, DaemonStatus};
use ringbuf::HeapRb;
use ringbuf::traits::{RingBuffer, Consumer, Observer};
use crossbeam_channel::bounded;
@ -54,7 +54,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(unix)]
let _lock_file = {
let runtime_dir = env::var("XDG_RUNTIME_DIR").unwrap_or_else(|_| format!("/run/user/{}", unsafe { libc::getuid() }));
let lock_path = format!("{}/vrec.lock", runtime_dir);
let lock_path = format!("{}/scythe.lock", runtime_dir);
match std::fs::OpenOptions::new()
.create(true)
.read(true)
@ -66,7 +66,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
use std::os::unix::io::AsRawFd;
let res = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) };
if res != 0 {
eprintln!("Another instance of vrec-daemon is already running. Exiting.");
eprintln!("Another instance of scythe-daemon is already running. Exiting.");
return Ok(());
}
Some(file)
@ -74,11 +74,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
Err(_) => None,
}
};
let initial_config = vrec::config::VrecConfig::load();
let initial_config = scythe::config::ScytheConfig::load();
// Automatically register dynamic keybindings on Hyprland if active (no config edits needed)
vrec::hyprland_binds::register_hyprland_binds(&initial_config);
vrec::hyprland_binds::spawn_hyprland_reload_watcher();
scythe::hyprland_binds::register_hyprland_binds(&initial_config);
scythe::hyprland_binds::spawn_hyprland_reload_watcher();
let (frame_tx, frame_rx) = bounded::<Frame>(5);
let (cmd_tx, cmd_rx) = bounded::<Command>(32);
@ -92,7 +92,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(unix)]
let listener = {
let socket_path = vrec::ipc::get_socket_path();
let socket_path = scythe::ipc::get_socket_path();
let _ = std::fs::remove_file(&socket_path);
let l = std::os::unix::net::UnixListener::bind(&socket_path)?;
println!("Daemon listening on IPC socket: {}", socket_path);
@ -107,8 +107,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
l
}
Err(e) => {
if vrec::ipc::query_status().is_ok() {
println!("Another instance of vrec-daemon is already running. Exiting cleanly.");
if scythe::ipc::query_status().is_ok() {
println!("Another instance of scythe-daemon is already running. Exiting cleanly.");
return Ok(());
}
std::thread::sleep(std::time::Duration::from_millis(500));
@ -152,7 +152,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} else {
0
};
let cfg = vrec::config::VrecConfig::load();
let cfg = scythe::config::ScytheConfig::load();
let status = DaemonStatus {
is_recording: rec,
recording_duration_sec: duration,
@ -170,7 +170,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
}
},
Command::ToggleCursor => {
let mut cfg = vrec::config::VrecConfig::load();
let mut cfg = scythe::config::ScytheConfig::load();
cfg.show_cursor = !cfg.show_cursor;
let _ = cfg.save();
#[cfg(target_os = "linux")]
@ -186,7 +186,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("Cursor display toggled to: {}", cfg.show_cursor);
},
Command::CycleAudioMode => {
let mut cfg = vrec::config::VrecConfig::load();
let mut cfg = scythe::config::ScytheConfig::load();
cfg.audio_mode = match cfg.audio_mode.as_str() {
"system" => "mic",
"mic" => "both",
@ -199,7 +199,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
},
Command::StopDaemon => {
println!("StopDaemon requested: Finalizing active recordings...");
vrec::hyprland_binds::unregister_hyprland_binds(&vrec::config::VrecConfig::load());
scythe::hyprland_binds::unregister_hyprland_binds(&scythe::config::ScytheConfig::load());
let _ = cmd_tx_ipc.send(Command::StopRecording);
std::thread::sleep(std::time::Duration::from_millis(350));
std::process::exit(0);
@ -225,7 +225,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
println!("\nReceived shutdown signal. Finalizing recordings...");
let _ = cmd_tx_sig.send(Command::StopRecording);
tokio::time::sleep(std::time::Duration::from_millis(350)).await;
vrec::hyprland_binds::unregister_hyprland_binds(&vrec::config::VrecConfig::load());
scythe::hyprland_binds::unregister_hyprland_binds(&scythe::config::ScytheConfig::load());
std::process::exit(0);
}
});
@ -233,7 +233,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string());
println!("Detected session type: {}", session_type);
let audio_capture_initial = vrec::capture::audio::AudioCapture::new_with_device_mode_and_volumes(
let audio_capture_initial = scythe::capture::audio::AudioCapture::new_with_device_mode_and_volumes(
audio_tx.clone(),
Some(&initial_config.audio_device),
&initial_config.audio_mode,
@ -243,12 +243,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut source: Box<dyn FrameSource> = if std::env::args().any(|a| a == "--mock") {
println!("Initializing MOCK capture...");
Box::new(vrec::capture::mock::MockCapture::new())
Box::new(scythe::capture::mock::MockCapture::new())
} else if cfg!(target_os = "windows") {
#[cfg(target_os = "windows")]
{
println!("Initializing Windows DXGI capture...");
Box::new(vrec::capture::windows::WindowsCapture::new()?)
Box::new(scythe::capture::windows::WindowsCapture::new()?)
}
#[cfg(not(target_os = "windows"))]
{
@ -258,7 +258,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(target_os = "linux")]
{
println!("Initializing Wayland capture (cursor: {})...", initial_config.show_cursor);
Box::new(vrec::capture::wayland::WaylandCapture::new_with_cursor(initial_config.show_cursor).await?)
Box::new(scythe::capture::wayland::WaylandCapture::new_with_cursor(initial_config.show_cursor).await?)
}
#[cfg(not(target_os = "linux"))]
{
@ -268,7 +268,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
#[cfg(target_os = "linux")]
{
println!("Initializing X11 capture...");
Box::new(vrec::capture::x11::X11Capture::new()?)
Box::new(scythe::capture::x11::X11Capture::new()?)
}
#[cfg(not(target_os = "linux"))]
{
@ -311,7 +311,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut audio_capture = audio_capture_initial.map(|(c, _, _)| c);
let recorder_handle = thread::spawn(move || {
let mut config = vrec::config::VrecConfig::load();
let mut config = scythe::config::ScytheConfig::load();
replay_state_clone.store(config.replay_enabled, Ordering::SeqCst);
let mut encoder = VideoEncoder::new_with_params(width, height, config.record_bitrate_kbps, config.fps, &config.video_codec)
@ -319,9 +319,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let codec_ctx_ptr = encoder.codec_ctx() as usize;
let mut audio_encoder = if let Some((sr, ch)) = audio_info {
vrec::encoder::AudioEncoder::new(sr as i32, ch as i32).ok()
scythe::encoder::AudioEncoder::new(sr as i32, ch as i32).ok()
} else {
vrec::encoder::AudioEncoder::new(48000, 2).ok()
scythe::encoder::AudioEncoder::new(48000, 2).ok()
};
let audio_codec_ctx_ptr = audio_encoder.as_ref().map(|e| e.codec_ctx() as usize);
@ -339,7 +339,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
while let Ok(drain) = mux_rx.recv() {
let ts = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
let filename = format!("replay_{}.mp4", ts);
let full_path = vrec::config::VrecConfig::load().resolve_save_path(&filename);
let full_path = scythe::config::ScytheConfig::load().resolve_save_path(&filename);
println!("Saving replay to {}...", full_path);
// Find the first video IDR keyframe (ignore audio packets which always have keyframe flag set)
@ -425,7 +425,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
if let Ok(cmd) = cmd_res {
match cmd {
Command::ReloadConfig => {
let new_config = vrec::config::VrecConfig::load();
let new_config = scythe::config::ScytheConfig::load();
if new_config.show_cursor != config.show_cursor {
println!("Cursor display changed ({} -> {}). Restarting daemon for new capture session...", config.show_cursor, new_config.show_cursor);
if let Some(mut m) = normal_muxer.take() {
@ -443,8 +443,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
ring = HeapRb::<Packet>::new(new_capacity);
println!("Replay buffer resized to {} packets.", new_capacity);
}
vrec::hyprland_binds::register_hyprland_binds(&config);
audio_capture = match vrec::capture::audio::AudioCapture::new_with_device_mode_and_volumes(
scythe::hyprland_binds::register_hyprland_binds(&config);
audio_capture = match scythe::capture::audio::AudioCapture::new_with_device_mode_and_volumes(
audio_tx_clone.clone(),
Some(&config.audio_device),
&config.audio_mode,

View file

@ -1,7 +1,7 @@
#![cfg_attr(target_os = "windows", windows_subsystem = "windows")]
use vrec::ipc::{Command, send_command, query_status};
use vrec::overlay::show_notification;
use scythe::ipc::{Command, send_command, query_status};
use scythe::overlay::show_notification;
use std::env;
use global_hotkey::{GlobalHotKeyManager, hotkey::{HotKey, Modifiers, Code}, GlobalHotKeyEvent};
@ -48,30 +48,36 @@ fn get_daemon_cmd() -> std::process::Command {
let mut cmd = if let Ok(mut path) = std::env::current_exe() {
path.pop();
#[cfg(target_os = "windows")]
let candidate = path.join("vrec-daemon.exe");
let primary = path.join("scythe-daemon.exe");
#[cfg(target_os = "windows")]
let fallback = path.join("vrec-daemon.exe");
#[cfg(not(target_os = "windows"))]
let candidate = path.join("vrec-daemon");
let primary = path.join("scythe-daemon");
#[cfg(not(target_os = "windows"))]
let fallback = path.join("vrec-daemon");
if candidate.exists() {
std::process::Command::new(candidate)
if primary.exists() {
std::process::Command::new(primary)
} else if fallback.exists() {
std::process::Command::new(fallback)
} else {
#[cfg(target_os = "windows")]
{
std::process::Command::new("vrec-daemon.exe")
std::process::Command::new("scythe-daemon.exe")
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("vrec-daemon")
std::process::Command::new("scythe-daemon")
}
}
} else {
#[cfg(target_os = "windows")]
{
std::process::Command::new("vrec-daemon.exe")
std::process::Command::new("scythe-daemon.exe")
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("vrec-daemon")
std::process::Command::new("scythe-daemon")
}
};
@ -91,11 +97,11 @@ fn get_ui_cmd() -> std::process::Command {
} else {
#[cfg(target_os = "windows")]
{
std::process::Command::new("vrec-ui.exe")
std::process::Command::new("scythe-ui.exe")
}
#[cfg(not(target_os = "windows"))]
{
std::process::Command::new("vrec-ui")
std::process::Command::new("scythe-ui")
}
};
@ -131,7 +137,7 @@ fn ensure_hotkeys_running() {
use windows::Win32::Foundation::{CloseHandle, GetLastError, WIN32_ERROR};
use windows::Win32::System::Threading::CreateMutexW;
if let Ok(handle) = CreateMutexW(None, false, windows::core::w!("Global\\vrec_hotkeys_single_instance")) {
if let Ok(handle) = CreateMutexW(None, false, windows::core::w!("Global\\scythe_hotkeys_single_instance")) {
if GetLastError() == WIN32_ERROR(183) { // ERROR_ALREADY_EXISTS
let _ = CloseHandle(handle);
return;
@ -157,7 +163,7 @@ fn send_with_notification(cmd: Command, success_msg: &str) -> Result<(), Box<dyn
Ok(())
}
Err(e) => {
show_notification("Error: failed to connect to vrec-daemon");
show_notification("Error: failed to connect to scythe-daemon");
Err(e)
}
}
@ -177,7 +183,7 @@ fn handle_toggle_recording() -> Result<(), Box<dyn std::error::Error + Send + Sy
Ok(())
}
Err(e) => {
show_notification("Error: failed to connect to vrec-daemon");
show_notification("Error: failed to connect to scythe-daemon");
Err(e)
}
}
@ -197,7 +203,7 @@ fn handle_toggle_cursor() -> Result<(), Box<dyn std::error::Error + Send + Sync>
Ok(())
}
Err(e) => {
show_notification("Error: failed to connect to vrec-daemon");
show_notification("Error: failed to connect to scythe-daemon");
Err(e)
}
}
@ -215,16 +221,16 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
"--menu" => {
ensure_daemon_running();
ensure_hotkeys_running();
#[cfg(not(target_os = "windows"))]
vrec::overlay::show_menu_overlay();
#[cfg(target_os = "windows")]
vrec::overlay_egui::run_egui_overlay();
#[cfg(not(target_os = "linux"))]
scythe::overlay_egui::run_egui_overlay();
#[cfg(target_os = "linux")]
scythe::overlay::show_menu_overlay();
return Ok(());
}
"--egui" => {
ensure_daemon_running();
ensure_hotkeys_running();
vrec::overlay_egui::run_egui_overlay();
scythe::overlay_egui::run_egui_overlay();
return Ok(());
}
"--hotkeys" | "--background" => {
@ -259,7 +265,7 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
return Ok(());
}
Err(e) => {
eprintln!("Failed to connect to vrec daemon: {}", e);
eprintln!("Failed to connect to scythe daemon: {}", e);
std::process::exit(1);
}
}
@ -268,19 +274,19 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
return send_command(Command::StopDaemon);
}
"--help" | "-h" => {
println!("vrec-ui - UI Overlay and Hotkey listener for vrec");
println!("scythe-ui - UI Overlay and Hotkey listener for scythe");
println!("Usage:");
println!(" vrec-ui Open the interactive overlay UI menu (default)");
println!(" vrec-ui --menu Open the interactive overlay UI menu");
println!(" vrec-ui --hotkeys Run global hotkey manager in background");
println!(" vrec-ui --status Query current daemon status");
println!(" vrec-ui --save Save instant replay and show notification");
println!(" vrec-ui --record Toggle normal recording on/off (with notification)");
println!(" vrec-ui --cursor Toggle mouse cursor recording on/off (with notification)");
println!(" vrec-ui --start Start normal recording");
println!(" vrec-ui --stop Stop normal recording");
println!(" vrec-ui --reload Reload daemon configuration");
println!(" vrec-ui --quit Stop background daemon");
println!(" scythe-ui Open the interactive overlay UI menu (default)");
println!(" scythe-ui --menu Open the interactive overlay UI menu");
println!(" scythe-ui --hotkeys Run global hotkey manager in background");
println!(" scythe-ui --status Query current daemon status");
println!(" scythe-ui --save Save instant replay and show notification");
println!(" scythe-ui --record Toggle normal recording on/off (with notification)");
println!(" scythe-ui --cursor Toggle mouse cursor recording on/off (with notification)");
println!(" scythe-ui --start Start normal recording");
println!(" scythe-ui --stop Stop normal recording");
println!(" scythe-ui --reload Reload daemon configuration");
println!(" scythe-ui --quit Stop background daemon");
return Ok(());
}
_ => {}
@ -288,10 +294,10 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
} else {
ensure_daemon_running();
ensure_hotkeys_running();
#[cfg(not(target_os = "windows"))]
vrec::overlay::show_menu_overlay();
#[cfg(target_os = "windows")]
vrec::overlay_egui::run_egui_overlay();
#[cfg(not(target_os = "linux"))]
scythe::overlay_egui::run_egui_overlay();
#[cfg(target_os = "linux")]
scythe::overlay::show_menu_overlay();
return Ok(());
}
@ -300,14 +306,14 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
use windows::Win32::Foundation::{GetLastError, WIN32_ERROR};
use windows::Win32::System::Threading::CreateMutexW;
let handle = CreateMutexW(None, true, windows::core::w!("Global\\vrec_hotkeys_single_instance"));
let handle = CreateMutexW(None, true, windows::core::w!("Global\\scythe_hotkeys_single_instance"));
if GetLastError() == WIN32_ERROR(183) {
return Ok(());
}
handle.ok()
};
println!("Starting vrec UI/Hotkey process...");
println!("Starting scythe UI/Hotkey process...");
let session_type = env::var("XDG_SESSION_TYPE").unwrap_or_else(|_| "x11".to_string());
let desktop = env::var("XDG_CURRENT_DESKTOP").unwrap_or_else(|_| "".to_string()).to_lowercase();
@ -319,11 +325,11 @@ fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
if is_gnome || is_kde {
println!("Note: Wayland native layer shell used. Ensure your compositor allows it.");
println!("Tip: Global hotkeys may be restricted by Wayland compositors.");
println!(" You can bind `vrec-ui --menu` or `vrec-ui --save` in your system shortcut settings.");
println!(" You can bind `scythe-ui --menu` or `scythe-ui --save` in your system shortcut settings.");
} else {
println!("wlroots/Hyprland compositor detected.");
vrec::hyprland_binds::register_hyprland_binds(&vrec::config::VrecConfig::load());
vrec::hyprland_binds::spawn_hyprland_reload_watcher();
scythe::hyprland_binds::register_hyprland_binds(&scythe::config::ScytheConfig::load());
scythe::hyprland_binds::spawn_hyprland_reload_watcher();
}
} else {
println!("X11 session detected.");

View file

@ -39,7 +39,7 @@ impl PipeWireStream {
let stream = pw::stream::StreamBox::new(
&core,
"vrec-capture",
"scythe-capture",
properties! {
*pw::keys::MEDIA_TYPE => "Video",
*pw::keys::MEDIA_CATEGORY => "Capture",

View file

@ -20,7 +20,7 @@ fn default_mic_volume() -> f32 { 0.60 }
fn default_system_volume() -> f32 { 1.00 }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct VrecConfig {
pub struct ScytheConfig {
#[serde(default = "default_true")]
pub replay_enabled: bool,
#[serde(default = "default_replay_duration")]
@ -36,7 +36,7 @@ pub struct VrecConfig {
pub fps: u32,
#[serde(default = "default_video_codec")]
pub video_codec: String,
#[serde(default = "VrecConfig::default_output_directory")]
#[serde(default = "ScytheConfig::default_output_directory")]
pub output_directory: String,
#[serde(default = "default_true")]
pub show_cursor: bool,
@ -65,7 +65,9 @@ pub struct VrecConfig {
pub cursor_hotkey: String,
}
impl Default for VrecConfig {
pub type VrecConfig = ScytheConfig;
impl Default for ScytheConfig {
fn default() -> Self {
Self {
replay_enabled: true,
@ -94,18 +96,21 @@ impl Default for VrecConfig {
}
}
impl VrecConfig {
impl ScytheConfig {
pub fn default_output_directory() -> String {
let mut p = dirs::video_dir().unwrap_or_else(|| PathBuf::from("."));
p.push("vrec");
p.push("Scythe");
p.to_string_lossy().to_string()
}
pub fn config_path() -> PathBuf {
let mut path = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
path.push("vrec");
path.push("config.json");
path
let base = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
base.join("scythe").join("config.json")
}
pub fn legacy_config_path() -> PathBuf {
let base = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
base.join("vrec").join("config.json")
}
pub fn expand_tilde(path: &str) -> PathBuf {
@ -134,6 +139,16 @@ impl VrecConfig {
&& let Ok(cfg) = serde_json::from_str(&content) {
return cfg;
}
let legacy = Self::legacy_config_path();
if let Ok(content) = fs::read_to_string(&legacy)
&& let Ok(mut cfg) = serde_json::from_str::<Self>(&content) {
// If the legacy config used the old default directory, update it to the new Scythe directory
if cfg.output_directory.ends_with("/Videos/vrec") || cfg.output_directory.ends_with("\\Videos\\vrec") {
cfg.output_directory = Self::default_output_directory();
}
let _ = cfg.save();
return cfg;
}
let default_cfg = Self::default();
let _ = default_cfg.save();
default_cfg
@ -145,7 +160,15 @@ impl VrecConfig {
fs::create_dir_all(parent)?;
}
let json = serde_json::to_string_pretty(self)?;
fs::write(path, json)?;
fs::write(&path, &json)?;
// Also update legacy config if its directory exists, ensuring backward compatibility
let legacy = Self::legacy_config_path();
if let Some(parent) = legacy.parent()
&& parent.exists() {
let _ = fs::write(legacy, &json);
}
Ok(())
}
@ -160,7 +183,7 @@ mod tests {
#[test]
fn test_config_defaults_and_json() {
let cfg = VrecConfig::default();
let cfg = ScytheConfig::default();
assert_eq!(cfg.replay_duration_sec, 60);
assert_eq!(cfg.replay_bitrate_kbps, 18000);
assert_eq!(cfg.fps, 60);
@ -168,7 +191,7 @@ mod tests {
assert_eq!(cfg.audio_mode, "system");
assert!(cfg.show_cursor);
let json = serde_json::to_string(&cfg).unwrap();
let parsed: VrecConfig = serde_json::from_str(&json).unwrap();
let parsed: ScytheConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.replay_duration_sec, 60);
assert_eq!(parsed.audio_mode, "system");
assert!(parsed.show_cursor);

View file

@ -8,7 +8,8 @@ use std::io::{BufRead, BufReader};
use std::os::unix::net::UnixStream;
#[cfg(unix)]
use std::path::PathBuf;
use crate::config::VrecConfig;
use crate::config::ScytheConfig;
pub type VrecConfig = ScytheConfig;
pub fn is_hyprland() -> bool {
#[cfg(unix)]
@ -99,34 +100,42 @@ pub fn hotkey_to_hyprland_lua(hotkey: &str) -> Option<String> {
#[cfg(unix)]
/// Dynamically inject binds and window rules into running Hyprland without touching hyprland.conf
pub fn register_hyprland_binds(config: &VrecConfig) {
pub fn register_hyprland_binds(config: &ScytheConfig) {
if !is_hyprland() {
return;
}
// Register modern Hyprland (0.55+) layer rules for genuine frosted glass blur & floating window rules
let _ = Command::new("hyprctl")
.args(["eval", r#"hl.layer_rule({ match = { namespace = "scythe-overlay" }, blur = true, ignore_alpha = 0.1 })"#])
.output();
let _ = Command::new("hyprctl")
.args(["eval", r#"hl.layer_rule({ match = { namespace = "scythe-notification" }, blur = true, ignore_alpha = 0.1 })"#])
.output();
let _ = Command::new("hyprctl")
.args(["eval", r#"hl.window_rule({ match = { class = "scythe-overlay" }, float = true, pin = true, stay_focused = true })"#])
.output();
let _ = Command::new("hyprctl")
.args(["eval", r#"hl.layer_rule({ match = { namespace = "vrec-overlay" }, blur = true, ignore_alpha = 0.1 })"#])
.output();
let _ = Command::new("hyprctl")
.args(["eval", r#"hl.layer_rule({ match = { namespace = "vrec-notification" }, blur = true, ignore_alpha = 0.1 })"#])
.output();
let _ = Command::new("hyprctl")
.args(["eval", r#"hl.window_rule({ match = { class = "vrec-overlay" }, float = true, pin = true, stay_focused = true })"#])
.output();
// Also register legacy window rules for backward compatibility with older Hyprland versions
let legacy_rules = [
"float, class:^(scythe-overlay)$",
"move 50% 45, class:^(scythe-overlay)$",
"pin, class:^(scythe-overlay)$",
"stayfocused, class:^(scythe-overlay)$",
"noborder, class:^(scythe-overlay)$",
"float, class:^(scythe-hud)$",
"move 50% 45, class:^(scythe-hud)$",
"pin, class:^(scythe-hud)$",
"stayfocused, class:^(scythe-hud)$",
"noborder, class:^(scythe-hud)$",
"float, class:^(vrec-overlay)$",
"move 50% 45, class:^(vrec-overlay)$",
"pin, class:^(vrec-overlay)$",
"stayfocused, class:^(vrec-overlay)$",
"noborder, class:^(vrec-overlay)$",
"float, class:^(vrec-hud)$",
"move 50% 45, class:^(vrec-hud)$",
"pin, class:^(vrec-hud)$",
"stayfocused, class:^(vrec-hud)$",
"noborder, class:^(vrec-hud)$",
];
for rule in legacy_rules {
let _ = Command::new("hyprctl")
@ -135,10 +144,10 @@ pub fn register_hyprland_binds(config: &VrecConfig) {
}
let binds = [
(&config.menu_hotkey, "vrec-ui --menu"),
(&config.save_hotkey, "vrec-ui --save"),
(&config.record_hotkey, "vrec-ui --record"),
(&config.cursor_hotkey, "vrec-ui --cursor"),
(&config.menu_hotkey, "scythe-ui --menu"),
(&config.save_hotkey, "scythe-ui --save"),
(&config.record_hotkey, "scythe-ui --record"),
(&config.cursor_hotkey, "scythe-ui --cursor"),
];
for (hotkey, cmd) in binds {
@ -170,11 +179,11 @@ pub fn register_hyprland_binds(config: &VrecConfig) {
}
#[cfg(not(unix))]
pub fn register_hyprland_binds(_config: &VrecConfig) {}
pub fn register_hyprland_binds(_config: &ScytheConfig) {}
#[cfg(unix)]
/// Dynamically remove binds from running Hyprland
pub fn unregister_hyprland_binds(config: &VrecConfig) {
pub fn unregister_hyprland_binds(config: &ScytheConfig) {
if !is_hyprland() {
return;
}
@ -207,7 +216,7 @@ pub fn unregister_hyprland_binds(config: &VrecConfig) {
}
#[cfg(not(unix))]
pub fn unregister_hyprland_binds(_config: &VrecConfig) {}
pub fn unregister_hyprland_binds(_config: &ScytheConfig) {}
#[cfg(unix)]
/// Find Hyprland socket2 for live event listening
@ -240,8 +249,8 @@ pub fn spawn_hyprland_reload_watcher() {
let reader = BufReader::new(stream);
for line in reader.lines().map_while(Result::ok) {
if line.starts_with("configreloaded") {
println!("Hyprland config reload detected! Re-registering vrec dynamic binds...");
let config = VrecConfig::load();
println!("Hyprland config reload detected! Re-registering scythe dynamic binds...");
let config = ScytheConfig::load();
register_hyprland_binds(&config);
}
}

View file

@ -49,6 +49,13 @@ fn default_audio_mode_str() -> String {
#[cfg(unix)]
pub fn get_socket_path() -> String {
let runtime_dir = std::env::var("XDG_RUNTIME_DIR")
.unwrap_or_else(|_| format!("/run/user/{}", unsafe { libc::getuid() }));
format!("{}/scythe.sock", runtime_dir)
}
#[cfg(unix)]
pub fn get_legacy_socket_path() -> String {
let runtime_dir = std::env::var("XDG_RUNTIME_DIR")
.unwrap_or_else(|_| format!("/run/user/{}", unsafe { libc::getuid() }));
format!("{}/vrec.sock", runtime_dir)
@ -71,7 +78,9 @@ pub fn send_command(cmd: Command) -> Result<(), Box<dyn std::error::Error + Send
{
use std::os::unix::net::UnixStream;
let socket_path = get_socket_path();
match UnixStream::connect(&socket_path) {
let connect_res = UnixStream::connect(&socket_path)
.or_else(|_| UnixStream::connect(get_legacy_socket_path()));
match connect_res {
Ok(mut stream) => {
let _ = stream.set_write_timeout(Some(Duration::from_millis(1000)));
let _ = stream.set_read_timeout(Some(Duration::from_millis(1000)));
@ -104,7 +113,7 @@ pub fn send_command(cmd: Command) -> Result<(), Box<dyn std::error::Error + Send
}
}
Err(last_err.unwrap_or_else(|| "Failed to communicate with vrec-daemon".into()))
Err(last_err.unwrap_or_else(|| "Failed to communicate with scythe-daemon".into()))
}
pub fn query_status() -> Result<DaemonStatus, Box<dyn std::error::Error + Send + Sync>> {
@ -125,6 +134,7 @@ pub fn query_status() -> Result<DaemonStatus, Box<dyn std::error::Error + Send +
use std::os::unix::net::UnixStream;
let socket_path = get_socket_path();
UnixStream::connect(&socket_path)
.or_else(|_| UnixStream::connect(get_legacy_socket_path()))
};
#[cfg(windows)]
@ -162,5 +172,5 @@ pub fn query_status() -> Result<DaemonStatus, Box<dyn std::error::Error + Send +
}
}
Err(last_err.unwrap_or_else(|| "Failed to query status from vrec-daemon".into()))
Err(last_err.unwrap_or_else(|| "Failed to query status from scythe-daemon".into()))
}

View file

@ -10,7 +10,7 @@ use std::f64::consts::PI;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use crate::config::VrecConfig;
use crate::config::ScytheConfig;
use crate::ipc::{self, Command};
pub fn show_notification_overlay() {
@ -28,7 +28,7 @@ pub fn show_notification(message: &str) {
$notify = new-object system.windows.forms.notifyicon; \
$notify.icon = [System.Drawing.SystemIcons]::Information; \
$notify.visible = $true; \
$notify.showballoontip(2000, 'vrec', '{}', [system.windows.forms.tooltipicon]::Info); \
$notify.showballoontip(2000, 'scythe', '{}', [system.windows.forms.tooltipicon]::Info); \
Start-Sleep -Seconds 2; \
$notify.dispose()",
msg.replace('\'', "''")
@ -51,7 +51,7 @@ pub fn show_notification(message: &str) {
}
let app = Application::builder()
.application_id("com.vrec.notification")
.application_id("com.scythe.notification")
.build();
let msg_text = message.to_string();
@ -70,7 +70,7 @@ pub fn show_notification(message: &str) {
if layer_shell_ok {
window.init_layer_shell();
window.set_layer(Layer::Overlay);
window.set_namespace("vrec-notification");
window.set_namespace("scythe-notification");
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 40);
window.set_anchor(gtk_layer_shell::Edge::Top, true);
} else {
@ -141,7 +141,7 @@ pub fn show_notification(message: &str) {
hbox.style_context().add_class("notify-box");
hbox.set_halign(gtk::Align::Center);
let badge = Label::new(Some("VREC"));
let badge = Label::new(Some("SCYTHE"));
badge.style_context().add_class("notify-badge");
let label = Label::new(Some(&msg_text));
label.style_context().add_class("notify-label");
@ -253,9 +253,16 @@ fn draw_gear_icon(cr: &gtk::cairo::Context, width: f64, height: f64) {
}
pub fn show_menu_overlay() {
#[cfg(unix)]
#[cfg(target_os = "windows")]
{
crate::overlay_egui::run_egui_overlay();
return;
}
#[cfg(not(target_os = "windows"))]
{
if std::env::var("WAYLAND_DISPLAY").is_err() && std::env::var("DISPLAY").is_err() {
eprintln!("Note: No display server detected for GTK. Falling back to cross-platform egui overlay...");
eprintln!("Warning: No display found for GTK menu overlay. Falling back to cross-platform egui overlay...");
crate::overlay_egui::run_egui_overlay();
return;
}
@ -266,12 +273,12 @@ pub fn show_menu_overlay() {
}
let app = Application::builder()
.application_id("com.vrec.hud")
.application_id("com.scythe.hud")
.flags(gtk::gio::ApplicationFlags::NON_UNIQUE)
.build();
app.connect_activate(|app| {
let config = VrecConfig::load();
let config = ScytheConfig::load();
let window = ApplicationWindow::builder()
.application(app)
@ -287,7 +294,7 @@ pub fn show_menu_overlay() {
if layer_shell_ok {
window.init_layer_shell();
window.set_layer(Layer::Overlay);
window.set_namespace("vrec-overlay");
window.set_namespace("scythe-overlay");
window.set_keyboard_interactivity(true);
// Shifted higher towards the top (50px margin) per user feedback
window.set_layer_shell_margin(gtk_layer_shell::Edge::Top, 50);
@ -335,8 +342,8 @@ pub fn show_menu_overlay() {
let header_box = Box::new(Orientation::Horizontal, 10);
header_box.style_context().add_class("header-bar");
let brand_badge = Label::new(Some("VREC"));
brand_badge.style_context().add_class("vrec-badge");
let brand_badge = Label::new(Some("SCYTHE"));
brand_badge.style_context().add_class("scythe-badge");
let title_label = Label::new(Some("SHADOWPLAY OVERLAY"));
title_label.style_context().add_class("overlay-title");
@ -887,6 +894,7 @@ pub fn show_menu_overlay() {
padding: 8px 18px;
box-shadow: 0px 12px 32px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.16);
}
.scythe-badge,
.vrec-badge {
background-color: #76b900;
color: #0b1204;
@ -1181,10 +1189,10 @@ pub fn show_menu_overlay() {
let rev_r_hide = replay_revealer.clone();
let card_r_deactivate = replay_card_btn.clone();
replay_toggle_item.connect_clicked(move |_| {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
cfg.replay_enabled = !cfg.replay_enabled;
let _ = cfg.save();
VrecConfig::notify_daemon_reload();
ScytheConfig::notify_daemon_reload();
r_state_sync.store(cfg.replay_enabled, Ordering::Relaxed);
r_icon_sync.queue_draw();
@ -1251,7 +1259,7 @@ pub fn show_menu_overlay() {
// Direct Mouse Cursor Switch Toggle
let banner_cur = status_banner.clone();
cursor_switch.connect_state_set(move |_, active| {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
if cfg.show_cursor != active {
cfg.show_cursor = active;
let _ = cfg.save();
@ -1275,7 +1283,7 @@ pub fn show_menu_overlay() {
let cur_sw_sync = cursor_switch.clone();
apply_btn.connect_clicked(move |_| {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
cfg.show_cursor = cur_sw_sync.is_active();
cfg.fps = fps_spin.value() as u32;
let bit_mbps = bit_spin.value() as u32;
@ -1305,7 +1313,7 @@ pub fn show_menu_overlay() {
cfg.autostart = auto_switch.is_active();
let _ = cfg.save();
VrecConfig::notify_daemon_reload();
ScytheConfig::notify_daemon_reload();
r_sub_lbl_sync.set_text(&format!("{}s Buffer", cfg.replay_duration_sec));
rec_sub_lbl_sync.set_text(&format!("{} FPS • {} Mbps", cfg.fps, cfg.record_bitrate_kbps / 1000));
@ -1387,3 +1395,4 @@ pub fn show_menu_overlay() {
app.run_with_args(&[] as &[&str]);
}
}

View file

@ -3,7 +3,7 @@ use egui::{Color32, CornerRadius, FontId, Margin, Stroke, Vec2};
use std::path::PathBuf;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::time::{Duration, Instant, SystemTime};
use crate::config::VrecConfig;
use crate::config::ScytheConfig;
use crate::ipc::{self, Command, DaemonStatus};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -22,7 +22,7 @@ pub struct VideoClipInfo {
}
fn scan_recordings(dir_str: &str) -> Vec<VideoClipInfo> {
let dir = VrecConfig::expand_tilde(dir_str);
let dir = ScytheConfig::expand_tilde(dir_str);
let mut clips = Vec::new();
if let Ok(entries) = std::fs::read_dir(&dir) {
for entry in entries.flatten() {
@ -424,8 +424,8 @@ fn render_section_card(ui: &mut egui::Ui, header: &str, add_contents: impl FnOnc
});
}
pub struct VrecOverlayApp {
config: VrecConfig,
pub struct ScytheOverlayApp {
config: ScytheConfig,
status: DaemonStatus,
daemon_connected: bool,
current_view: ShadowPlayView,
@ -449,15 +449,17 @@ pub struct VrecOverlayApp {
initial_pos_set: bool,
}
impl Default for VrecOverlayApp {
pub type VrecOverlayApp = ScytheOverlayApp;
impl Default for ScytheOverlayApp {
fn default() -> Self {
Self::new()
}
}
impl VrecOverlayApp {
impl ScytheOverlayApp {
pub fn new() -> Self {
let config = VrecConfig::load();
let config = ScytheConfig::load();
let replay_sec = config.replay_duration_sec;
let bitrate_mbps = (config.record_bitrate_kbps / 1000).max(1);
let target_fps = config.fps;
@ -571,13 +573,13 @@ impl VrecOverlayApp {
.show(ui, |ui| {
ui.set_width(730.0);
ui.horizontal(|ui| {
// VREC Badge
let (badge_rect, _) = ui.allocate_exact_size(Vec2::new(44.0, 20.0), egui::Sense::hover());
// SCYTHE Badge
let (badge_rect, _) = ui.allocate_exact_size(Vec2::new(56.0, 20.0), egui::Sense::hover());
ui.painter().rect_filled(badge_rect, CornerRadius::same(4_u8), Color32::from_rgb(118, 185, 0));
ui.painter().text(
badge_rect.center(),
egui::Align2::CENTER_CENTER,
"VREC",
"SCYTHE",
FontId::proportional(11.0),
Color32::from_rgb(11, 18, 4),
);
@ -674,10 +676,10 @@ impl VrecOverlayApp {
render_dropdown_menu(ui, card_w, |ui| {
let toggle_text = if is_replay_active { "Turn off" } else { "Turn on" };
if render_menu_item(ui, toggle_text) {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
cfg.replay_enabled = !cfg.replay_enabled;
let _ = cfg.save();
VrecConfig::notify_daemon_reload();
ScytheConfig::notify_daemon_reload();
self.config.replay_enabled = cfg.replay_enabled;
self.status.is_replay_active = cfg.replay_enabled;
self.replay_dropdown_open = false;
@ -958,7 +960,7 @@ impl VrecOverlayApp {
pick_folder(&self.output_dir, self.folder_tx.clone());
}
if ui.button(egui::RichText::new("Open").size(11.0)).clicked() {
open_folder(&VrecConfig::expand_tilde(&self.output_dir));
open_folder(&ScytheConfig::expand_tilde(&self.output_dir));
}
});
ui.label(egui::RichText::new(format!("{} video recordings in destination folder.", self.clips.len())).size(10.5).color(Color32::from_rgb(148, 163, 184)));
@ -1027,7 +1029,7 @@ impl VrecOverlayApp {
}
}
impl eframe::App for VrecOverlayApp {
impl eframe::App for ScytheOverlayApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
self.poll_async_events();
self.anim_time += 0.033;
@ -1075,8 +1077,8 @@ impl eframe::App for VrecOverlayApp {
pub fn run_egui_overlay() {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("vrec ShadowPlay")
.with_app_id("vrec-overlay")
.with_title("Scythe")
.with_app_id("scythe-overlay")
.with_position([400.0, 50.0])
.with_inner_size([760.0, 270.0])
.with_min_inner_size([740.0, 250.0])
@ -1089,8 +1091,8 @@ pub fn run_egui_overlay() {
};
let _ = eframe::run_native(
"vrec-overlay",
"scythe-overlay",
options,
Box::new(|_cc| Ok(Box::new(VrecOverlayApp::new()))),
Box::new(|_cc| Ok(Box::new(ScytheOverlayApp::new()))),
);
}

View file

@ -1,8 +1,8 @@
use gtk::prelude::*;
use gtk::{Window, WindowType, Box, Orientation, Label, Entry, Button, Switch, ComboBoxText};
use crate::config::VrecConfig;
use crate::config::ScytheConfig;
pub fn open_replay_settings(config_in: &VrecConfig) {
pub fn open_replay_settings(config_in: &ScytheConfig) {
let window = Window::new(WindowType::Toplevel);
window.set_title("Replay Settings");
window.set_default_size(320, 200);
@ -32,7 +32,7 @@ pub fn open_replay_settings(config_in: &VrecConfig) {
let window_clone = window.clone();
save_btn.connect_clicked(move |_| {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
if let Ok(val) = duration_entry.text().parse() {
cfg.replay_duration_sec = val;
}
@ -40,7 +40,7 @@ pub fn open_replay_settings(config_in: &VrecConfig) {
cfg.replay_bitrate_kbps = val;
}
let _ = cfg.save();
VrecConfig::notify_daemon_reload();
ScytheConfig::notify_daemon_reload();
window_clone.close();
});
@ -57,7 +57,7 @@ pub fn open_replay_settings(config_in: &VrecConfig) {
window.show_all();
}
pub fn open_record_settings(config_in: &VrecConfig) {
pub fn open_record_settings(config_in: &ScytheConfig) {
let window = Window::new(WindowType::Toplevel);
window.set_title("Record Settings");
window.set_default_size(320, 150);
@ -80,12 +80,12 @@ pub fn open_record_settings(config_in: &VrecConfig) {
let window_clone = window.clone();
save_btn.connect_clicked(move |_| {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
if let Ok(val) = bitrate_entry.text().parse() {
cfg.record_bitrate_kbps = val;
}
let _ = cfg.save();
VrecConfig::notify_daemon_reload();
ScytheConfig::notify_daemon_reload();
window_clone.close();
});
@ -102,7 +102,7 @@ pub fn open_record_settings(config_in: &VrecConfig) {
window.show_all();
}
pub fn open_general_settings(config_in: &VrecConfig) {
pub fn open_general_settings(config_in: &ScytheConfig) {
let window = Window::new(WindowType::Toplevel);
window.set_title("General Settings");
window.set_default_size(380, 320);
@ -160,7 +160,7 @@ pub fn open_general_settings(config_in: &VrecConfig) {
let window_clone = window.clone();
save_btn.connect_clicked(move |_| {
let mut cfg = VrecConfig::load();
let mut cfg = ScytheConfig::load();
cfg.autostart = auto_switch.is_active();
if let Some(txt) = theme_combo.active_text() {
cfg.ui_color_theme = txt.to_string();
@ -169,7 +169,7 @@ pub fn open_general_settings(config_in: &VrecConfig) {
cfg.menu_hotkey = menu_hk_entry.text().to_string();
cfg.save_hotkey = save_hk_entry.text().to_string();
let _ = cfg.save();
VrecConfig::notify_daemon_reload();
ScytheConfig::notify_daemon_reload();
window_clone.close();
});

View file

@ -2,27 +2,27 @@
set -e
echo "============================================="
echo " vrec Automated End-to-End Test Suite "
echo " Scythe Automated End-to-End Test Suite "
echo "============================================="
# Ensure binaries are compiled
echo "[1/6] Building release binaries..."
cargo build --release --quiet
SOCKET_PATH="${XDG_RUNTIME_DIR:-/tmp}/vrec.sock"
SOCKET_PATH="${XDG_RUNTIME_DIR:-/tmp}/scythe.sock"
rm -f "$SOCKET_PATH"
rm -f record_*.mp4 replay_*.mp4
SAVE_DIR=$(python3 -c 'import json, os; print(json.load(open(os.path.expanduser("~/.config/vrec/config.json")))["output_directory"])' 2>/dev/null || echo "$HOME/Videos/vrec")
SAVE_DIR=$(python3 -c 'import json, os; p=os.path.expanduser("~/.config/scythe/config.json"); print(json.load(open(p))["output_directory"]) if os.path.exists(p) else print(os.path.expanduser("~/Videos/Scythe"))' 2>/dev/null || echo "$HOME/Videos/Scythe")
mkdir -p "$SAVE_DIR"
echo "[2/6] Starting vrec-daemon with mock capture..."
target/release/vrec-daemon --mock > daemon_test.log 2>&1 &
echo "[2/6] Starting scythe-daemon with mock capture..."
target/release/scythe-daemon --mock > daemon_test.log 2>&1 &
DAEMON_PID=$!
cleanup() {
if kill -0 $DAEMON_PID 2>/dev/null; then
target/release/vrec-ui --quit 2>/dev/null || kill -9 $DAEMON_PID 2>/dev/null || true
target/release/scythe-ui --quit 2>/dev/null || kill -9 $DAEMON_PID 2>/dev/null || true
fi
rm -f daemon_test.log
}
@ -46,23 +46,23 @@ echo "Daemon is live (PID: $DAEMON_PID, Socket: $SOCKET_PATH)"
# Test 1: Start Normal Recording
echo "[3/6] Testing: Start Normal Recording..."
target/release/vrec-ui --start
target/release/scythe-ui --start
echo "Recording in progress for 3 seconds..."
sleep 3
# Test 2: Stop Normal Recording
echo "[4/6] Testing: Stop Normal Recording..."
target/release/vrec-ui --stop
target/release/scythe-ui --stop
sleep 1
# Test 3: Save Instant Replay
echo "[5/6] Testing: Save Instant Replay buffer..."
target/release/vrec-ui --save
target/release/scythe-ui --save
sleep 2
# Test 4: Quit Daemon
echo "[6/6] Testing: Clean Daemon Shutdown..."
target/release/vrec-ui --quit
target/release/scythe-ui --quit
wait $DAEMON_PID 2>/dev/null || true
echo "Daemon shutdown cleanly."
@ -72,7 +72,7 @@ echo " Verifying Generated Recordings with ffprobe"
echo "============================================="
FOUND_ANY=0
for f in $(find "$SAVE_DIR" ./vrec . -maxdepth 2 \( -name "record_*.mp4" -o -name "replay_*.mp4" \) 2>/dev/null); do
for f in $(find "$SAVE_DIR" "$HOME/Videos/Scythe" "$HOME/Videos/vrec" . -maxdepth 2 \( -name "record_*.mp4" -o -name "replay_*.mp4" \) 2>/dev/null); do
if [ -f "$f" ]; then
FOUND_ANY=1
echo "--> Testing file: $f ($(ls -lh "$f" | awk '{print $5}'))"