aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTrung Lê <8@tle.id.au>2026-06-10 11:46:52 +1000
committerGitHub <noreply@github.com>2026-06-10 11:46:52 +1000
commitf51ba1bf936ae5085ba604b76bbca18e79554dfe (patch)
tree8584adb5c971446eedd3959ac6fdaea86953e74d
parent3d6435e45cc4b760afa10ddf96a1c542817c0fe1 (diff)
parenta646f86c232ecbc0d1875ef2b83a165760127699 (diff)
Merge pull request #8 from runlevel5/feature/notification-open-folder
feat: click the saved-recording notification to open its folder
-rw-r--r--src/gsr-config.c7
-rw-r--r--src/gsr-record-page.c5
-rw-r--r--src/gsr-replay-page.c5
-rw-r--r--src/gsr-window.c31
-rw-r--r--src/main.c42
5 files changed, 73 insertions, 17 deletions
diff --git a/src/gsr-config.c b/src/gsr-config.c
index af8b980..8b9bd4b 100644
--- a/src/gsr-config.c
+++ b/src/gsr-config.c
@@ -68,9 +68,10 @@ gsr_config_get_dir(void)
char *
gsr_config_get_videos_dir(void)
{
- const char *vdir = g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS);
- if (vdir)
- return g_strdup(vdir);
+ /* Prefer the standard XDG videos directory, fall back to ~/Videos. */
+ const char *xdg_videos = g_getenv("XDG_VIDEOS_DIR");
+ if (xdg_videos && *xdg_videos)
+ return g_strdup(xdg_videos);
char *home = get_home_dir();
char *result = g_build_filename(home, "Videos", NULL);
g_free(home);
diff --git a/src/gsr-record-page.c b/src/gsr-record-page.c
index 1a34c0a..24247da 100644
--- a/src/gsr-record-page.c
+++ b/src/gsr-record-page.c
@@ -69,10 +69,7 @@ G_DEFINE_FINAL_TYPE(GsrRecordPage, gsr_record_page, ADW_TYPE_PREFERENCES_PAGE)
static char *
get_default_videos_dir(void)
{
- const char *vdir = g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS);
- if (vdir)
- return g_strdup(vdir);
- return g_build_filename(g_get_home_dir(), "Videos", NULL);
+ return gsr_config_get_videos_dir();
}
/* ── Timer helpers ───────────────────────────────────────────────── */
diff --git a/src/gsr-replay-page.c b/src/gsr-replay-page.c
index 0a354be..3c516a8 100644
--- a/src/gsr-replay-page.c
+++ b/src/gsr-replay-page.c
@@ -67,10 +67,7 @@ G_DEFINE_FINAL_TYPE(GsrReplayPage, gsr_replay_page, ADW_TYPE_PREFERENCES_PAGE)
static char *
get_default_videos_dir(void)
{
- const char *vdir = g_get_user_special_dir(G_USER_DIRECTORY_VIDEOS);
- if (vdir)
- return g_strdup(vdir);
- return g_build_filename(g_get_home_dir(), "Videos", NULL);
+ return gsr_config_get_videos_dir();
}
/* ── Timer helpers ───────────────────────────────────────────────── */
diff --git a/src/gsr-window.c b/src/gsr-window.c
index fe7a55e..6620b59 100644
--- a/src/gsr-window.c
+++ b/src/gsr-window.c
@@ -133,8 +133,9 @@ on_notification_withdraw(gpointer user_data)
* case so the user still sees them.
*/
static void
-send_notification(GsrWindow *self, const char *title,
- const char *body, GNotificationPriority priority)
+send_notification_full(GsrWindow *self, const char *title,
+ const char *body, GNotificationPriority priority,
+ const char *open_file_path)
{
GtkApplication *app = GTK_APPLICATION(
gtk_window_get_application(GTK_WINDOW(self)));
@@ -153,6 +154,11 @@ send_notification(GsrWindow *self, const char *title,
g_notification_set_priority(notif, effective);
+ /* Clicking the notification reveals the saved file in the file manager */
+ if (open_file_path && *open_file_path)
+ g_notification_set_default_action_and_target(notif,
+ "app.open-folder", "s", open_file_path);
+
/* Cancel any pending withdrawal timer, then send */
if (self->showing_notification) {
g_application_withdraw_notification(G_APPLICATION(app),
@@ -171,9 +177,22 @@ send_notification(GsrWindow *self, const char *title,
/* ── In-app toast ── */
AdwToast *toast = adw_toast_new(body);
adw_toast_set_timeout(toast, (effective >= G_NOTIFICATION_PRIORITY_URGENT) ? 10 : 3);
+ if (open_file_path && *open_file_path) {
+ adw_toast_set_button_label(toast, "Open Folder");
+ adw_toast_set_action_name(toast, "app.open-folder");
+ adw_toast_set_action_target_value(toast,
+ g_variant_new_string(open_file_path));
+ }
adw_toast_overlay_add_toast(self->toast_overlay, toast);
}
+static void
+send_notification(GsrWindow *self, const char *title,
+ const char *body, GNotificationPriority priority)
+{
+ send_notification_full(self, title, body, priority, NULL);
+}
+
/* ── Container compatibility fix ─────────────────────────────────── */
static const char *
@@ -553,8 +572,8 @@ handle_child_death(GsrWindow *self, int exit_status)
if (gsr_config_page_get_notify_saved(self->config_page)) {
char *msg = g_strdup_printf("Recording saved to %s",
self->record_filename);
- send_notification(self, "GPU Screen Recorder", msg,
- G_NOTIFICATION_PRIORITY_NORMAL);
+ send_notification_full(self, "GPU Screen Recorder", msg,
+ G_NOTIFICATION_PRIORITY_NORMAL, self->record_filename);
g_free(msg);
}
} else if (gsr_config_page_get_notify_stopped(self->config_page)) {
@@ -1114,8 +1133,8 @@ gsr_window_stop_process(GsrWindow *self, gboolean *already_dead)
if (gsr_config_page_get_notify_saved(self->config_page)) {
char *msg = g_strdup_printf("Recording saved to %s",
self->record_filename);
- send_notification(self, "GPU Screen Recorder", msg,
- G_NOTIFICATION_PRIORITY_NORMAL);
+ send_notification_full(self, "GPU Screen Recorder", msg,
+ G_NOTIFICATION_PRIORITY_NORMAL, self->record_filename);
g_free(msg);
}
} else if (gsr_config_page_get_notify_stopped(self->config_page)) {
diff --git a/src/main.c b/src/main.c
index 02487c1..770436c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -76,6 +76,46 @@ on_shortcuts_action(GSimpleAction *action,
adw_dialog_present(ADW_DIALOG(dialog), GTK_WIDGET(win));
}
+/* ── Open containing folder (from "Recording saved" notification) ──── */
+
+static void
+on_open_folder_finished(GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ (void)user_data;
+
+ GtkFileLauncher *launcher = GTK_FILE_LAUNCHER(source);
+ GError *error = NULL;
+ if (!gtk_file_launcher_open_containing_folder_finish(launcher, result,
+ &error)) {
+ /* Cancellation is not an error worth reporting. */
+ if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning("Failed to open containing folder: %s", error->message);
+ g_clear_error(&error);
+ }
+}
+
+static void
+on_open_folder_action(GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
+{
+ (void)action;
+
+ GtkApplication *app = GTK_APPLICATION(user_data);
+ const char *path = parameter ? g_variant_get_string(parameter, NULL) : NULL;
+ if (!path || !*path)
+ return;
+
+ GtkWindow *win = gtk_application_get_active_window(app);
+
+ g_autoptr(GFile) file = g_file_new_for_path(path);
+ g_autoptr(GtkFileLauncher) launcher = gtk_file_launcher_new(file);
+ gtk_file_launcher_open_containing_folder(launcher, win, NULL,
+ on_open_folder_finished, NULL);
+}
+
/* ── Application activate ────────────────────────────────────────── */
static void
@@ -122,6 +162,8 @@ main(int argc, char *argv[])
static const GActionEntry app_actions[] = {
{ .name = "shortcuts", .activate = on_shortcuts_action },
{ .name = "about", .activate = on_about_action },
+ { .name = "open-folder", .activate = on_open_folder_action,
+ .parameter_type = "s" },
};
g_action_map_add_action_entries(G_ACTION_MAP(app),
app_actions, G_N_ELEMENTS(app_actions), app);