From c57d512cedcbdd6c0301bc36624d1471c2b2e7af Mon Sep 17 00:00:00 2001 From: davfsa Date: Tue, 25 Mar 2025 02:32:29 +0100 Subject: [PATCH 01/11] Fix notifications border rounding (#856) Signed-off-by: davfsa --- src/scss/style/menus/notifications.scss | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/scss/style/menus/notifications.scss b/src/scss/style/menus/notifications.scss index 61ba575..185d6bb 100644 --- a/src/scss/style/menus/notifications.scss +++ b/src/scss/style/menus/notifications.scss @@ -36,9 +36,7 @@ min-height: 0em; font-size: $font-size * $bar-menus-menu-notifications-scaling * 0.01; border: 0.15em solid $notification-border; - border-radius: 0em; - border-bottom-left-radius: $notification-border_radius; - border-top-left-radius: $notification-border_radius; + border-radius: $notification-border_radius; margin: 0em; } From dba7ac64c6c3ff81adfafac45bf0200a4dc89d97 Mon Sep 17 00:00:00 2001 From: Siddharth Jain <83913594+siddharthjain25@users.noreply.github.com> Date: Tue, 25 Mar 2025 09:22:17 +0530 Subject: [PATCH 02/11] Feat: Added a configuration option to define the save location of recordings. * Add file picker for saving screen recordings Implemented a file picker using Zenity to allow users to choose the save location for their screen recordings after stopping. Replaced the hardcoded save path with dynamic user input. Improved the notification system to inform users when recordings are saved or discarded. * Refactored RecordingButton to fetch the latest recording path dynamically. Removed static path references, ensuring the updated path from Hyprpanel config is always used. * Update screen_record.sh Added comment why use "sleep 1" at line 80 * Update module.nix Updated nix module. * Expand ~ in output directory, set default path, and add validation - Properly expand `~` to `$HOME` in the output directory path. - Set default recording directory to `$HOME/Videos` if none is provided. - Validate that the output directory exists before starting a recording. * Update scripts/screen_record.sh Co-authored-by: Chase Taylor <11805686+dotaxis@users.noreply.github.com> * Update scripts/screen_record.sh Co-authored-by: Chase Taylor <11805686+dotaxis@users.noreply.github.com> * Code Quality Check. * Update RecordingButton.tsx Removed debug logs as well. * Update src/components/menus/dashboard/shortcuts/buttons/RecordingButton.tsx Co-authored-by: Jas Singh * updated RecordingButton.tsx && helper.tsx Fixed the issues pointed by @Jas-SinghFSU * Update RecordingButton.tsx Fixed few linter errors. --------- Co-authored-by: Chase Taylor <11805686+dotaxis@users.noreply.github.com> Co-authored-by: Jas Singh --- nix/module.nix | 1 + package-lock.json | 7 +- scripts/screen_record.sh | 141 ++++++++++++------ .../shortcuts/buttons/RecordingButton.tsx | 60 ++++---- .../menus/dashboard/shortcuts/helpers.ts | 38 +++-- .../settings/pages/config/menus/dashboard.tsx | 3 + src/options.ts | 3 + 7 files changed, 163 insertions(+), 90 deletions(-) diff --git a/nix/module.nix b/nix/module.nix index 4c54c46..478889a 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -394,6 +394,7 @@ in menus.dashboard.powermenu.reboot = mkStrOption "systemctl reboot"; menus.dashboard.powermenu.shutdown = mkStrOption "systemctl poweroff"; menus.dashboard.powermenu.sleep = mkStrOption "systemctl suspend"; + menus.dashboard.recording.path = mkStrOption "$HOME/Videos/Screencasts" menus.dashboard.shortcuts.enabled = mkBoolOption true; menus.dashboard.shortcuts.left.shortcut1.command = mkStrOption "microsoft-edge-stable"; menus.dashboard.shortcuts.left.shortcut1.icon = mkStrOption "󰇩"; diff --git a/package-lock.json b/package-lock.json index fc70eb6..7d53acc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,8 +24,13 @@ "typescript": "^5.6.2" } }, + "../../../../../usr/share/astal/gjs": { + "name": "astal", + "license": "LGPL-2.1" + }, "../../../../usr/share/astal/gjs": { "name": "astal", + "extraneous": true, "license": "LGPL-2.1" }, "node_modules/@eslint-community/eslint-utils": { @@ -601,7 +606,7 @@ } }, "node_modules/astal": { - "resolved": "../../../../usr/share/astal/gjs", + "resolved": "../../../../../usr/share/astal/gjs", "link": true }, "node_modules/available-typed-arrays": { diff --git a/scripts/screen_record.sh b/scripts/screen_record.sh index f203689..3854db9 100755 --- a/scripts/screen_record.sh +++ b/scripts/screen_record.sh @@ -1,18 +1,18 @@ #!/usr/bin/env bash # Requires wf-recorder: https://github.com/ammen99/wf-recorder -outputDir="$HOME/Videos/Screencasts" +# Get the default audio sink defaultSink=$(pactl get-default-sink) WF_RECORDER_OPTS="--audio=$defaultSink.monitor -c libx264rgb" +outputFile="" +outputDir="" +# Function to check if recording is active checkRecording() { - if pgrep -f "wf-recorder" >/dev/null; then - return 0 - else - return 1 - fi + pgrep -f "wf-recorder" >/dev/null } +# Function to start screen recording startRecording() { if checkRecording; then echo "A recording is already in progress." @@ -21,66 +21,113 @@ startRecording() { target="$2" - outputFile="recording_$(date +%Y-%m-%d_%H-%M-%S)" - outputPath="$outputDir/${outputFile}.mp4" - mkdir -p "$outputDir" - if [ "$target" == "screen" ]; then - monitor_info=$(hyprctl -j monitors | jq -r ".[] | select(.name == \"$3\")") - w=$(echo $monitor_info | jq -r '.width') - h=$(echo $monitor_info | jq -r '.height') - scale=$(echo $monitor_info | jq -r '.scale') - scaled_width=$(awk "BEGIN {print $w / $scale}") - scaled_height=$(awk "BEGIN {print $h / $scale}") - x=$(echo $monitor_info | jq -r '.x') - y=$(echo $monitor_info | jq -r '.y') + monitor_name="$3" + outputDir="$4" + elif [ "$target" == "region" ]; then + outputDir="$3" + else + echo "Usage: $0 start {screen | region} " + exit 1 + fi + + # Set a default output directory if not provided + outputDir="${outputDir:-$HOME/Videos}" + + # Expand ~ to $HOME if present in outputDir + outputDir="${outputDir/#\~/$HOME}" + + # Ensure output directory exists + if [ ! -d "$outputDir" ]; then + echo "Error: Output directory '$outputDir' does not exist." + exit 1 + fi + + # Generate output filename and path + outputFile="recording_$(date +%Y-%m-%d_%H-%M-%S).mp4" + outputPath="$outputDir/$outputFile" + + echo "Target: $target" + echo "Monitor: ${monitor_name:-N/A}" + echo "Output dir: $outputDir" + echo "Output file: $outputPath" + + # Start screen recording + if [ "$target" == "screen" ]; then + if [ -z "$monitor_name" ]; then + echo "Error: Monitor name is required for screen recording." + exit 1 + fi + + monitor_info=$(hyprctl -j monitors | jq -r ".[] | select(.name == \"$monitor_name\")") + if [ -z "$monitor_info" ]; then + echo "Error: Monitor '$monitor_name' not found." + exit 1 + fi + + w=$(echo "$monitor_info" | jq -r '.width') + h=$(echo "$monitor_info" | jq -r '.height') + scale=$(echo "$monitor_info" | jq -r '.scale') + scaled_width=$(awk "BEGIN { print $w / $scale }") + scaled_height=$(awk "BEGIN { print $h / $scale }") + x=$(echo "$monitor_info" | jq -r '.x') + y=$(echo "$monitor_info" | jq -r '.y') + wf-recorder $WF_RECORDER_OPTS --geometry "${x},${y} ${scaled_width}x${scaled_height}" --file "$outputPath" & elif [ "$target" == "region" ]; then wf-recorder $WF_RECORDER_OPTS --geometry "$(slurp)" --file "$outputPath" & - else - echo "Usage: $0 start {region|screen [screen_name]}" - exit 1 fi - disown "$(jobs -p | tail -n 1)" - echo "Recording started. Output will be saved to $outputPath" + disown "$(jobs -p | tail -n 1)" + echo "Recording started. Saving to $outputPath" + echo "$outputPath" > /tmp/last_recording_path } +# Function to stop screen recording stopRecording() { if ! checkRecording; then - echo "No recording is in progress." + echo "No recording in progress." exit 1 fi pkill -SIGINT -f wf-recorder + sleep 1 # Allow wf-recorder time to terminate before proceeding - recentFile=$(ls -t "$outputDir"/recording_*.mp4 | head -n 1) + outputPath=$(cat /tmp/last_recording_path 2>/dev/null) - notify-send "Recording stopped" "Your recording has been saved." \ + if [ -z "$outputPath" ] || [ ! -f "$outputPath" ]; then + notify-send "Recording stopped" "No recent recording found." \ + -i video-x-generic \ + -a "Screen Recorder" \ + -t 10000 + exit 1 + fi + + notify-send "Recording stopped" "Saved to: $outputPath" \ -i video-x-generic \ -a "Screen Recorder" \ -t 10000 \ - -u normal \ - --action="scriptAction:-xdg-open $outputDir=Directory" \ - --action="scriptAction:-xdg-open $recentFile=Play" + --action="scriptAction:-xdg-open $(dirname "$outputPath")=Open Directory" \ + --action="scriptAction:-xdg-open $outputPath=Play" } +# Handle script arguments case "$1" in -start) - startRecording "$@" - ;; -stop) - stopRecording - ;; -status) - if checkRecording; then - echo "recording" - else - echo "not recording" - fi - ;; -*) - echo "Usage: $0 {start [screen screen_name|region]|stop|status}" - exit 1 - ;; -esac + start) + startRecording "$@" + ;; + stop) + stopRecording + ;; + status) + if checkRecording; then + echo "recording" + else + echo "not recording" + fi + ;; + *) + echo "Usage: $0 {start [screen | region] | stop | status}" + exit 1 + ;; +esac \ No newline at end of file diff --git a/src/components/menus/dashboard/shortcuts/buttons/RecordingButton.tsx b/src/components/menus/dashboard/shortcuts/buttons/RecordingButton.tsx index 1f054dd..b48cc38 100644 --- a/src/components/menus/dashboard/shortcuts/buttons/RecordingButton.tsx +++ b/src/components/menus/dashboard/shortcuts/buttons/RecordingButton.tsx @@ -1,8 +1,8 @@ -import { bind, execAsync, Variable } from 'astal'; +import { bind, Variable } from 'astal'; import { App, Gdk, Gtk } from 'astal/gtk3'; import Menu from 'src/components/shared/Menu'; import MenuItem from 'src/components/shared/MenuItem'; -import { isRecording } from '../helpers'; +import { isRecording, getRecordingPath, executeCommand } from '../helpers'; import AstalHyprland from 'gi://AstalHyprland?version=0.1'; const hyprlandService = AstalHyprland.get_default(); @@ -10,44 +10,41 @@ const hyprlandService = AstalHyprland.get_default(); const MonitorListDropdown = (): JSX.Element => { const monitorList: Variable = Variable([]); - const monitorBinding = Variable.derive([bind(hyprlandService, 'monitors')], () => - monitorList.set(hyprlandService.get_monitors()), - ); + const monitorBinding = Variable.derive([bind(hyprlandService, 'monitors')], () => { + monitorList.set(hyprlandService.get_monitors()); + }); return ( monitorBinding.drop()} hexpand> - {bind(monitorList).as((monitors) => { - return monitors.map((monitor) => ( - { - const buttonClicked = event.get_button()[1]; + {bind(monitorList).as((monitors) => + monitors.map((monitor) => { + const sanitizedPath = getRecordingPath().replace(/"/g, '\\"'); - if (buttonClicked !== Gdk.BUTTON_PRIMARY) { - return; - } + return ( + { + if (event.get_button()[1] !== Gdk.BUTTON_PRIMARY) return; - App.get_window('dashboardmenu')?.set_visible(false); + App.get_window('dashboardmenu')?.set_visible(false); - execAsync(`${SRC_DIR}/scripts/screen_record.sh start screen ${monitor.name}`).catch((err) => - console.error(err), - ); - }} - /> - )); - })} + const command = `${SRC_DIR}/scripts/screen_record.sh start screen "${monitor.name}" "${sanitizedPath}"`; + executeCommand(command); + }} + /> + ); + }), + )} { - const buttonClicked = event.get_button()[1]; - - if (buttonClicked !== Gdk.BUTTON_PRIMARY) { - return; - } + if (event.get_button()[1] !== Gdk.BUTTON_PRIMARY) return; App.get_window('dashboardmenu')?.set_visible(false); - execAsync(`${SRC_DIR}/scripts/screen_record.sh start region`).catch((err) => console.error(err)); + const sanitizedPath = getRecordingPath().replace(/"/g, '\\"'); + const command = `${SRC_DIR}/scripts/screen_record.sh start region "${sanitizedPath}"`; + executeCommand(command); }} /> @@ -58,7 +55,7 @@ export const RecordingButton = (): JSX.Element => { return (