qs: add replay functionality (yet disabled)
This commit is contained in:
@@ -29,6 +29,7 @@ binds {
|
||||
Mod+Shift+K { spawn-sh "quickshell-kill || quickshell"; }
|
||||
Mod+I { spawn "qs" "ipc" "call" "idleInhibitor" "toggle"; }
|
||||
Mod+Alt+R { spawn "qs" "ipc" "call" "recording" "startOrStop"; }
|
||||
Mod+Alt+G { spawn "qs" "ipc" "call" "recording" "saveReplay"; }
|
||||
Mod+Shift+E { spawn "qs" "ipc" "call" "sunset" "toggle"; }
|
||||
Mod+X { spawn "qs" "ipc" "call" "notes" "openRecent"; }
|
||||
Mod+Shift+X { spawn "qs" "ipc" "call" "notes" "create"; }
|
||||
|
||||
@@ -24,6 +24,6 @@ environment {
|
||||
XCURSOR_THEME "Bibata-Modern-Ice"
|
||||
XCURSOR_SIZE "24"
|
||||
ELECTRON_OZONE_PLATFORM_HINT "wayland"
|
||||
QSG_RHI_BACKEND "vulkan"
|
||||
// QSG_RHI_BACKEND "vulkan"
|
||||
// GSK_RENDERER "vulkan"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
screenshot-path "~/Pictures/Screenshots/niri_screenshot_%Y-%m-%d_%H-%M-%S.png"
|
||||
|
||||
debug {
|
||||
render-drm-device "/dev/dri/renderD128"
|
||||
render-drm-device "/dev/dri/renderD129"
|
||||
}
|
||||
|
||||
// gestures {
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
|
||||
environment {
|
||||
__NV_PRIME_RENDER_OFFLOAD "1"
|
||||
__VK_LAYER_NV_optimus "NVIDIA_only"
|
||||
__GLX_VENDOR_LIBRARY_NAME "nvidia"
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ Item {
|
||||
RecordService.startOrStop();
|
||||
}
|
||||
|
||||
function saveReplay() {
|
||||
RecordService.stopReplay();
|
||||
}
|
||||
|
||||
target: "recording"
|
||||
}
|
||||
|
||||
|
||||
@@ -7,18 +7,33 @@ import qs.Utils
|
||||
pragma Singleton
|
||||
|
||||
Singleton {
|
||||
// Connections {
|
||||
// function onSinkChanged() {
|
||||
// if (!isReplayInitStarted && AudioService.sink) {
|
||||
// Logger.i("RecordService", "Audio sink available, starting replay buffer.");
|
||||
// startReplay();
|
||||
// isReplayInitStarted = true;
|
||||
// }
|
||||
// }
|
||||
// target: AudioService
|
||||
// }
|
||||
|
||||
readonly property string recordingDir: Paths.recordingDir
|
||||
property bool isRecording: false
|
||||
property bool isReplayInitStarted: false
|
||||
property bool isReplayStarted: false
|
||||
property bool isStopping: false
|
||||
property bool isReplayStopping: false
|
||||
readonly property string codec: "libx264"
|
||||
readonly property string container: "mkv"
|
||||
readonly property string pixelFormat: "yuv420p"
|
||||
property string recordingDisplay: ""
|
||||
readonly property int replayDuration: 15
|
||||
readonly property int framerate: 60
|
||||
readonly property var codecParams: Object.freeze(["preset=ultrafast", "crf=15", "tune=zerolatency", "color_range=tv"])
|
||||
readonly property var filterArgs: ""
|
||||
|
||||
function getFilename() {
|
||||
function getFilename(prefix = "recording") {
|
||||
var d = new Date();
|
||||
var year = d.getFullYear();
|
||||
var month = ("0" + (d.getMonth() + 1)).slice(-2);
|
||||
@@ -26,7 +41,7 @@ Singleton {
|
||||
var hours = ("0" + d.getHours()).slice(-2);
|
||||
var minutes = ("0" + d.getMinutes()).slice(-2);
|
||||
var seconds = ("0" + d.getSeconds()).slice(-2);
|
||||
return "recording_" + year + "-" + month + "-" + day + "_" + hours + "." + minutes + "." + seconds + "." + container;
|
||||
return prefix + "_" + year + "-" + month + "-" + day + "_" + hours + "." + minutes + "." + seconds + "." + container;
|
||||
}
|
||||
|
||||
function getAudioSink() {
|
||||
@@ -37,6 +52,15 @@ Singleton {
|
||||
return Niri.focusedOutput || null;
|
||||
}
|
||||
|
||||
function getPrimaryVideoSource() {
|
||||
for (const screen of Quickshell.screens) {
|
||||
if (screen.name === "DP-1")
|
||||
return screen.name;
|
||||
|
||||
}
|
||||
return Quickshell.screens.length ? Quickshell.screens[0].name : null;
|
||||
}
|
||||
|
||||
function startOrStop() {
|
||||
if (isRecording)
|
||||
stop();
|
||||
@@ -76,7 +100,7 @@ Singleton {
|
||||
Logger.e("RecordService", "No audio sink available.");
|
||||
return ;
|
||||
}
|
||||
recordProcess.filePath = recordingDir + getFilename();
|
||||
recordProcess.filePath = recordingDir + getFilename("recording");
|
||||
recordProcess.command = ["wf-recorder", "--audio=" + audioSink, "-o", source, "--codec", codec, "--pixel-format", pixelFormat, "--framerate", framerate.toString(), "-f", recordProcess.filePath];
|
||||
for (const param of codecParams) {
|
||||
recordProcess.command.push("-p");
|
||||
@@ -87,39 +111,78 @@ Singleton {
|
||||
recordProcess.command.push(filterArgs);
|
||||
}
|
||||
Logger.i("RecordService", "Starting recording with command: " + recordProcess.command.join(" "));
|
||||
recordProcess.onErrorExit = function() {
|
||||
Logger.e("RecordService", "Recording process exited with an error.");
|
||||
SendNotification.show("Recording failed", "An error occurred while trying to record the screen.");
|
||||
};
|
||||
recordProcess.onNormalExit = function() {
|
||||
Logger.i("RecordService", "Recording stopped, file saved to: " + recordProcess.filePath);
|
||||
SendNotification.show("Recording stopped", recordProcess.filePath);
|
||||
};
|
||||
recordProcess.running = true;
|
||||
SendNotification.show("Recording started", "Recording to " + recordProcess.filePath);
|
||||
}
|
||||
|
||||
function startReplay() {
|
||||
if (isReplayStarted || isReplayStopping) {
|
||||
Logger.w("RecordService", "Replay buffer already active, cannot start.");
|
||||
return ;
|
||||
}
|
||||
isReplayStarted = true;
|
||||
const source = getPrimaryVideoSource();
|
||||
if (!source) {
|
||||
SendNotification.show("Replay buffer failed", "Could not determine which display to record from.");
|
||||
Logger.e("RecordService", "No recording source available for replay buffer.");
|
||||
return ;
|
||||
}
|
||||
const audioSink = getAudioSink();
|
||||
if (!audioSink) {
|
||||
SendNotification.show("Replay buffer failed", "No audio sink available to record from.");
|
||||
Logger.e("RecordService", "No audio sink available for replay buffer.");
|
||||
return ;
|
||||
}
|
||||
replayProcess.filePath = recordingDir + getFilename("replay");
|
||||
replayProcess.command = ["wf-recorder", "--history", replayDuration, "--audio=" + audioSink, "-o", source, "--codec", codec, "--pixel-format", pixelFormat, "--framerate", framerate.toString(), "-f", replayProcess.filePath];
|
||||
for (const param of codecParams) {
|
||||
replayProcess.command.push("-p");
|
||||
replayProcess.command.push(param);
|
||||
}
|
||||
if (filterArgs !== "") {
|
||||
replayProcess.command.push("-F");
|
||||
replayProcess.command.push(filterArgs);
|
||||
}
|
||||
Logger.i("RecordService", "Starting replay buffer with command: " + replayProcess.command.join(" "));
|
||||
replayProcess.running = true;
|
||||
}
|
||||
|
||||
function stopReplay() {
|
||||
if (!isReplayStarted) {
|
||||
Logger.w("RecordService", "Replay buffer not active, cannot stop.");
|
||||
return ;
|
||||
}
|
||||
if (isReplayStopping) {
|
||||
Logger.w("RecordService", "Already stopping replay buffer, please wait.");
|
||||
return ;
|
||||
}
|
||||
isReplayStopping = true;
|
||||
replayStopTimeout.restart();
|
||||
replayProcess.signal(10); // SIGUSR1
|
||||
}
|
||||
|
||||
Component.onDestruction: function() {
|
||||
if (recordProcess.running)
|
||||
recordProcess.running = false;
|
||||
|
||||
if (replayProcess.running)
|
||||
replayProcess.signal(2);
|
||||
|
||||
}
|
||||
|
||||
Process {
|
||||
id: recordProcess
|
||||
|
||||
property string filePath: ""
|
||||
property var onNormalExit: null
|
||||
property var onErrorExit: null
|
||||
|
||||
running: false
|
||||
onExited: function(exitCode, exitStatus) {
|
||||
if (exitCode === 0) {
|
||||
Logger.i("RecordService", "Recording stopped successfully.");
|
||||
if (onNormalExit) {
|
||||
onNormalExit();
|
||||
onNormalExit = null;
|
||||
}
|
||||
SendNotification.show("Recording stopped", "File saved to: " + filePath);
|
||||
} else {
|
||||
Logger.e("RecordService", "Recording process exited with error code: " + exitCode);
|
||||
if (onErrorExit) {
|
||||
onErrorExit();
|
||||
onErrorExit = null;
|
||||
}
|
||||
SendNotification.show("Recording failed", "An error occurred while trying to record the screen.");
|
||||
}
|
||||
isRecording = false;
|
||||
isStopping = false;
|
||||
@@ -127,4 +190,50 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: replayProcess
|
||||
|
||||
property string filePath: ""
|
||||
|
||||
running: false
|
||||
onExited: function(exitCode, exitStatus) {
|
||||
replayStopTimeout.stop();
|
||||
if (exitCode === 0) {
|
||||
Logger.i("RecordService", "Replay buffer saved successfully.");
|
||||
SendNotification.show("Replay saved", "File saved to: " + filePath);
|
||||
} else {
|
||||
Logger.e("RecordService", "Replay process exited with error code: " + exitCode);
|
||||
SendNotification.show("Replay failed", "An error occurred while trying to record the replay buffer.");
|
||||
}
|
||||
isReplayStarted = false;
|
||||
isReplayStopping = false;
|
||||
replayRestartTimer.restart();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: replayRestartTimer
|
||||
|
||||
interval: 1000
|
||||
repeat: false
|
||||
onTriggered: function() {
|
||||
if (!isReplayStarted && !isReplayStopping)
|
||||
startReplay();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: replayStopTimeout
|
||||
|
||||
interval: 5000
|
||||
repeat: false
|
||||
onTriggered: function() {
|
||||
if (isReplayStopping) {
|
||||
Logger.w("RecordService", "Replay buffer did not stop in time, killing process.");
|
||||
replayProcess.running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
pid=$(pgrep -x quickshell)
|
||||
[ -z "$pid" ] && exit 1
|
||||
|
||||
for child in $(pgrep -P "$pid" 2>/dev/null); do
|
||||
kill "$child"
|
||||
done
|
||||
# for child in $(pgrep -P "$pid" 2>/dev/null); do
|
||||
# kill "$child"
|
||||
# done
|
||||
|
||||
sleep 0.3
|
||||
children=$(pgrep -P "$pid" 2>/dev/null)
|
||||
|
||||
kill "$pid"
|
||||
|
||||
sleep 0.5
|
||||
|
||||
for child in $children; do
|
||||
kill "$child" || true
|
||||
done
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
# Constants
|
||||
niri_config_file="$HOME/.config/niri/config/misc.kdl"
|
||||
prefer_order=(intel nvidia)
|
||||
prefer_order=(nvidia intel)
|
||||
|
||||
# Get vendor and path of each GPU
|
||||
default_card_path="$(find /dev/dri/card* 2>/dev/null | head -n 1)"
|
||||
|
||||
Reference in New Issue
Block a user