aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorTrung Lê <8@tle.id.au>2026-02-22 11:29:18 +1100
committerTrung Lê <8@tle.id.au>2026-02-22 11:29:18 +1100
commit9690617ae3855ab9f58e5bce0f55b4842b9832e5 (patch)
tree22fb5532cbaf4a6f0c4b1714f563aebf68b52918
parent442c8be65ac2ba1fdcbcfa8204adec5fecb2c474 (diff)
Prepare 1.0.0: fix memory leaks, use-after-free bugs, and code quality issues
- Version 6.0.0 → 1.0.0, C standard gnu17 → gnu23 - Fix desktop file path (was referencing parent directory) - global_shortcuts.c: fix systematic GVariant/GDBusProxy leaks, guint64-to-gchar* type mismatch, buffer overflow risk, missing malloc NULL checks, debug artifacts - gsr-window.c: fix notification timeout use-after-free, menu object leak in finalize, localtime NULL check, extract active_mode_to_string helper - gsr-stream-page.c, gsr-record-page.c, gsr-replay-page.c: fix timer source leak in finalize (use-after-free if destroyed while timer running) - gsr-shortcut-accel-dialog.c: fix Pango markup injection via shortcut titles - gsr-info.c: fix pclose UB, replace fprintf with g_warning - gsr-config.c: include errno in fopen failure warning - Remove unused include, stale Phase 5 comments
-rw-r--r--meson.build6
-rw-r--r--src/global_shortcuts.c91
-rw-r--r--src/gsr-config.c2
-rw-r--r--src/gsr-info.c4
-rw-r--r--src/gsr-record-page.c7
-rw-r--r--src/gsr-record-page.h2
-rw-r--r--src/gsr-replay-page.c6
-rw-r--r--src/gsr-replay-page.h2
-rw-r--r--src/gsr-shortcut-accel-dialog.c12
-rw-r--r--src/gsr-stream-page.c8
-rw-r--r--src/gsr-stream-page.h2
-rw-r--r--src/gsr-window.c57
12 files changed, 138 insertions, 61 deletions
diff --git a/meson.build b/meson.build
index c891547..15f285b 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
project('gpu-screen-recorder-adwaita', 'c',
- version : '6.0.0',
- default_options : ['warning_level=2', 'c_std=gnu17'],
+ version : '1.0.0',
+ default_options : ['warning_level=2', 'c_std=gnu23'],
)
add_project_arguments('-Wshadow', language : 'c')
@@ -57,7 +57,7 @@ executable('gpu-screen-recorder-adw',
)
install_data(
- files('../com.dec05eba.gpu_screen_recorder.desktop'),
+ files('com.dec05eba.gpu_screen_recorder.desktop'),
install_dir : join_paths(get_option('prefix'), get_option('datadir'), 'applications'),
)
install_subdir('icons/hicolor', install_dir : join_paths(get_option('prefix'), get_option('datadir'), 'icons'))
diff --git a/src/global_shortcuts.c b/src/global_shortcuts.c
index a7540f0..794de04 100644
--- a/src/global_shortcuts.c
+++ b/src/global_shortcuts.c
@@ -5,14 +5,9 @@
#include <sys/random.h>
#include <gio/gio.h>
-/* TODO: Remove G_DBUS_CALL_FLAGS_NO_AUTO_START and G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START? also in gpu screen recorder equivalent */
-/* TODO: More error handling and clean up resources after done */
-/* TODO: Use GArray instead of GVariant where possible */
-
static bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) {
- /* TODO: Use other functions on other platforms than linux */
if(getrandom(buffer, buffer_size, 0) < buffer_size) {
- fprintf(stderr, "gsr error: generate_random_characters: failed to get random bytes, error: %s\n", strerror(errno));
+ g_warning("generate_random_characters: failed to get random bytes: %s", strerror(errno));
return false;
}
@@ -35,11 +30,11 @@ static void handle_shortcuts_data(GVariant *shortcuts, gsr_shortcut_callback cal
GVariant *shortcut_values = NULL;
g_variant_get_child(shortcuts, i, "(s@a{sv})", &shortcut_id, &shortcut_values);
- if(!shortcut_id || !shortcut_values)
+ if(!shortcut_id || !shortcut_values) {
+ g_free(shortcut_id);
+ if(shortcut_values) g_variant_unref(shortcut_values);
continue;
-
- // gchar *description = NULL;
- // g_variant_lookup(shortcut_values, "description", "s", &description);
+ }
gchar *trigger_description = NULL;
g_variant_lookup(shortcut_values, "trigger_description", "s", &trigger_description);
@@ -48,6 +43,10 @@ static void handle_shortcuts_data(GVariant *shortcuts, gsr_shortcut_callback cal
shortcut.id = shortcut_id;
shortcut.trigger_description = trigger_description ? trigger_description : "";
callback(shortcut, userdata);
+
+ g_free(shortcut_id);
+ g_free(trigger_description);
+ g_variant_unref(shortcut_values);
}
}
@@ -67,14 +66,20 @@ static void dbus_signal_list_bind(GDBusProxy *proxy, gchar *sender_name, gchar *
GVariant *results = NULL;
g_variant_get(parameters, "(u@a{sv})", &response, &results);
- if(response != 0 || !results)
+ if(response != 0 || !results) {
+ if(results) g_variant_unref(results);
goto done;
+ }
GVariant *shortcuts = g_variant_lookup_value(results, "shortcuts", G_VARIANT_TYPE("a(sa{sv})"));
- if(!shortcuts)
+ if(!shortcuts) {
+ g_variant_unref(results);
goto done;
+ }
handle_shortcuts_data(shortcuts, userdata->callback, userdata->userdata);
+ g_variant_unref(shortcuts);
+ g_variant_unref(results);
done:
free(userdata);
@@ -107,12 +112,16 @@ static void signal_callback(GDBusConnection *connection,
if(strcmp(signal_name, "Deactivated") == 0) {
gchar *session_handle = NULL;
gchar *shortcut_id = NULL;
- gchar *timestamp = NULL;
+ guint64 timestamp = 0;
GVariant *options = NULL;
g_variant_get(parameters, "(ost@a{sv})", &session_handle, &shortcut_id, &timestamp, &options);
if(session_handle && shortcut_id && strcmp(session_handle, cu->self->session_handle) == 0)
cu->deactivated_callback(shortcut_id, cu->userdata);
+
+ g_free(session_handle);
+ g_free(shortcut_id);
+ if(options) g_variant_unref(options);
} else if(strcmp(signal_name, "ShortcutsChanged") == 0) {
gchar *session_handle = NULL;
GVariant *shortcuts = NULL;
@@ -120,6 +129,9 @@ static void signal_callback(GDBusConnection *connection,
if(session_handle && shortcuts && strcmp(session_handle, cu->self->session_handle) == 0)
handle_shortcuts_data(shortcuts, cu->shortcut_changed_callback, cu->userdata);
+
+ g_free(session_handle);
+ if(shortcuts) g_variant_unref(shortcuts);
}
}
@@ -141,6 +153,7 @@ static void dbus_signal_create_session(GDBusProxy *proxy, gchar *sender_name, gc
if(response != 0 || !results) {
cu->callback(false, cu->userdata);
+ if(results) g_variant_unref(results);
goto done;
}
@@ -149,18 +162,22 @@ static void dbus_signal_create_session(GDBusProxy *proxy, gchar *sender_name, gc
cu->self->session_handle = strdup(session_handle);
cu->self->session_created = true;
cu->callback(true, cu->userdata);
+ g_free(session_handle);
+ } else {
+ cu->callback(false, cu->userdata);
}
+ g_variant_unref(results);
done:
free(cu);
}
static bool gsr_global_shortcuts_create_session(gsr_global_shortcuts *self, gsr_init_callback callback, void *userdata) {
- char handle_token[64];
+ char handle_token[128];
gsr_dbus_portal_get_unique_handle_token(self, handle_token, sizeof(handle_token));
- char session_handle_token[64];
- snprintf(session_handle_token, sizeof(session_handle_token), "gpu_screen_recorder_adwaita");
+ char session_handle_token[128];
+ snprintf(session_handle_token, sizeof(session_handle_token), "gpu_screen_recorder_adwaita_%s", self->random_str);
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
@@ -173,16 +190,21 @@ static bool gsr_global_shortcuts_create_session(gsr_global_shortcuts *self, gsr_
if(ret) {
const gchar *val = NULL;
g_variant_get(ret, "(&o)", &val);
- if(!val)
+ if(!val) {
+ g_variant_unref(ret);
return false;
- //g_variant_unref(ret);
+ }
GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.freedesktop.portal.Desktop", val, "org.freedesktop.portal.Request", NULL, NULL);
+ g_variant_unref(ret);
if(!proxy)
return false;
- //g_object_unref(proxy);
signal_create_session_userdata *cu = malloc(sizeof(signal_create_session_userdata));
+ if(!cu) {
+ g_object_unref(proxy);
+ return false;
+ }
cu->self = self;
cu->callback = callback;
cu->userdata = userdata;
@@ -198,13 +220,13 @@ bool gsr_global_shortcuts_init(gsr_global_shortcuts *self, gsr_init_callback cal
self->random_str[DBUS_RANDOM_STR_SIZE] = '\0';
if(!generate_random_characters(self->random_str, DBUS_RANDOM_STR_SIZE, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62)) {
- fprintf(stderr, "gsr error: gsr_global_shortcuts_init: failed to generate random string\n");
+ g_warning("gsr_global_shortcuts_init: failed to generate random string");
return false;
}
self->gdbus_con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
if(!self->gdbus_con) {
- fprintf(stderr, "gsr error: gsr_global_shortcuts_init: g_bus_get_sync failed\n");
+ g_warning("gsr_global_shortcuts_init: g_bus_get_sync failed");
return false;
}
@@ -218,8 +240,7 @@ bool gsr_global_shortcuts_init(gsr_global_shortcuts *self, gsr_init_callback cal
void gsr_global_shortcuts_deinit(gsr_global_shortcuts *self) {
if(self->gdbus_con) {
- /* TODO: Re-add this. Right now it causes errors as the connection is already closed, but checking if it's already closed here has no effect */
- //g_dbus_connection_close(self->gdbus_con, NULL, NULL, NULL);
+ g_object_unref(self->gdbus_con);
self->gdbus_con = NULL;
}
@@ -233,7 +254,7 @@ bool gsr_global_shortcuts_list_shortcuts(gsr_global_shortcuts *self, gsr_shortcu
if(!self->session_created)
return false;
- char handle_token[64];
+ char handle_token[128];
gsr_dbus_portal_get_unique_handle_token(self, handle_token, sizeof(handle_token));
GVariant *session_handle_obj = g_variant_new_object_path(self->session_handle);
@@ -249,15 +270,21 @@ bool gsr_global_shortcuts_list_shortcuts(gsr_global_shortcuts *self, gsr_shortcu
if(ret) {
const gchar *val = NULL;
g_variant_get(ret, "(&o)", &val);
- if(!val)
+ if(!val) {
+ g_variant_unref(ret);
return false;
+ }
GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.freedesktop.portal.Desktop", val, "org.freedesktop.portal.Request", NULL, NULL);
+ g_variant_unref(ret);
if(!proxy)
return false;
- //g_object_unref(proxy);
signal_list_bind_userdata *cu = malloc(sizeof(signal_list_bind_userdata));
+ if(!cu) {
+ g_object_unref(proxy);
+ return false;
+ }
cu->self = self;
cu->callback = callback;
cu->userdata = userdata;
@@ -272,7 +299,7 @@ bool gsr_global_shortcuts_bind_shortcuts(gsr_global_shortcuts *self, const gsr_b
if(!self->session_created)
return false;
- char handle_token[64];
+ char handle_token[128];
gsr_dbus_portal_get_unique_handle_token(self, handle_token, sizeof(handle_token));
GVariant *session_handle_obj = g_variant_new_object_path(self->session_handle);
@@ -303,15 +330,21 @@ bool gsr_global_shortcuts_bind_shortcuts(gsr_global_shortcuts *self, const gsr_b
if(ret) {
const gchar *val = NULL;
g_variant_get(ret, "(&o)", &val);
- if(!val)
+ if(!val) {
+ g_variant_unref(ret);
return false;
+ }
GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.freedesktop.portal.Desktop", val, "org.freedesktop.portal.Request", NULL, NULL);
+ g_variant_unref(ret);
if(!proxy)
return false;
- //g_object_unref(proxy);
signal_list_bind_userdata *cu = malloc(sizeof(signal_list_bind_userdata));
+ if(!cu) {
+ g_object_unref(proxy);
+ return false;
+ }
cu->self = self;
cu->callback = callback;
cu->userdata = userdata;
diff --git a/src/gsr-config.c b/src/gsr-config.c
index 21405ea..e9de887 100644
--- a/src/gsr-config.c
+++ b/src/gsr-config.c
@@ -389,7 +389,7 @@ gsr_config_save(const GsrConfig *config)
FILE *file = fopen(config_path, "wb");
if (!file) {
- g_warning("Failed to create config file: %s", config_path);
+ g_warning("Failed to create config file: %s: %s", config_path, g_strerror(errno));
g_free(config_path);
return;
}
diff --git a/src/gsr-info.c b/src/gsr-info.c
index 04da95c..4187aed 100644
--- a/src/gsr-info.c
+++ b/src/gsr-info.c
@@ -32,7 +32,7 @@ read_command_output(const char *cmd, int *exit_code)
int status = pclose(f);
if (exit_code) {
- if (WIFEXITED(status))
+ if (status >= 0 && WIFEXITED(status))
*exit_code = WEXITSTATUS(status);
else
*exit_code = -1;
@@ -223,7 +223,7 @@ gsr_info_load(GsrInfo *info)
int exit_code = -1;
char *output = read_command_output("gpu-screen-recorder --info", &exit_code);
if (!output) {
- fprintf(stderr, "error: 'gpu-screen-recorder --info' failed to run\n");
+ g_warning("'gpu-screen-recorder --info' failed to run");
return GSR_INFO_EXIT_FAILED_TO_RUN;
}
diff --git a/src/gsr-record-page.c b/src/gsr-record-page.c
index a9575c4..de4781e 100644
--- a/src/gsr-record-page.c
+++ b/src/gsr-record-page.c
@@ -3,7 +3,6 @@
#include "gsr-shortcut-accel-dialog.h"
#endif
#include "gsr-window.h"
-#include <glib/gi18n.h>
#include <signal.h>
#include <time.h>
@@ -503,6 +502,12 @@ static void
gsr_record_page_finalize(GObject *object)
{
GsrRecordPage *self = GSR_RECORD_PAGE(object);
+
+ if (self->timer_source_id) {
+ g_source_remove(self->timer_source_id);
+ self->timer_source_id = 0;
+ }
+
g_free(self->save_directory);
#ifdef HAVE_X11
g_free(self->x11_start_stop_accel);
diff --git a/src/gsr-record-page.h b/src/gsr-record-page.h
index 94acaf4..ee5f4f5 100644
--- a/src/gsr-record-page.h
+++ b/src/gsr-record-page.h
@@ -15,7 +15,7 @@ void gsr_record_page_apply_config (GsrRecordPage *self,
void gsr_record_page_read_config (GsrRecordPage *self,
GsrConfig *config);
-/* Process management API (Phase 5) */
+/* Process management API */
void gsr_record_page_set_active (GsrRecordPage *self,
gboolean active);
void gsr_record_page_set_paused (GsrRecordPage *self,
diff --git a/src/gsr-replay-page.c b/src/gsr-replay-page.c
index 67c7c9a..4dcbc81 100644
--- a/src/gsr-replay-page.c
+++ b/src/gsr-replay-page.c
@@ -490,6 +490,12 @@ static void
gsr_replay_page_finalize(GObject *object)
{
GsrReplayPage *self = GSR_REPLAY_PAGE(object);
+
+ if (self->timer_source_id) {
+ g_source_remove(self->timer_source_id);
+ self->timer_source_id = 0;
+ }
+
g_free(self->save_directory);
#ifdef HAVE_X11
g_free(self->x11_start_stop_accel);
diff --git a/src/gsr-replay-page.h b/src/gsr-replay-page.h
index 697cba2..d703d93 100644
--- a/src/gsr-replay-page.h
+++ b/src/gsr-replay-page.h
@@ -15,7 +15,7 @@ void gsr_replay_page_apply_config (GsrReplayPage *self,
void gsr_replay_page_read_config (GsrReplayPage *self,
GsrConfig *config);
-/* Process management API (Phase 5) */
+/* Process management API */
void gsr_replay_page_set_active (GsrReplayPage *self,
gboolean active);
void gsr_replay_page_update_timer (GsrReplayPage *self,
diff --git a/src/gsr-shortcut-accel-dialog.c b/src/gsr-shortcut-accel-dialog.c
index bf5b0e9..37e90c6 100644
--- a/src/gsr-shortcut-accel-dialog.c
+++ b/src/gsr-shortcut-accel-dialog.c
@@ -280,9 +280,11 @@ build_dialog_ui(GsrShortcutAccelDialog *self)
gtk_widget_add_css_class(GTK_WIDGET(keyboard_icon), "dim-label");
gtk_box_append(capture_box, GTK_WIDGET(keyboard_icon));
+ char *escaped_title = g_markup_escape_text(
+ self->shortcut_title ? self->shortcut_title : "Shortcut", -1);
char *label_text = g_strdup_printf(
- "Enter new shortcut for\n<b>%s</b>",
- self->shortcut_title ? self->shortcut_title : "Shortcut");
+ "Enter new shortcut for\n<b>%s</b>", escaped_title);
+ g_free(escaped_title);
self->capture_label = GTK_LABEL(gtk_label_new(NULL));
gtk_label_set_markup(self->capture_label, label_text);
gtk_label_set_justify(self->capture_label, GTK_JUSTIFY_CENTER);
@@ -309,9 +311,11 @@ build_dialog_ui(GsrShortcutAccelDialog *self)
gtk_widget_set_margin_bottom(GTK_WIDGET(display_box), 24);
GtkLabel *display_title = GTK_LABEL(gtk_label_new(NULL));
+ char *escaped_display_title = g_markup_escape_text(
+ self->shortcut_title ? self->shortcut_title : "Shortcut", -1);
char *display_text = g_strdup_printf(
- "Shortcut for <b>%s</b>",
- self->shortcut_title ? self->shortcut_title : "Shortcut");
+ "Shortcut for <b>%s</b>", escaped_display_title);
+ g_free(escaped_display_title);
gtk_label_set_markup(display_title, display_text);
gtk_label_set_justify(display_title, GTK_JUSTIFY_CENTER);
gtk_label_set_wrap(display_title, TRUE);
diff --git a/src/gsr-stream-page.c b/src/gsr-stream-page.c
index fceef37..e3ec495 100644
--- a/src/gsr-stream-page.c
+++ b/src/gsr-stream-page.c
@@ -413,8 +413,14 @@ build_status_group(GsrStreamPage *self)
static void
gsr_stream_page_finalize(GObject *object)
{
-#ifdef HAVE_X11
GsrStreamPage *self = GSR_STREAM_PAGE(object);
+
+ if (self->timer_source_id) {
+ g_source_remove(self->timer_source_id);
+ self->timer_source_id = 0;
+ }
+
+#ifdef HAVE_X11
g_free(self->x11_start_stop_accel);
#endif
G_OBJECT_CLASS(gsr_stream_page_parent_class)->finalize(object);
diff --git a/src/gsr-stream-page.h b/src/gsr-stream-page.h
index af63bc6..798e2f4 100644
--- a/src/gsr-stream-page.h
+++ b/src/gsr-stream-page.h
@@ -15,7 +15,7 @@ void gsr_stream_page_apply_config (GsrStreamPage *self,
void gsr_stream_page_read_config (GsrStreamPage *self,
GsrConfig *config);
-/* Process management API (Phase 5) */
+/* Process management API */
void gsr_stream_page_set_active (GsrStreamPage *self,
gboolean active);
void gsr_stream_page_update_timer (GsrStreamPage *self,
diff --git a/src/gsr-window.c b/src/gsr-window.c
index 19b5f1a..28091f9 100644
--- a/src/gsr-window.c
+++ b/src/gsr-window.c
@@ -66,6 +66,7 @@ struct _GsrWindow {
/* ── Desktop notifications ─── */
gboolean showing_notification;
+ guint notification_timeout_id; /* auto-withdraw timer */
gboolean is_kde; /* KDE workaround */
/* ── Startup error state ─── */
@@ -80,6 +81,19 @@ static void handle_child_death(GsrWindow *self, int exit_status);
static void send_notification(GsrWindow *self, const char *title,
const char *body, GNotificationPriority priority);
+/* ── Mode-to-string helper ───────────────────────────────────────── */
+
+static const char *
+active_mode_to_string(GsrActiveMode mode)
+{
+ switch (mode) {
+ case GSR_ACTIVE_MODE_STREAM: return "streaming";
+ case GSR_ACTIVE_MODE_RECORD: return "recording";
+ case GSR_ACTIVE_MODE_REPLAY: return "replay";
+ default: return "unknown";
+ }
+}
+
/* ── Desktop notification helpers ────────────────────────────────── */
/**
@@ -91,6 +105,8 @@ on_notification_withdraw(gpointer user_data)
{
GsrWindow *self = GSR_WINDOW(user_data);
+ self->notification_timeout_id = 0; /* source is being removed */
+
if (self->showing_notification) {
GtkApplication *app = GTK_APPLICATION(
gtk_window_get_application(GTK_WINDOW(self)));
@@ -146,8 +162,12 @@ send_notification(GsrWindow *self, const char *title,
g_object_unref(notif);
/* Auto-withdraw after timeout */
+ if (self->notification_timeout_id) {
+ g_source_remove(self->notification_timeout_id);
+ self->notification_timeout_id = 0;
+ }
guint timeout_ms = (effective >= G_NOTIFICATION_PRIORITY_URGENT) ? 10000 : 3000;
- g_timeout_add(timeout_ms, on_notification_withdraw, self);
+ self->notification_timeout_id = g_timeout_add(timeout_ms, on_notification_withdraw, self);
/* ── In-app toast ── */
AdwToast *toast = adw_toast_new(body);
@@ -211,6 +231,9 @@ build_record_filename(const char *dir, const char *container_display)
{
time_t now = time(NULL);
struct tm *tm = localtime(&now);
+ if (!tm) {
+ return g_strdup_printf("%s/Video.%s", dir, container_display);
+ }
char date_buf[64];
strftime(date_buf, sizeof(date_buf), "%Y-%m-%d_%H-%M-%S", tm);
return g_strdup_printf("%s/Video_%s.%s", dir, date_buf, container_display);
@@ -540,9 +563,7 @@ handle_child_death(GsrWindow *self, int exit_status)
g_free(msg);
}
} else if (gsr_config_page_get_notify_stopped(self->config_page)) {
- const char *mode_str =
- mode == GSR_ACTIVE_MODE_STREAM ? "streaming" :
- mode == GSR_ACTIVE_MODE_REPLAY ? "replay" : "recording";
+ const char *mode_str = active_mode_to_string(mode);
char *msg = g_strdup_printf("Stopped %s", mode_str);
send_notification(self, "GPU Screen Recorder", msg,
G_NOTIFICATION_PRIORITY_NORMAL);
@@ -638,7 +659,11 @@ on_close_request(GtkWindow *window, gpointer user_data G_GNUC_UNUSED)
self->hotkeys = NULL;
}
- /* Withdraw any pending desktop notification */
+ /* Withdraw any pending desktop notification and cancel its timer */
+ if (self->notification_timeout_id) {
+ g_source_remove(self->notification_timeout_id);
+ self->notification_timeout_id = 0;
+ }
if (self->showing_notification) {
GtkApplication *app = GTK_APPLICATION(
gtk_window_get_application(GTK_WINDOW(self)));
@@ -982,12 +1007,19 @@ gsr_window_finalize(GObject *object)
self->poll_timer_id = 0;
}
+ if (self->notification_timeout_id) {
+ g_source_remove(self->notification_timeout_id);
+ self->notification_timeout_id = 0;
+ }
+
if (self->hotkeys) {
gsr_hotkeys_free(self->hotkeys);
self->hotkeys = NULL;
}
g_free(self->record_filename);
+ g_clear_object(&self->primary_menu);
+ g_clear_object(&self->view_section);
gsr_config_clear(&self->config);
gsr_info_clear(&self->info);
G_OBJECT_CLASS(gsr_window_parent_class)->finalize(object);
@@ -1046,10 +1078,7 @@ gsr_window_start_process(GsrWindow *self, GsrActiveMode mode)
g_ptr_array_unref(args);
if (!ok) {
- const char *mode_str =
- mode == GSR_ACTIVE_MODE_STREAM ? "streaming" :
- mode == GSR_ACTIVE_MODE_RECORD ? "recording" :
- mode == GSR_ACTIVE_MODE_REPLAY ? "replay" : "unknown";
+ const char *mode_str = active_mode_to_string(mode);
char *msg = g_strdup_printf("Failed to start %s (failed to fork)", mode_str);
send_notification(self, "GPU Screen Recorder", msg,
G_NOTIFICATION_PRIORITY_URGENT);
@@ -1065,10 +1094,7 @@ gsr_window_start_process(GsrWindow *self, GsrActiveMode mode)
/* Show "started" notification */
if (gsr_config_page_get_notify_started(self->config_page)) {
- const char *mode_str =
- mode == GSR_ACTIVE_MODE_STREAM ? "streaming" :
- mode == GSR_ACTIVE_MODE_RECORD ? "recording" :
- mode == GSR_ACTIVE_MODE_REPLAY ? "replay" : "unknown";
+ const char *mode_str = active_mode_to_string(mode);
char *msg = g_strdup_printf("Started %s", mode_str);
send_notification(self, "GPU Screen Recorder", msg,
G_NOTIFICATION_PRIORITY_NORMAL);
@@ -1112,10 +1138,7 @@ gsr_window_stop_process(GsrWindow *self, gboolean *already_dead)
g_free(msg);
}
} else if (gsr_config_page_get_notify_stopped(self->config_page)) {
- const char *mode_str =
- mode == GSR_ACTIVE_MODE_STREAM ? "streaming" :
- mode == GSR_ACTIVE_MODE_RECORD ? "recording" :
- mode == GSR_ACTIVE_MODE_REPLAY ? "replay" : "unknown";
+ const char *mode_str = active_mode_to_string(mode);
char *msg = g_strdup_printf("Stopped %s", mode_str);
send_notification(self, "GPU Screen Recorder", msg,
G_NOTIFICATION_PRIORITY_NORMAL);