diff options
| author | Trung Lê <8@tle.id.au> | 2026-06-10 11:11:52 +1000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-06-10 11:11:52 +1000 |
| commit | 3d6435e45cc4b760afa10ddf96a1c542817c0fe1 (patch) | |
| tree | c461c87c953d6b57abb5ca353bfe8929d06fe12d | |
| parent | 76ff689850c160e7eca27ee51852dcf17cc1938a (diff) | |
| parent | 53ccd9f7ca56a77c445c1cf5eada3ea8e07c16b6 (diff) | |
Merge pull request #7 from runlevel5/feature/goblint-analysis
ci: adopt gobject-linter as a blocking static-analysis gate
| -rw-r--r-- | .github/workflows/gobject-linter.yml | 44 | ||||
| -rw-r--r-- | gobject-linter.toml | 32 | ||||
| -rw-r--r-- | src/global_shortcuts.c | 19 | ||||
| -rw-r--r-- | src/global_shortcuts.h | 5 | ||||
| -rw-r--r-- | src/gsr-config-page.c | 31 | ||||
| -rw-r--r-- | src/gsr-config-page.h | 3 | ||||
| -rw-r--r-- | src/gsr-config.c | 10 | ||||
| -rw-r--r-- | src/gsr-config.h | 3 | ||||
| -rw-r--r-- | src/gsr-hotkeys.c | 14 | ||||
| -rw-r--r-- | src/gsr-info.c | 26 | ||||
| -rw-r--r-- | src/gsr-info.h | 3 | ||||
| -rw-r--r-- | src/gsr-record-page.c | 34 | ||||
| -rw-r--r-- | src/gsr-record-page.h | 3 | ||||
| -rw-r--r-- | src/gsr-replay-page.c | 34 | ||||
| -rw-r--r-- | src/gsr-replay-page.h | 3 | ||||
| -rw-r--r-- | src/gsr-shortcut-accel-dialog.c | 3 | ||||
| -rw-r--r-- | src/gsr-stream-page.c | 38 | ||||
| -rw-r--r-- | src/gsr-stream-page.h | 3 | ||||
| -rw-r--r-- | src/gsr-window.c | 75 | ||||
| -rw-r--r-- | src/gsr-x11-hotkeys.c | 4 | ||||
| -rw-r--r-- | src/gsr-x11-window-picker.c | 6 |
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..42b72da --- /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@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + + # 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@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + # 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, ×tamp, &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 ─────────────────────────────────────────────── */ |
