aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/gobject-linter.yml44
-rw-r--r--gobject-linter.toml32
-rw-r--r--src/global_shortcuts.c19
-rw-r--r--src/global_shortcuts.h5
-rw-r--r--src/gsr-config-page.c31
-rw-r--r--src/gsr-config-page.h3
-rw-r--r--src/gsr-config.c10
-rw-r--r--src/gsr-config.h3
-rw-r--r--src/gsr-hotkeys.c14
-rw-r--r--src/gsr-info.c26
-rw-r--r--src/gsr-info.h3
-rw-r--r--src/gsr-record-page.c34
-rw-r--r--src/gsr-record-page.h3
-rw-r--r--src/gsr-replay-page.c34
-rw-r--r--src/gsr-replay-page.h3
-rw-r--r--src/gsr-shortcut-accel-dialog.c3
-rw-r--r--src/gsr-stream-page.c38
-rw-r--r--src/gsr-stream-page.h3
-rw-r--r--src/gsr-window.c75
-rw-r--r--src/gsr-x11-hotkeys.c4
-rw-r--r--src/gsr-x11-window-picker.c6
21 files changed, 209 insertions, 184 deletions
diff --git a/.github/workflows/gobject-linter.yml b/.github/workflows/gobject-linter.yml
new file mode 100644
index 0000000..403871f
--- /dev/null
+++ b/.github/workflows/gobject-linter.yml
@@ -0,0 +1,44 @@
+name: GObject Lint
+
+# Static analysis of the GObject/GLib C code with gobject-linter.
+# Rules and severities are configured in ./gobject-linter.toml.
+# This is a blocking gate: the "Lint (blocking)" step fails the build on any
+# error-level finding. use_auto_cleanup is configured as a warning and does not
+# block (it mis-fires on floating GVariant references; see gobject-linter.toml).
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ gobject-linter:
+ runs-on: ubuntu-latest
+ container:
+ image: ghcr.io/bilelmoussaoui/gobject-linter:latest
+ permissions:
+ contents: read
+ security-events: write
+ steps:
+ - uses: actions/checkout@v4
+
+ # Produce SARIF for the Security tab and inline PR annotations.
+ # Never fails the job — the blocking gate below decides pass/fail.
+ - name: Analyze (SARIF)
+ run: gobject-linter . --format sarif > gobject-linter.sarif || true
+
+ - name: Upload SARIF
+ uses: github/codeql-action/upload-sarif@v3
+ # Pull requests from forks get a read-only token and cannot upload to
+ # code scanning; don't let that fail the run.
+ continue-on-error: true
+ with:
+ sarif_file: gobject-linter.sarif
+ category: gobject-linter
+
+ # Blocking gate: exits non-zero on any error-level finding.
+ - name: Lint (blocking)
+ run: gobject-linter .
diff --git a/gobject-linter.toml b/gobject-linter.toml
new file mode 100644
index 0000000..5fe6bb6
--- /dev/null
+++ b/gobject-linter.toml
@@ -0,0 +1,32 @@
+# gobject-linter configuration
+# https://github.com/bilelmoussaoui/gobject-linter
+#
+# Run locally with: gobject-linter .
+# Auto-fix: gobject-linter . --fix
+#
+# CI runs this as a blocking gate (see .github/workflows/gobject-linter.yml):
+# every enabled rule is a hard error, so the build fails on any regression.
+
+# libadwaita >= 1.8 pulls in GLib >= 2.80; rules needing a newer GLib are skipped.
+min_glib_version = "2.80"
+
+# Treat every enabled, non-opt-in rule as an error so CI fails on new findings.
+default_level = "error"
+
+# This project deliberately uses the standard C scalar types (int/char/…) rather
+# than the GLib aliases (gint/gchar/…). The alias style is not enforced.
+[rules.type_style]
+level = "ignore"
+
+# The application is not yet internationalized (no gettext domain). Re-enable
+# once translation infrastructure is in place.
+[rules.untranslated_string]
+level = "ignore"
+
+# Advisory only: this rule mis-fires on floating GVariant references (consumed by
+# g_variant_new_tuple/builders) and on objects intentionally kept alive past the
+# function (e.g. the GlobalShortcuts GDBusProxy). Converting those to g_autoptr
+# would over-unref and crash, so it reports rather than blocks. Convert the safe
+# g_autofree/g_autoptr sites manually over time.
+[rules.use_auto_cleanup]
+level = "warn"
diff --git a/src/global_shortcuts.c b/src/global_shortcuts.c
index 794de04..4cbf39d 100644
--- a/src/global_shortcuts.c
+++ b/src/global_shortcuts.c
@@ -1,9 +1,11 @@
#include "global_shortcuts.h"
-#include <stdio.h>
+
#include <errno.h>
+#include <stdio.h>
#include <string.h>
-#include <sys/random.h>
+
#include <gio/gio.h>
+#include <sys/random.h>
static bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) {
if(getrandom(buffer, buffer_size, 0) < buffer_size) {
@@ -109,25 +111,25 @@ static void signal_callback(GDBusConnection *connection,
signal_userdata *cu = userdata;
/* Button released */
- if(strcmp(signal_name, "Deactivated") == 0) {
+ if(g_strcmp0(signal_name, "Deactivated") == 0) {
gchar *session_handle = NULL;
gchar *shortcut_id = 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)
+ if(session_handle && shortcut_id && g_strcmp0(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) {
+ } else if(g_strcmp0(signal_name, "ShortcutsChanged") == 0) {
gchar *session_handle = NULL;
GVariant *shortcuts = NULL;
g_variant_get(parameters, "(o@a(sa{sv}))", &session_handle, &shortcuts);
- if(session_handle && shortcuts && strcmp(session_handle, cu->self->session_handle) == 0)
+ if(session_handle && shortcuts && g_strcmp0(session_handle, cu->self->session_handle) == 0)
handle_shortcuts_data(shortcuts, cu->shortcut_changed_callback, cu->userdata);
g_free(session_handle);
@@ -239,10 +241,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) {
- g_object_unref(self->gdbus_con);
- self->gdbus_con = NULL;
- }
+ g_clear_object(&self->gdbus_con);
if(self->session_handle) {
free(self->session_handle);
diff --git a/src/global_shortcuts.h b/src/global_shortcuts.h
index 087c177..ddedec7 100644
--- a/src/global_shortcuts.h
+++ b/src/global_shortcuts.h
@@ -1,5 +1,4 @@
-#ifndef GLOBAL_SHORTCUTS_H
-#define GLOBAL_SHORTCUTS_H
+#pragma once
/* Global shortcuts via desktop portal */
@@ -37,5 +36,3 @@ bool gsr_global_shortcuts_list_shortcuts(gsr_global_shortcuts *self, gsr_shortcu
bool gsr_global_shortcuts_bind_shortcuts(gsr_global_shortcuts *self, const gsr_bind_shortcut *shortcuts, int num_shortcuts, gsr_shortcut_callback callback, void *userdata);
bool gsr_global_shortcuts_subscribe_activated_signal(gsr_global_shortcuts *self, gsr_deactivated_callback deactivated_callback, gsr_shortcut_callback shortcut_changed_callback, void *userdata);
-
-#endif /* GLOBAL_SHORTCUTS_H */
diff --git a/src/gsr-config-page.c b/src/gsr-config-page.c
index 9beb39d..7736bdb 100644
--- a/src/gsr-config-page.c
+++ b/src/gsr-config-page.c
@@ -1023,10 +1023,9 @@ gsr_config_page_read_config(GsrConfigPage *self, GsrConfig *config)
/* ── Capture Target ── */
guint ra_idx = adw_combo_row_get_selected(self->record_area_row);
- g_free(m->record_area_option);
- m->record_area_option = (ra_idx < (guint)self->n_record_area_ids)
- ? g_strdup(self->record_area_ids[ra_idx])
- : g_strdup("");
+ g_set_str(&m->record_area_option,
+ (ra_idx < (guint)self->n_record_area_ids)
+ ? self->record_area_ids[ra_idx] : "");
m->change_video_resolution = adw_switch_row_get_active(self->change_resolution_row);
m->video_width = (int32_t)adw_spin_row_get_value(self->video_width_row);
@@ -1041,8 +1040,7 @@ gsr_config_page_read_config(GsrConfigPage *self, GsrConfig *config)
if (m->audio_input) {
for (int i = 0; i < m->n_audio_input; i++)
g_free(m->audio_input[i]);
- g_free(m->audio_input);
- m->audio_input = NULL;
+ g_clear_pointer(&m->audio_input, g_free);
m->n_audio_input = 0;
}
@@ -1111,33 +1109,28 @@ gsr_config_page_read_config(GsrConfigPage *self, GsrConfig *config)
m->record_app_audio_inverted = adw_switch_row_get_active(self->app_audio_inverted_row);
/* Audio codec */
- g_free(m->audio_codec);
- m->audio_codec = g_strdup(audio_codec_index_to_string(
+ g_set_str(&m->audio_codec, audio_codec_index_to_string(
adw_combo_row_get_selected(self->audio_codec_row)));
/* ── Video ── */
- g_free(m->quality);
- m->quality = g_strdup(quality_index_to_string(
+ g_set_str(&m->quality, quality_index_to_string(
adw_combo_row_get_selected(self->quality_row)));
m->video_bitrate = (int32_t)adw_spin_row_get_value(self->bitrate_row);
/* Video codec */
guint vc_idx = adw_combo_row_get_selected(self->video_codec_row);
- g_free(m->codec);
- m->codec = (vc_idx < (guint)self->n_video_codec_ids)
- ? g_strdup(self->video_codec_ids[vc_idx])
- : g_strdup("auto");
+ g_set_str(&m->codec,
+ (vc_idx < (guint)self->n_video_codec_ids)
+ ? self->video_codec_ids[vc_idx] : "auto");
/* Color range */
- g_free(m->color_range);
- m->color_range = g_strdup(color_range_index_to_string(
+ g_set_str(&m->color_range, color_range_index_to_string(
adw_combo_row_get_selected(self->color_range_row)));
m->fps = (int32_t)adw_spin_row_get_value(self->fps_row);
- g_free(m->framerate_mode);
- m->framerate_mode = g_strdup(framerate_mode_index_to_string(
+ g_set_str(&m->framerate_mode, framerate_mode_index_to_string(
adw_combo_row_get_selected(self->framerate_mode_row)));
m->overclock = adw_switch_row_get_active(self->overclock_row);
@@ -1366,7 +1359,7 @@ gsr_config_page_build_audio_args(GsrConfigPage *self, gboolean merge_tracks)
g_string_append(merged, g_ptr_array_index(tracks, i));
}
g_ptr_array_set_size(tracks, 0);
- g_ptr_array_add(tracks, g_string_free(merged, FALSE));
+ g_ptr_array_add(tracks, g_string_free_and_steal(merged));
}
return tracks;
diff --git a/src/gsr-config-page.h b/src/gsr-config-page.h
index 851bd76..bf5304b 100644
--- a/src/gsr-config-page.h
+++ b/src/gsr-config-page.h
@@ -1,8 +1,9 @@
#pragma once
#include <adwaita.h>
-#include "gsr-info.h"
+
#include "gsr-config.h"
+#include "gsr-info.h"
G_BEGIN_DECLS
diff --git a/src/gsr-config.c b/src/gsr-config.c
index e9de887..af8b980 100644
--- a/src/gsr-config.c
+++ b/src/gsr-config.c
@@ -1,15 +1,15 @@
#include "gsr-config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <errno.h>
#include <inttypes.h>
-#include <unistd.h>
-#include <limits.h>
#include <libgen.h>
+#include <limits.h>
#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
+#include <unistd.h>
#include <gtk/gtk.h>
diff --git a/src/gsr-config.h b/src/gsr-config.h
index 3ed0525..31f89e2 100644
--- a/src/gsr-config.h
+++ b/src/gsr-config.h
@@ -1,9 +1,10 @@
#pragma once
-#include <glib.h>
#include <stdbool.h>
#include <stdint.h>
+#include <glib.h>
+
G_BEGIN_DECLS
/* ── Hotkey ──────────────────────────────────────────────────────── */
diff --git a/src/gsr-hotkeys.c b/src/gsr-hotkeys.c
index 5c1b7f7..41fa541 100644
--- a/src/gsr-hotkeys.c
+++ b/src/gsr-hotkeys.c
@@ -1,11 +1,12 @@
#include "gsr-hotkeys.h"
-#include "gsr-window.h"
-#include "gsr-config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "gsr-config.h"
+#include "gsr-window.h"
+
/*
* On X11 we use gsr-x11-hotkeys.h for XGrabKey + GSource polling.
* On Wayland we use global_shortcuts.h for the D-Bus portal.
@@ -14,8 +15,9 @@
*/
#ifdef HAVE_X11
-#include "gsr-x11-hotkeys.h"
#include <X11/keysym.h>
+
+#include "gsr-x11-hotkeys.h"
#ifdef GDK_WINDOWING_X11
#include <gdk/x11/gdkx.h>
#endif
@@ -202,17 +204,17 @@ on_wayland_deactivated(const char *shortcut_id, void *userdata)
/* The portal uses 3 shared IDs across all modes.
* Dispatch based on the currently visible page. */
- if (strcmp(shortcut_id, SHORTCUT_ID_START_STOP) == 0) {
+ if (g_strcmp0(shortcut_id, SHORTCUT_ID_START_STOP) == 0) {
if (g_str_equal(page, "stream") ||
g_str_equal(page, "record") ||
g_str_equal(page, "replay"))
{
dispatch_start_stop(self);
}
- } else if (strcmp(shortcut_id, SHORTCUT_ID_PAUSE_UNPAUSE) == 0) {
+ } else if (g_strcmp0(shortcut_id, SHORTCUT_ID_PAUSE_UNPAUSE) == 0) {
if (g_str_equal(page, "record"))
dispatch_pause_unpause(self);
- } else if (strcmp(shortcut_id, SHORTCUT_ID_SAVE_REPLAY) == 0) {
+ } else if (g_strcmp0(shortcut_id, SHORTCUT_ID_SAVE_REPLAY) == 0) {
if (g_str_equal(page, "replay"))
dispatch_save_replay(self);
}
diff --git a/src/gsr-info.c b/src/gsr-info.c
index 4187aed..861cd38 100644
--- a/src/gsr-info.c
+++ b/src/gsr-info.c
@@ -258,19 +258,19 @@ gsr_info_clear(GsrInfo *info)
bool
gsr_info_is_codec_supported(const GsrInfo *info, const char *codec_id)
{
- if (strcmp(codec_id, "auto") == 0) return true;
+ if (g_strcmp0(codec_id, "auto") == 0) return true;
const GsrSupportedVideoCodecs *vc = &info->supported_video_codecs;
- if (strcmp(codec_id, "h264") == 0) return vc->h264;
- if (strcmp(codec_id, "h264_software") == 0) return vc->h264_software;
- if (strcmp(codec_id, "hevc") == 0) return vc->hevc;
- if (strcmp(codec_id, "hevc_hdr") == 0) return vc->hevc_hdr;
- if (strcmp(codec_id, "hevc_10bit") == 0) return vc->hevc_10bit;
- if (strcmp(codec_id, "av1") == 0) return vc->av1;
- if (strcmp(codec_id, "av1_hdr") == 0) return vc->av1_hdr;
- if (strcmp(codec_id, "av1_10bit") == 0) return vc->av1_10bit;
- if (strcmp(codec_id, "vp8") == 0) return vc->vp8;
- if (strcmp(codec_id, "vp9") == 0) return vc->vp9;
+ if (g_strcmp0(codec_id, "h264") == 0) return vc->h264;
+ if (g_strcmp0(codec_id, "h264_software") == 0) return vc->h264_software;
+ if (g_strcmp0(codec_id, "hevc") == 0) return vc->hevc;
+ if (g_strcmp0(codec_id, "hevc_hdr") == 0) return vc->hevc_hdr;
+ if (g_strcmp0(codec_id, "hevc_10bit") == 0) return vc->hevc_10bit;
+ if (g_strcmp0(codec_id, "av1") == 0) return vc->av1;
+ if (g_strcmp0(codec_id, "av1_hdr") == 0) return vc->av1_hdr;
+ if (g_strcmp0(codec_id, "av1_10bit") == 0) return vc->av1_10bit;
+ if (g_strcmp0(codec_id, "vp8") == 0) return vc->vp8;
+ if (g_strcmp0(codec_id, "vp9") == 0) return vc->vp9;
return false;
}
@@ -278,10 +278,10 @@ bool
gsr_info_is_capture_option_enabled(const GsrInfo *info, const char *option_id)
{
if (info->system_info.display_server == GSR_DISPLAY_SERVER_WAYLAND) {
- if (strcmp(option_id, "window") == 0 || strcmp(option_id, "focused") == 0)
+ if (g_strcmp0(option_id, "window") == 0 || g_strcmp0(option_id, "focused") == 0)
return false;
}
- if (strcmp(option_id, "portal") == 0)
+ if (g_strcmp0(option_id, "portal") == 0)
return info->supported_capture_options.portal;
return true;
}
diff --git a/src/gsr-info.h b/src/gsr-info.h
index 9764fee..8ce10d0 100644
--- a/src/gsr-info.h
+++ b/src/gsr-info.h
@@ -1,8 +1,9 @@
#pragma once
-#include <glib.h>
#include <stdbool.h>
+#include <glib.h>
+
G_BEGIN_DECLS
/* ── Enums ───────────────────────────────────────────────────────── */
diff --git a/src/gsr-record-page.c b/src/gsr-record-page.c
index de4781e..1a34c0a 100644
--- a/src/gsr-record-page.c
+++ b/src/gsr-record-page.c
@@ -1,10 +1,13 @@
#include "gsr-record-page.h"
+
+#include <signal.h>
+#include <time.h>
+
+#include "gsr-window.h"
+
#ifdef HAVE_X11
#include "gsr-shortcut-accel-dialog.h"
#endif
-#include "gsr-window.h"
-#include <signal.h>
-#include <time.h>
/* ═══════════════════════════════════════════════════════════════════
* GsrRecordPage — "Record" tab
@@ -231,8 +234,7 @@ on_x11_start_stop_shortcut_set(GsrShortcutAccelDialog *dialog,
GsrRecordPage *self = GSR_RECORD_PAGE(user_data);
const char *accel = gsr_shortcut_accel_dialog_get_accelerator(dialog);
- g_free(self->x11_start_stop_accel);
- self->x11_start_stop_accel = g_strdup(accel);
+ g_set_str(&self->x11_start_stop_accel, accel);
if (self->x11_start_stop_label)
gtk_shortcut_label_set_accelerator(self->x11_start_stop_label,
@@ -263,8 +265,7 @@ on_x11_pause_shortcut_set(GsrShortcutAccelDialog *dialog,
GsrRecordPage *self = GSR_RECORD_PAGE(user_data);
const char *accel = gsr_shortcut_accel_dialog_get_accelerator(dialog);
- g_free(self->x11_pause_accel);
- self->x11_pause_accel = g_strdup(accel);
+ g_set_str(&self->x11_pause_accel, accel);
if (self->x11_pause_label)
gtk_shortcut_label_set_accelerator(self->x11_pause_label,
@@ -503,10 +504,7 @@ 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_clear_handle_id(&self->timer_source_id, g_source_remove);
g_free(self->save_directory);
#ifdef HAVE_X11
@@ -585,8 +583,7 @@ gsr_record_page_apply_config(GsrRecordPage *self, const GsrConfig *config)
/* Save directory */
if (r->save_directory && r->save_directory[0]) {
- g_free(self->save_directory);
- self->save_directory = g_strdup(r->save_directory);
+ g_set_str(&self->save_directory, r->save_directory);
adw_action_row_set_subtitle(self->save_dir_row, self->save_directory);
}
@@ -617,12 +614,10 @@ gsr_record_page_read_config(GsrRecordPage *self, GsrConfig *config)
GsrRecordConfig *r = &config->record_config;
/* Save directory */
- g_free(r->save_directory);
- r->save_directory = g_strdup(self->save_directory ? self->save_directory : "");
+ g_set_str(&r->save_directory, self->save_directory ? self->save_directory : "");
/* Container */
- g_free(r->container);
- r->container = g_strdup(container_display_to_id(
+ g_set_str(&r->container, container_display_to_id(
combo_row_get_selected_string(self->container_row)));
/* Hotkeys */
@@ -660,10 +655,7 @@ gsr_record_page_set_active(GsrRecordPage *self, gboolean active)
/* Reset internal state (handles external stop via handle_child_death) */
self->is_active = FALSE;
self->is_paused = FALSE;
- if (self->timer_source_id) {
- g_source_remove(self->timer_source_id);
- self->timer_source_id = 0;
- }
+ g_clear_handle_id(&self->timer_source_id, g_source_remove);
}
}
diff --git a/src/gsr-record-page.h b/src/gsr-record-page.h
index ee5f4f5..0efec2d 100644
--- a/src/gsr-record-page.h
+++ b/src/gsr-record-page.h
@@ -1,8 +1,9 @@
#pragma once
#include <adwaita.h>
-#include "gsr-info.h"
+
#include "gsr-config.h"
+#include "gsr-info.h"
G_BEGIN_DECLS
diff --git a/src/gsr-replay-page.c b/src/gsr-replay-page.c
index 4dcbc81..0a354be 100644
--- a/src/gsr-replay-page.c
+++ b/src/gsr-replay-page.c
@@ -1,10 +1,13 @@
#include "gsr-replay-page.h"
+
+#include <signal.h>
+#include <time.h>
+
+#include "gsr-window.h"
+
#ifdef HAVE_X11
#include "gsr-shortcut-accel-dialog.h"
#endif
-#include "gsr-window.h"
-#include <signal.h>
-#include <time.h>
/* ═══════════════════════════════════════════════════════════════════
* GsrReplayPage — "Replay" tab
@@ -212,8 +215,7 @@ on_x11_start_stop_shortcut_set(GsrShortcutAccelDialog *dialog,
GsrReplayPage *self = GSR_REPLAY_PAGE(user_data);
const char *accel = gsr_shortcut_accel_dialog_get_accelerator(dialog);
- g_free(self->x11_start_stop_accel);
- self->x11_start_stop_accel = g_strdup(accel);
+ g_set_str(&self->x11_start_stop_accel, accel);
if (self->x11_start_stop_label)
gtk_shortcut_label_set_accelerator(self->x11_start_stop_label,
@@ -243,8 +245,7 @@ on_x11_save_shortcut_set(GsrShortcutAccelDialog *dialog,
GsrReplayPage *self = GSR_REPLAY_PAGE(user_data);
const char *accel = gsr_shortcut_accel_dialog_get_accelerator(dialog);
- g_free(self->x11_save_accel);
- self->x11_save_accel = g_strdup(accel);
+ g_set_str(&self->x11_save_accel, accel);
if (self->x11_save_label)
gtk_shortcut_label_set_accelerator(self->x11_save_label,
@@ -491,10 +492,7 @@ 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_clear_handle_id(&self->timer_source_id, g_source_remove);
g_free(self->save_directory);
#ifdef HAVE_X11
@@ -573,8 +571,7 @@ gsr_replay_page_apply_config(GsrReplayPage *self, const GsrConfig *config)
/* Save directory */
if (rp->save_directory && rp->save_directory[0]) {
- g_free(self->save_directory);
- self->save_directory = g_strdup(rp->save_directory);
+ g_set_str(&self->save_directory, rp->save_directory);
adw_action_row_set_subtitle(self->save_dir_row, self->save_directory);
}
@@ -609,12 +606,10 @@ gsr_replay_page_read_config(GsrReplayPage *self, GsrConfig *config)
GsrReplayConfig *rp = &config->replay_config;
/* Save directory */
- g_free(rp->save_directory);
- rp->save_directory = g_strdup(self->save_directory ? self->save_directory : "");
+ g_set_str(&rp->save_directory, self->save_directory ? self->save_directory : "");
/* Container */
- g_free(rp->container);
- rp->container = g_strdup(container_display_to_id(
+ g_set_str(&rp->container, container_display_to_id(
combo_row_get_selected_string(self->container_row)));
/* Replay time */
@@ -650,10 +645,7 @@ gsr_replay_page_set_active(GsrReplayPage *self, gboolean active)
/* Reset internal state (handles external stop via handle_child_death) */
self->is_active = FALSE;
- if (self->timer_source_id) {
- g_source_remove(self->timer_source_id);
- self->timer_source_id = 0;
- }
+ g_clear_handle_id(&self->timer_source_id, g_source_remove);
}
}
diff --git a/src/gsr-replay-page.h b/src/gsr-replay-page.h
index d703d93..f8f6c89 100644
--- a/src/gsr-replay-page.h
+++ b/src/gsr-replay-page.h
@@ -1,8 +1,9 @@
#pragma once
#include <adwaita.h>
-#include "gsr-info.h"
+
#include "gsr-config.h"
+#include "gsr-info.h"
G_BEGIN_DECLS
diff --git a/src/gsr-shortcut-accel-dialog.c b/src/gsr-shortcut-accel-dialog.c
index 37e90c6..83db23e 100644
--- a/src/gsr-shortcut-accel-dialog.c
+++ b/src/gsr-shortcut-accel-dialog.c
@@ -76,8 +76,7 @@ update_display(GsrShortcutAccelDialog *self)
static void
set_accelerator(GsrShortcutAccelDialog *self, const char *accel)
{
- g_free(self->accelerator);
- self->accelerator = g_strdup(accel);
+ g_set_str(&self->accelerator, accel);
}
/**
diff --git a/src/gsr-stream-page.c b/src/gsr-stream-page.c
index e3ec495..989927b 100644
--- a/src/gsr-stream-page.c
+++ b/src/gsr-stream-page.c
@@ -1,9 +1,12 @@
#include "gsr-stream-page.h"
+
+#include <time.h>
+
+#include "gsr-window.h"
+
#ifdef HAVE_X11
#include "gsr-shortcut-accel-dialog.h"
#endif
-#include "gsr-window.h"
-#include <time.h>
/* ═══════════════════════════════════════════════════════════════════
* GsrStreamPage — "Stream" tab
@@ -185,8 +188,7 @@ on_x11_start_stop_shortcut_set(GsrShortcutAccelDialog *dialog,
GsrStreamPage *self = GSR_STREAM_PAGE(user_data);
const char *accel = gsr_shortcut_accel_dialog_get_accelerator(dialog);
- g_free(self->x11_start_stop_accel);
- self->x11_start_stop_accel = g_strdup(accel);
+ g_set_str(&self->x11_start_stop_accel, accel);
if (self->x11_start_stop_label)
gtk_shortcut_label_set_accelerator(self->x11_start_stop_label,
@@ -415,10 +417,7 @@ gsr_stream_page_finalize(GObject *object)
{
GsrStreamPage *self = GSR_STREAM_PAGE(object);
- if (self->timer_source_id) {
- g_source_remove(self->timer_source_id);
- self->timer_source_id = 0;
- }
+ g_clear_handle_id(&self->timer_source_id, g_source_remove);
#ifdef HAVE_X11
g_free(self->x11_start_stop_accel);
@@ -571,24 +570,16 @@ gsr_stream_page_read_config(GsrStreamPage *self, GsrConfig *config)
{
GsrStreamingConfig *s = &config->streaming_config;
- g_free(s->streaming_service);
- s->streaming_service = g_strdup(service_index_to_string(
+ g_set_str(&s->streaming_service, service_index_to_string(
adw_combo_row_get_selected(self->service_row)));
- g_free(s->twitch_stream_key);
- s->twitch_stream_key = g_strdup(
- gtk_editable_get_text(GTK_EDITABLE(self->twitch_key_row)));
+ g_set_str(&s->twitch_stream_key, gtk_editable_get_text(GTK_EDITABLE(self->twitch_key_row)));
- g_free(s->youtube_stream_key);
- s->youtube_stream_key = g_strdup(
- gtk_editable_get_text(GTK_EDITABLE(self->youtube_key_row)));
+ g_set_str(&s->youtube_stream_key, gtk_editable_get_text(GTK_EDITABLE(self->youtube_key_row)));
- g_free(s->custom_url);
- s->custom_url = g_strdup(
- gtk_editable_get_text(GTK_EDITABLE(self->custom_url_row)));
+ g_set_str(&s->custom_url, gtk_editable_get_text(GTK_EDITABLE(self->custom_url_row)));
- g_free(s->custom_container);
- s->custom_container = g_strdup(stream_container_display_to_id(
+ g_set_str(&s->custom_container, stream_container_display_to_id(
combo_row_get_selected_string(self->container_row)));
/* Hotkeys */
@@ -618,10 +609,7 @@ gsr_stream_page_set_active(GsrStreamPage *self, gboolean active)
/* Reset internal state (handles external stop via handle_child_death) */
self->is_active = FALSE;
- if (self->timer_source_id) {
- g_source_remove(self->timer_source_id);
- self->timer_source_id = 0;
- }
+ g_clear_handle_id(&self->timer_source_id, g_source_remove);
}
}
diff --git a/src/gsr-stream-page.h b/src/gsr-stream-page.h
index 798e2f4..7921f3f 100644
--- a/src/gsr-stream-page.h
+++ b/src/gsr-stream-page.h
@@ -1,8 +1,9 @@
#pragma once
#include <adwaita.h>
-#include "gsr-info.h"
+
#include "gsr-config.h"
+#include "gsr-info.h"
G_BEGIN_DECLS
diff --git a/src/gsr-window.c b/src/gsr-window.c
index 28091f9..fe7a55e 100644
--- a/src/gsr-window.c
+++ b/src/gsr-window.c
@@ -1,17 +1,18 @@
#include "gsr-window.h"
-#include "gsr-info.h"
-#include "gsr-config.h"
-#include "gsr-config-page.h"
-#include "gsr-stream-page.h"
-#include "gsr-record-page.h"
-#include "gsr-replay-page.h"
-#include "gsr-hotkeys.h"
+#include <errno.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
-#include <errno.h>
+
+#include "gsr-config-page.h"
+#include "gsr-config.h"
+#include "gsr-hotkeys.h"
+#include "gsr-info.h"
+#include "gsr-record-page.h"
+#include "gsr-replay-page.h"
+#include "gsr-stream-page.h"
#ifdef __linux__
#include <sys/prctl.h>
@@ -71,6 +72,7 @@ struct _GsrWindow {
/* ── Startup error state ─── */
GsrInfoExitStatus info_status; /* cached for deferred dialog */
+ guint startup_idle_id; /* deferred error-check idle */
};
G_DEFINE_FINAL_TYPE(GsrWindow, gsr_window, ADW_TYPE_APPLICATION_WINDOW)
@@ -162,10 +164,7 @@ 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;
- }
+ g_clear_handle_id(&self->notification_timeout_id, g_source_remove);
guint timeout_ms = (effective >= G_NOTIFICATION_PRIORITY_URGENT) ? 10000 : 3000;
self->notification_timeout_id = g_timeout_add(timeout_ms, on_notification_withdraw, self);
@@ -405,8 +404,7 @@ build_command_args(GsrWindow *self, GsrActiveMode mode)
char *filename = build_record_filename(
save_dir ? save_dir : "/tmp", ext);
- g_free(self->record_filename);
- self->record_filename = g_strdup(filename);
+ g_set_str(&self->record_filename, filename);
g_ptr_array_add(args, g_strdup("-o"));
g_ptr_array_add(args, filename); /* transfers ownership */
@@ -526,10 +524,7 @@ handle_child_death(GsrWindow *self, int exit_status)
g_debug("Child died with exit_status=%d, mode=%d", exit_status, mode);
/* Stop the poll timer */
- if (self->poll_timer_id) {
- g_source_remove(self->poll_timer_id);
- self->poll_timer_id = 0;
- }
+ g_clear_handle_id(&self->poll_timer_id, g_source_remove);
/* Enter the "stopped" state on the appropriate page */
switch (mode) {
@@ -648,10 +643,7 @@ on_close_request(GtkWindow *window, gpointer user_data G_GNUC_UNUSED)
}
/* Stop poll timer */
- if (self->poll_timer_id) {
- g_source_remove(self->poll_timer_id);
- self->poll_timer_id = 0;
- }
+ g_clear_handle_id(&self->poll_timer_id, g_source_remove);
/* Free hotkeys before the window is destroyed */
if (self->hotkeys) {
@@ -660,10 +652,7 @@ on_close_request(GtkWindow *window, gpointer user_data G_GNUC_UNUSED)
}
/* 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;
- }
+ g_clear_handle_id(&self->notification_timeout_id, g_source_remove);
if (self->showing_notification) {
GtkApplication *app = GTK_APPLICATION(
gtk_window_get_application(GTK_WINDOW(self)));
@@ -784,10 +773,11 @@ show_fatal_error(GsrWindow *self, const char *heading, const char *body)
adw_dialog_present(ADW_DIALOG(dlg), GTK_WIDGET(self));
}
-static gboolean
+static void
check_startup_errors_idle(gpointer user_data)
{
GsrWindow *self = GSR_WINDOW(user_data);
+ self->startup_idle_id = 0; /* source auto-removes after this one-shot */
switch (self->info_status) {
case GSR_INFO_EXIT_FAILED_TO_RUN:
@@ -796,7 +786,7 @@ check_startup_errors_idle(gpointer user_data)
"Failed to run the <tt>gpu-screen-recorder</tt> command.\n\n"
"Make sure <tt>gpu-screen-recorder</tt> is installed and "
"accessible in your PATH.");
- return G_SOURCE_REMOVE;
+ return;
case GSR_INFO_EXIT_OPENGL_FAILED:
show_fatal_error(self,
@@ -804,7 +794,7 @@ check_startup_errors_idle(gpointer user_data)
"Failed to get OpenGL information.\n\n"
"Make sure your GPU drivers are properly installed. "
"You may need to install the Vulkan or Mesa drivers for your GPU.");
- return G_SOURCE_REMOVE;
+ return;
case GSR_INFO_EXIT_NO_DRM_CARD:
show_fatal_error(self,
@@ -812,7 +802,7 @@ check_startup_errors_idle(gpointer user_data)
"Failed to find a valid DRM card for your GPU.\n\n"
"If you are running in a VM, make sure GPU passthrough is "
"enabled and properly configured.");
- return G_SOURCE_REMOVE;
+ return;
case GSR_INFO_EXIT_OK:
break;
@@ -824,7 +814,7 @@ check_startup_errors_idle(gpointer user_data)
"No display server detected",
"Neither X11 nor Wayland is running.\n\n"
"GPU Screen Recorder requires either X11 or Wayland.");
- return G_SOURCE_REMOVE;
+ return;
}
/* Check for monitors (Wayland without portal needs monitors) */
@@ -838,10 +828,8 @@ check_startup_errors_idle(gpointer user_data)
"Make sure GPU Screen Recorder is running on the same GPU "
"that your monitors are connected to. You can use the "
"<tt>DRI_PRIME</tt> environment variable to choose a GPU.");
- return G_SOURCE_REMOVE;
+ return;
}
-
- return G_SOURCE_REMOVE;
}
/* ── GObject boilerplate ─────────────────────────────────────────── */
@@ -994,7 +982,7 @@ gsr_window_init(GsrWindow *self)
#endif
/* ── Deferred startup error check ─── */
- g_idle_add(check_startup_errors_idle, self);
+ self->startup_idle_id = g_idle_add_once(check_startup_errors_idle, self);
}
static void
@@ -1002,15 +990,11 @@ gsr_window_finalize(GObject *object)
{
GsrWindow *self = GSR_WINDOW(object);
- if (self->poll_timer_id) {
- g_source_remove(self->poll_timer_id);
- self->poll_timer_id = 0;
- }
+ g_clear_handle_id(&self->startup_idle_id, g_source_remove);
- if (self->notification_timeout_id) {
- g_source_remove(self->notification_timeout_id);
- self->notification_timeout_id = 0;
- }
+ g_clear_handle_id(&self->poll_timer_id, g_source_remove);
+
+ g_clear_handle_id(&self->notification_timeout_id, g_source_remove);
if (self->hotkeys) {
gsr_hotkeys_free(self->hotkeys);
@@ -1121,10 +1105,7 @@ gsr_window_stop_process(GsrWindow *self, gboolean *already_dead)
gboolean success = kill_and_wait(pid, already_dead);
/* Stop poll timer */
- if (self->poll_timer_id) {
- g_source_remove(self->poll_timer_id);
- self->poll_timer_id = 0;
- }
+ g_clear_handle_id(&self->poll_timer_id, g_source_remove);
self->active_mode = GSR_ACTIVE_MODE_NONE;
diff --git a/src/gsr-x11-hotkeys.c b/src/gsr-x11-hotkeys.c
index a338441..dc0028f 100644
--- a/src/gsr-x11-hotkeys.c
+++ b/src/gsr-x11-hotkeys.c
@@ -2,11 +2,11 @@
#include <stdlib.h>
#include <string.h>
+
#include <X11/Xlib.h>
#include <X11/Xutil.h>
-
-#include <glib.h>
#include <glib-unix.h>
+#include <glib.h>
/* ── Internal types ──────────────────────────────────────────────── */
diff --git a/src/gsr-x11-window-picker.c b/src/gsr-x11-window-picker.c
index 83f1aa2..47ae182 100644
--- a/src/gsr-x11-window-picker.c
+++ b/src/gsr-x11-window-picker.c
@@ -2,13 +2,13 @@
#include <stdlib.h>
#include <string.h>
+
+#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/keysym.h>
-#include <X11/Xatom.h>
-
-#include <glib.h>
#include <glib-unix.h>
+#include <glib.h>
/* ── Internal struct ─────────────────────────────────────────────── */