Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gpu-screen-recorder-gtk for 
openSUSE:Factory checked in at 2024-11-18 20:00:43
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gpu-screen-recorder-gtk (Old)
 and      /work/SRC/openSUSE:Factory/.gpu-screen-recorder-gtk.new.2017 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gpu-screen-recorder-gtk"

Mon Nov 18 20:00:43 2024 rev:9 rq:1224597 version:20241116

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/gpu-screen-recorder-gtk/gpu-screen-recorder-gtk.changes
  2024-11-06 16:53:33.136048734 +0100
+++ 
/work/SRC/openSUSE:Factory/.gpu-screen-recorder-gtk.new.2017/gpu-screen-recorder-gtk.changes
        2024-11-18 20:01:04.716801398 +0100
@@ -1,0 +2,6 @@
+Sat Nov 16 23:02:07 UTC 2024 - mantari...@pm.me
+
+- Update to version 20241116:
+  * Add support application audio recording
+
+-------------------------------------------------------------------

Old:
----
  gpu-screen-recorder-gtk-20241105.tar.zst

New:
----
  gpu-screen-recorder-gtk-20241116.tar.zst

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gpu-screen-recorder-gtk.spec ++++++
--- /var/tmp/diff_new_pack.Yi5qyn/_old  2024-11-18 20:01:05.376829004 +0100
+++ /var/tmp/diff_new_pack.Yi5qyn/_new  2024-11-18 20:01:05.380829171 +0100
@@ -19,7 +19,7 @@
 %bcond_with test
 %define appid   com.dec05eba.gpu_screen_recorder
 Name:           gpu-screen-recorder-gtk
-Version:        20241105
+Version:        20241116
 Release:        0
 Summary:        GTK frontend for GPU Screen Recorder
 License:        GPL-3.0-only

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.Yi5qyn/_old  2024-11-18 20:01:05.424831011 +0100
+++ /var/tmp/diff_new_pack.Yi5qyn/_new  2024-11-18 20:01:05.432831346 +0100
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://repo.dec05eba.com/gpu-screen-recorder-gtk.git</param>
-              <param 
name="changesrevision">ee137eedf5dd59a96126f02675ffe9553812a044</param></service></servicedata>
+              <param 
name="changesrevision">0c2bb1a7a3dfe555619c17748d88e50bd330b80a</param></service></servicedata>
 (No newline at EOF)
 

++++++ gpu-screen-recorder-gtk-20241105.tar.zst -> 
gpu-screen-recorder-gtk-20241116.tar.zst ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gpu-screen-recorder-gtk-20241105/README.md 
new/gpu-screen-recorder-gtk-20241116/README.md
--- old/gpu-screen-recorder-gtk-20241105/README.md      2024-11-05 
10:29:53.000000000 +0100
+++ new/gpu-screen-recorder-gtk-20241116/README.md      2024-11-16 
17:40:32.000000000 +0100
@@ -6,7 +6,7 @@
 
 # Installation
 This program depends on [GPU Screen 
Recorder](https://git.dec05eba.com/gpu-screen-recorder/) which needs to be 
installed first.\
-Run `sudo ./install.sh` or if you are running Arch Linux, then you can find 
gpu screen recorder gtk on aur under the name gpu-screen-recorder-gtk-git (`yay 
-S gpu-screen-recorder-gtk-git`).\
+Run `sudo ./install.sh` or if you are running Arch Linux, then you can find 
gpu screen recorder gtk on aur under the name gpu-screen-recorder-gtk (`yay -S 
gpu-screen-recorder-gtk`).\
 You can also install gpu screen recorder (the gtk gui version) from 
[flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder). 
This flatpak includes gpu-screen-recorder so no need to install that first.
 
 # Dependencies
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gpu-screen-recorder-gtk-20241105/TODO 
new/gpu-screen-recorder-gtk-20241116/TODO
--- old/gpu-screen-recorder-gtk-20241105/TODO   2024-11-05 10:29:53.000000000 
+0100
+++ new/gpu-screen-recorder-gtk-20241116/TODO   2024-11-16 17:40:32.000000000 
+0100
@@ -61,4 +61,12 @@
 
 Use modprobe command. modprobe on system startup in modprobe.d directory is 
only available for udev, other systems need to add it to linux kernel boot 
parameters (is this also needed for nvidia open kernel module driver?).
 
-Save gpu screen recorder status in $XDG_RUNTIME_DIR.
\ No newline at end of file
+Save gpu screen recorder status in $XDG_RUNTIME_DIR.
+
+Add option to capture application audio. This should show a popup where you 
can use one of the available applications or a custom one and choose to record 
that application or all applications except that one.
+
+Add profile option. Convert view to profile, add an option at the bottom that 
says "Edit profiles..." which should show a popup where you can create/remove 
profiles. New profiles should always be in advanced view.
+
+Move x11 hotkey code to its own file.
+
+Add audio devices/app refresh button.
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gpu-screen-recorder-gtk-20241105/com.dec05eba.gpu_screen_recorder.appdata.xml
 
new/gpu-screen-recorder-gtk-20241116/com.dec05eba.gpu_screen_recorder.appdata.xml
--- 
old/gpu-screen-recorder-gtk-20241105/com.dec05eba.gpu_screen_recorder.appdata.xml
   2024-11-05 10:29:53.000000000 +0100
+++ 
new/gpu-screen-recorder-gtk-20241116/com.dec05eba.gpu_screen_recorder.appdata.xml
   2024-11-16 17:40:32.000000000 +0100
@@ -80,6 +80,13 @@
     </screenshots>
 
     <releases>
+        <release version="4.3.0" date="2024-11-16">
+            <description>
+                <ul>
+                    <li>Add option to record audio from applications instead 
of audio devices (pipewire only)</li>
+                </ul>
+            </description>
+        </release>
         <release version="4.2.6" date="2024-11-05">
             <description>
                 <ul>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gpu-screen-recorder-gtk-20241105/meson.build 
new/gpu-screen-recorder-gtk-20241116/meson.build
--- old/gpu-screen-recorder-gtk-20241105/meson.build    2024-11-05 
10:29:53.000000000 +0100
+++ new/gpu-screen-recorder-gtk-20241116/meson.build    2024-11-16 
17:40:32.000000000 +0100
@@ -1,4 +1,4 @@
-project('gpu-screen-recorder-gtk', ['c', 'cpp'], version : '4.2.6', 
default_options : ['warning_level=2'])
+project('gpu-screen-recorder-gtk', ['c', 'cpp'], version : '4.3.0', 
default_options : ['warning_level=2'])
 
 add_project_arguments('-Wshadow', language : ['c', 'cpp'])
 if get_option('buildtype') == 'debug'
@@ -18,15 +18,23 @@
     dependency('ayatana-appindicator3-0.1'),
 ]
 
-add_project_arguments('-DGSR_VERSION="' + meson.project_version() + '"', 
language: ['c', 'cpp'])
-
-executable('gpu-screen-recorder-gtk', src, dependencies : dep, install : true)
-
 prefix = get_option('prefix')
 datadir = get_option('datadir')
+icons_path = join_paths(prefix, datadir, 'icons')
+
+executable('gpu-screen-recorder-gtk',
+    src,
+    dependencies : dep,
+    install : true,
+    cpp_args : [
+        '-DGSR_ICONS_PATH="' + icons_path + '"',
+        '-DGSR_VERSION="' + meson.project_version() + '"'
+    ]
+)
+
 install_data(files('com.dec05eba.gpu_screen_recorder.desktop'), install_dir : 
join_paths(prefix, datadir, 'applications'))
 install_data(files('com.dec05eba.gpu_screen_recorder.appdata.xml'), 
install_dir : join_paths(prefix, datadir, 'metainfo'))
-install_subdir('icons/hicolor', install_dir : join_paths(prefix, datadir, 
'icons'))
+install_subdir('icons/hicolor', install_dir : icons_path)
 
 gnome = import('gnome')
 gnome.post_install(gtk_update_icon_cache : true, update_desktop_database : 
true)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gpu-screen-recorder-gtk-20241105/project.conf 
new/gpu-screen-recorder-gtk-20241116/project.conf
--- old/gpu-screen-recorder-gtk-20241105/project.conf   2024-11-05 
10:29:53.000000000 +0100
+++ new/gpu-screen-recorder-gtk-20241116/project.conf   2024-11-16 
17:40:32.000000000 +0100
@@ -1,7 +1,7 @@
 [package]
 name = "gpu-screen-recorder-gtk"
 type = "executable"
-version = "4.2.6"
+version = "4.3.0"
 platforms = ["posix"]
 
 [config]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gpu-screen-recorder-gtk-20241105/src/config.hpp 
new/gpu-screen-recorder-gtk-20241116/src/config.hpp
--- old/gpu-screen-recorder-gtk-20241105/src/config.hpp 2024-11-05 
10:29:53.000000000 +0100
+++ new/gpu-screen-recorder-gtk-20241116/src/config.hpp 2024-11-16 
17:40:32.000000000 +0100
@@ -28,8 +28,11 @@
     int32_t fps = 60;
     int32_t video_bitrate = 15000;
     bool merge_audio_tracks = true;
+    bool record_app_audio_inverted = false;
     bool change_video_resolution = false;
+    std::string audio_type_view = "audio_devices";
     std::vector<std::string> audio_input;
+    std::vector<std::string> application_audio;
     std::string color_range;
     std::string quality;
     std::string codec; // Video codec
@@ -313,8 +316,11 @@
         {"main.fps", {CONFIG_TYPE_I32, &config.main_config.fps}},
         {"main.video_bitrate", {CONFIG_TYPE_I32, 
&config.main_config.video_bitrate}},
         {"main.merge_audio_tracks", {CONFIG_TYPE_BOOL, 
&config.main_config.merge_audio_tracks}},
+        {"main.record_app_audio_inverted", {CONFIG_TYPE_BOOL, 
&config.main_config.record_app_audio_inverted}},
         {"main.change_video_resolution", {CONFIG_TYPE_BOOL, 
&config.main_config.change_video_resolution}},
+        {"main.audio_type_view", {CONFIG_TYPE_STRING, 
&config.main_config.audio_type_view}},
         {"main.audio_input", {CONFIG_TYPE_STRING_ARRAY, 
&config.main_config.audio_input}},
+        {"main.application_audio", {CONFIG_TYPE_STRING_ARRAY, 
&config.main_config.application_audio}},
         {"main.color_range", {CONFIG_TYPE_STRING, 
&config.main_config.color_range}},
         {"main.quality", {CONFIG_TYPE_STRING, &config.main_config.quality}},
         {"main.codec", {CONFIG_TYPE_STRING, &config.main_config.codec}},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gpu-screen-recorder-gtk-20241105/src/main.cpp 
new/gpu-screen-recorder-gtk-20241116/src/main.cpp
--- old/gpu-screen-recorder-gtk-20241105/src/main.cpp   2024-11-05 
10:29:53.000000000 +0100
+++ new/gpu-screen-recorder-gtk-20241116/src/main.cpp   2024-11-16 
17:40:32.000000000 +0100
@@ -90,19 +90,26 @@
 static GtkEntry *custom_stream_url_entry;
 static GtkSpinButton *replay_time_entry;
 static GtkButton *select_window_button;
-static GtkWidget *audio_input_used_list;
-static GtkWidget *add_audio_input_button;
+static GtkBox *audio_devices_items_box;
 static GtkWidget *record_start_stop_hotkey_button;
 static GtkWidget *pause_unpause_hotkey_button;
 static GtkWidget *replay_start_stop_hotkey_button;
 static GtkWidget *replay_save_hotkey_button;
 static GtkWidget *streaming_start_stop_hotkey_button;
+static GtkWidget *record_app_audio_inverted_button;
 static GtkWidget *merge_audio_tracks_button;
+static GtkFrame *notifications_frame;
 static GtkWidget *show_recording_started_notification_button;
 static GtkWidget *show_recording_stopped_notification_button;
 static GtkWidget *show_recording_saved_notification_button;
 static GtkWidget *record_cursor_button;
 static GtkWidget *restore_portal_session_button;
+static GtkBox *audio_type_radio_button_box;
+static GtkWidget *audio_devices_radio_button;
+static GtkWidget *application_audio_radio_button;
+static GtkGrid *audio_devices_grid;
+static GtkGrid *application_audio_grid;
+static GtkBox *application_audio_items_box;
 static GtkGrid *video_codec_grid;
 static GtkGrid *audio_codec_grid;
 static GtkGrid *color_range_grid;
@@ -169,6 +176,7 @@
 };
 
 static std::vector<AudioInput> audio_inputs;
+static std::vector<std::string> application_audio;
 
 enum class HotkeyMode {
     NoAction,
@@ -249,6 +257,7 @@
 
 struct SystemInfo {
     DisplayServer display_server = DisplayServer::UNKNOWN;
+    bool supports_app_audio = false;
     bool is_steam_deck = false;
 };
 
@@ -302,11 +311,6 @@
     { "hls", "m3u8" }
 };
 
-struct AudioRow {
-    GtkWidget *row;
-    GtkComboBoxText *input_list;
-};
-
 // Dumb hacks below!! why dont these fking paths work outside flatpak.. except 
in kde. TODO: fix this!
 static const char* get_tray_idle_icon_name() {
     if(flatpak)
@@ -546,65 +550,31 @@
     return inputs;
 }
 
-static void used_audio_input_loop_callback(GtkWidget *row, gpointer userdata) {
-    const AudioRow *audio_row = (AudioRow*)g_object_get_data(G_OBJECT(row), 
"audio-row");
-    std::function<void(const AudioRow*)> &callback = 
*(std::function<void(const AudioRow*)>*)userdata;
-    callback(audio_row);
-}
-
-static void for_each_used_audio_input(GtkListBox *list_box, 
std::function<void(const AudioRow*)> callback) {
-    gtk_container_foreach(GTK_CONTAINER(list_box), 
used_audio_input_loop_callback, &callback);
-}
-
-static void drag_begin (GtkWidget *widget, GdkDragContext *context, gpointer) {
-    GtkAllocation alloc;
-    int x, y;
-    double sx, sy;
-
-    GtkWidget *row = gtk_widget_get_ancestor(widget, GTK_TYPE_LIST_BOX_ROW);
-    gtk_widget_get_allocation(row, &alloc);
-    cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 
alloc.width, alloc.height);
-    cairo_t *cr = cairo_create(surface);
-
-    gtk_style_context_add_class(gtk_widget_get_style_context (row), 
"drag-icon");
-    gtk_widget_draw(row, cr);
-    gtk_style_context_remove_class(gtk_widget_get_style_context (row), 
"drag-icon");
-
-    gtk_widget_translate_coordinates(widget, row, 0, 0, &x, &y);
-    cairo_surface_get_device_scale(surface, &sx, &sy);
-    cairo_surface_set_device_offset(surface, -x * sx, -y * sy);
-    gtk_drag_set_icon_surface(context, surface);
-
-    cairo_destroy(cr);
-    cairo_surface_destroy(surface);
-}
-
-static void drag_data_get(GtkWidget *widget, GdkDragContext*, GtkSelectionData 
*selection_data, guint, guint, gpointer) {
-    gtk_selection_data_set(selection_data, 
gdk_atom_intern_static_string("GTK_LIST_BOX_ROW"),
-                          32,
-                          (const guchar *)&widget,
-                          sizeof(gpointer));
-}
+static std::vector<std::string> get_application_audio() {
+    std::vector<std::string> application_audio;
 
-static void drag_data_received(GtkWidget *widget, GdkDragContext*,
-    gint, gint,
-    GtkSelectionData *selection_data,
-    guint, guint32, gpointer)
-{
-    GtkWidget *target = widget;
+    FILE *f = popen("gpu-screen-recorder --list-application-audio", "r");
+    if(!f) {
+        fprintf(stderr, "error: 'gpu-screen-recorder --list-application-audio' 
failed\n");
+        return application_audio;
+    }
 
-    int pos = gtk_list_box_row_get_index(GTK_LIST_BOX_ROW (target));
-    GtkWidget *row = *(GtkWidget**)gtk_selection_data_get_data(selection_data);
-    GtkWidget *source = gtk_widget_get_ancestor(row, GTK_TYPE_LIST_BOX_ROW);
+    char output[16384];
+    ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
+    if(bytes_read < 0 || ferror(f)) {
+        fprintf(stderr, "error: failed to read 'gpu-screen-recorder 
--list-application-audio' output\n");
+        pclose(f);
+        return application_audio;
+    }
+    output[bytes_read] = '\0';
 
-    if (source == target)
-        return;
+    string_split_char(output, '\n', [&](StringView line) {
+        std::string line_str(line.str, line.size);
+        application_audio.emplace_back(std::move(line_str));
+        return true;
+    });
 
-    GtkWidget *list_box = gtk_widget_get_parent(source);
-    g_object_ref(source);
-    gtk_container_remove(GTK_CONTAINER(list_box), source);
-    gtk_list_box_insert(GTK_LIST_BOX(list_box), source, pos);
-    g_object_unref(source);
+    return application_audio;
 }
 
 static bool is_video_capture_option_enabled(const char *str) {
@@ -739,55 +709,8 @@
     gtk_widget_set_sensitive(GTK_WIDGET(stream_button), true);
 }
 
-static GtkWidget* create_used_audio_input_row(void) {
-    char entry_name[] = "GTK_LIST_BOX_ROW";
-    const GtkTargetEntry entries[] = {
-        { entry_name, GTK_TARGET_SAME_APP, 0 }
-    };
-
-    GtkWidget *row = gtk_list_box_row_new();
-
-    GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
-    gtk_container_add(GTK_CONTAINER(row), box);
-
-    GtkWidget *handle = gtk_event_box_new();
-    GtkWidget *image = gtk_image_new_from_icon_name("open-menu-symbolic", 
GTK_ICON_SIZE_MENU);
-    gtk_container_add(GTK_CONTAINER(handle), image);
-    gtk_container_add(GTK_CONTAINER(box), handle);
-
-    GtkComboBoxText *input_list = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-    for(const auto &audio_input : audio_inputs) {
-        gtk_combo_box_text_append(input_list, audio_input.name.c_str(), 
audio_input.description.c_str());
-    }
-    gtk_widget_set_hexpand(GTK_WIDGET(input_list), true);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(input_list), 0);
-    //gtk_combo_box_set_active_id(GTK_COMBO_BOX(combo), id);
-    gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(input_list));
-
-    GtkWidget *remove_button = gtk_button_new_with_label("Remove");
-    gtk_widget_set_halign(remove_button, GTK_ALIGN_END);
-    gtk_container_add(GTK_CONTAINER(box), remove_button);
-
-    gtk_drag_source_set(handle, GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
-    g_signal_connect(handle, "drag-begin", G_CALLBACK(drag_begin), NULL);
-    g_signal_connect(handle, "drag-data-get", G_CALLBACK(drag_data_get), NULL);
-
-    gtk_drag_dest_set(row, GTK_DEST_DEFAULT_ALL, entries, 1, GDK_ACTION_MOVE);
-    g_signal_connect(row, "drag-data-received", 
G_CALLBACK(drag_data_received), NULL);
-
-    AudioRow *audio_row = new AudioRow();
-    audio_row->row = row;
-    audio_row->input_list = input_list;
-    g_object_set_data(G_OBJECT(row), "audio-row", audio_row);
-
-    g_signal_connect(remove_button, "clicked", G_CALLBACK(+[](GtkButton*, 
gpointer userdata){
-        AudioRow *_audio_row = (AudioRow*)userdata;
-        
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(_audio_row->row)), 
_audio_row->row);
-        delete _audio_row;
-        return true;
-    }), audio_row);
-
-    return row;
+static gboolean scroll_event_ignore(GtkWidget*, GdkEvent*, void*) {
+    return TRUE;
 }
 
 // Return true from |callback_func| to continue to the next row
@@ -835,6 +758,96 @@
     return found_index;
 }
 
+static GtkWidget* create_audio_device_combo_box_row(const std::string 
&selected_row_text) {
+    GtkGrid *grid = GTK_GRID(gtk_grid_new());
+    gtk_grid_set_column_spacing(grid, 10);
+    gtk_widget_set_hexpand(GTK_WIDGET(grid), true);
+
+    GtkComboBoxText *audio_device_combo_box = 
GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(audio_device_combo_box, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
+    for(const auto &audio_input : audio_inputs) {
+        gtk_combo_box_text_append(audio_device_combo_box, 
audio_input.name.c_str(), audio_input.description.c_str());
+    }
+
+    if(!audio_inputs.empty() && selected_row_text.empty()) {
+        gtk_combo_box_set_active(GTK_COMBO_BOX(audio_device_combo_box), 0);
+    } else if(!selected_row_text.empty()) {
+        std::string audio_id;
+        const gint target_combo_box_index = 
combo_box_text_get_row_by_label(GTK_COMBO_BOX(audio_device_combo_box), 
selected_row_text.c_str(), audio_id);
+        if(target_combo_box_index != -1)
+            gtk_combo_box_set_active(GTK_COMBO_BOX(audio_device_combo_box), 
target_combo_box_index);
+        else if(!audio_inputs.empty())
+            gtk_combo_box_set_active(GTK_COMBO_BOX(audio_device_combo_box), 0);
+    }
+
+    gtk_widget_set_hexpand(GTK_WIDGET(audio_device_combo_box), true);
+    gtk_grid_attach(grid, GTK_WIDGET(audio_device_combo_box), 0, 0, 1, 1);
+
+    GtkButton *remove_button = GTK_BUTTON(gtk_button_new_with_label("Remove"));
+    gtk_grid_attach(grid, GTK_WIDGET(remove_button), 1, 0, 1, 1);
+
+    g_signal_connect(remove_button, "clicked", G_CALLBACK(+[](GtkButton*, 
gpointer userdata){
+        GtkGrid *grid = (GtkGrid*)userdata;
+        
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(grid))), 
GTK_WIDGET(grid));
+        return true;
+    }), grid);
+
+    return GTK_WIDGET(grid);
+}
+
+static GtkWidget* create_application_audio_combo_box_row(const std::string 
&selected_row_id) {
+    GtkGrid *grid = GTK_GRID(gtk_grid_new());
+    gtk_grid_set_column_spacing(grid, 10);
+    gtk_widget_set_hexpand(GTK_WIDGET(grid), true);
+
+    GtkComboBoxText *application_audio_combo_box = 
GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(application_audio_combo_box, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
+    for(const std::string &app_audio : application_audio) {
+        gtk_combo_box_text_append(application_audio_combo_box, 
app_audio.c_str(), app_audio.c_str());
+    }
+
+    if(!application_audio.empty() && selected_row_id.empty())
+        gtk_combo_box_set_active(GTK_COMBO_BOX(application_audio_combo_box), 
0);
+    else if(!selected_row_id.empty())
+        
gtk_combo_box_set_active_id(GTK_COMBO_BOX(application_audio_combo_box), 
selected_row_id.c_str());
+
+    gtk_widget_set_hexpand(GTK_WIDGET(application_audio_combo_box), true);
+    gtk_grid_attach(grid, GTK_WIDGET(application_audio_combo_box), 0, 0, 1, 1);
+
+    GtkButton *remove_button = GTK_BUTTON(gtk_button_new_with_label("Remove"));
+    gtk_grid_attach(grid, GTK_WIDGET(remove_button), 1, 0, 1, 1);
+
+    g_signal_connect(remove_button, "clicked", G_CALLBACK(+[](GtkButton*, 
gpointer userdata){
+        GtkGrid *grid = (GtkGrid*)userdata;
+        
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(grid))), 
GTK_WIDGET(grid));
+        return true;
+    }), grid);
+
+    return GTK_WIDGET(grid);
+}
+
+static GtkWidget* create_application_audio_custom_row(const std::string &text) 
{
+    GtkGrid *grid = GTK_GRID(gtk_grid_new());
+    gtk_grid_set_column_spacing(grid, 10);
+    gtk_widget_set_hexpand(GTK_WIDGET(grid), true);
+
+    GtkEntry *application_audio_entry = GTK_ENTRY(gtk_entry_new());
+    gtk_widget_set_hexpand(GTK_WIDGET(application_audio_entry), true);
+    gtk_entry_set_text(application_audio_entry, text.c_str());
+    gtk_grid_attach(grid, GTK_WIDGET(application_audio_entry), 0, 0, 1, 1);
+
+    GtkButton *remove_button = GTK_BUTTON(gtk_button_new_with_label("Remove"));
+    gtk_grid_attach(grid, GTK_WIDGET(remove_button), 1, 0, 1, 1);
+
+    g_signal_connect(remove_button, "clicked", G_CALLBACK(+[](GtkButton*, 
gpointer userdata){
+        GtkGrid *grid = (GtkGrid*)userdata;
+        
gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(grid))), 
GTK_WIDGET(grid));
+        return true;
+    }), grid);
+
+    return GTK_WIDGET(grid);
+}
+
 static bool is_directory(const char *filepath) {
     struct stat file_stat;
     memset(&file_stat, 0, sizeof(file_stat));
@@ -863,12 +876,33 @@
     config.main_config.fps = gtk_spin_button_get_value_as_int(fps_entry);
     config.main_config.video_bitrate = 
gtk_spin_button_get_value_as_int(video_bitrate_entry);
     config.main_config.merge_audio_tracks = 
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button));
+    config.main_config.record_app_audio_inverted = 
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button));
     config.main_config.change_video_resolution = 
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(change_video_resolution_button));
 
+    config.main_config.audio_type_view.clear();
+    
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(audio_devices_radio_button)))
+        config.main_config.audio_type_view = "audio_devices";
+    else 
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(application_audio_radio_button)))
+        config.main_config.audio_type_view = "app_audio";
+
     config.main_config.audio_input.clear();
-    for_each_used_audio_input(GTK_LIST_BOX(audio_input_used_list), [](const 
AudioRow *audio_row) {
-        
config.main_config.audio_input.push_back(gtk_combo_box_text_get_active_text(audio_row->input_list));
-    });
+    gtk_container_foreach(GTK_CONTAINER(audio_devices_items_box), [](GtkWidget 
*widget, gpointer) {
+        GtkWidget *row_item_widget = gtk_grid_get_child_at(GTK_GRID(widget), 
0, 0);
+        if(GTK_IS_COMBO_BOX_TEXT(row_item_widget))
+            
config.main_config.audio_input.push_back(gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(row_item_widget)));
+    }, nullptr);
+
+    config.main_config.application_audio.clear();
+    gtk_container_foreach(GTK_CONTAINER(application_audio_items_box), 
[](GtkWidget *widget, gpointer) {
+        GtkWidget *row_item_widget = gtk_grid_get_child_at(GTK_GRID(widget), 
0, 0);
+        const char *text = "";
+        if(GTK_IS_COMBO_BOX_TEXT(row_item_widget))
+            text = gtk_combo_box_get_active_id(GTK_COMBO_BOX(row_item_widget));
+        else if(GTK_IS_ENTRY(row_item_widget))
+            text = gtk_entry_get_text(GTK_ENTRY(row_item_widget));
+        config.main_config.application_audio.push_back(text);
+    }, nullptr);
+
     config.main_config.color_range = 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(color_range_input_menu));
     config.main_config.quality = 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(quality_input_menu));
     config.main_config.codec = video_codec_selection_menu_get_active_id();
@@ -1445,9 +1479,12 @@
     show_bugged_driver_warning();
 
     int num_audio_tracks = 0;
-    for_each_used_audio_input(GTK_LIST_BOX(audio_input_used_list), 
[&num_audio_tracks](const AudioRow*) {
-        ++num_audio_tracks;
-    });
+    gtk_container_foreach(GTK_CONTAINER(audio_devices_items_box), [](GtkWidget 
*widget, gpointer userdata) {
+        int &num_audio_tracks = *(int*)userdata;
+        GtkWidget *row_item_widget = gtk_grid_get_child_at(GTK_GRID(widget), 
0, 0);
+        if(GTK_IS_COMBO_BOX_TEXT(row_item_widget))
+            ++num_audio_tracks;
+    }, &num_audio_tracks);
 
     if(num_audio_tracks > 1 && 
!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button))) {
         GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), 
GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
@@ -1536,20 +1573,81 @@
     return exit_success;
 }
 
-static void add_audio_command_line_args(std::vector<const char*> &args, 
std::string &merge_audio_tracks_arg_value) {
+struct ApplicationAudioCallbackUserdata {
+    const char *arg_option;
+    std::vector<const char*> &args;
+};
+
+static void add_audio_devices_command_line_args(std::vector<const char*> 
&args, std::string &merge_audio_tracks_arg_value) {
+    
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button))) {
+        gtk_container_foreach(GTK_CONTAINER(audio_devices_items_box), 
[](GtkWidget *widget, gpointer userdata) {
+            std::string &merge_audio_tracks_arg_value = 
*(std::string*)userdata;
+            GtkWidget *row_item_widget = 
gtk_grid_get_child_at(GTK_GRID(widget), 0, 0);
+
+            if(GTK_IS_COMBO_BOX_TEXT(row_item_widget)) {
+                if(!merge_audio_tracks_arg_value.empty())
+                    merge_audio_tracks_arg_value += '|';
+                merge_audio_tracks_arg_value += 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(row_item_widget));
+            }
+        }, &merge_audio_tracks_arg_value);
+
+        if(!merge_audio_tracks_arg_value.empty())
+            args.insert(args.end(), { "-a", 
merge_audio_tracks_arg_value.c_str() });
+    } else {
+        gtk_container_foreach(GTK_CONTAINER(audio_devices_items_box), 
[](GtkWidget *widget, gpointer userdata) {
+            std::vector<const char*> &args = *(std::vector<const 
char*>*)userdata;
+            GtkWidget *row_item_widget = 
gtk_grid_get_child_at(GTK_GRID(widget), 0, 0);
+
+            if(GTK_IS_COMBO_BOX_TEXT(row_item_widget))
+                args.insert(args.end(), { "-a", 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(row_item_widget)) });
+        }, &args);
+    }
+}
+
+static void add_application_audio_command_line_args(std::vector<const char*> 
&args, std::string &merge_audio_tracks_arg_value) {
+    const char *arg_option = 
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button))
 ? "-aai" : "-aa";
+    ApplicationAudioCallbackUserdata app_audio_callback = {
+        arg_option,
+        args
+    };
+
     
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button))) {
-        for_each_used_audio_input(GTK_LIST_BOX(audio_input_used_list), 
[&merge_audio_tracks_arg_value](const AudioRow *audio_row) {
+        gtk_container_foreach(GTK_CONTAINER(application_audio_items_box), 
[](GtkWidget *widget, gpointer userdata) {
+            std::string &merge_audio_tracks_arg_value = 
*(std::string*)userdata;
+            GtkWidget *row_item_widget = 
gtk_grid_get_child_at(GTK_GRID(widget), 0, 0);
+
+            const char *text = "";
+            if(GTK_IS_COMBO_BOX_TEXT(row_item_widget))
+                text = 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(row_item_widget));
+            else if(GTK_IS_ENTRY(row_item_widget))
+                text = gtk_entry_get_text(GTK_ENTRY(row_item_widget));
+
             if(!merge_audio_tracks_arg_value.empty())
                 merge_audio_tracks_arg_value += '|';
-            merge_audio_tracks_arg_value += 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(audio_row->input_list));
-        });
+            merge_audio_tracks_arg_value += text;
+        }, &merge_audio_tracks_arg_value);
 
         if(!merge_audio_tracks_arg_value.empty())
-            args.insert(args.end(), { "-a", 
merge_audio_tracks_arg_value.c_str() });
+            args.insert(args.end(), { arg_option, 
merge_audio_tracks_arg_value.c_str() });
     } else {
-        for_each_used_audio_input(GTK_LIST_BOX(audio_input_used_list), 
[&args](const AudioRow *audio_row) {
-            args.insert(args.end(), { "-a", 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(audio_row->input_list)) });
-        });
+        gtk_container_foreach(GTK_CONTAINER(application_audio_items_box), 
[](GtkWidget *widget, gpointer userdata) {
+            ApplicationAudioCallbackUserdata *app_audio_callback = 
(ApplicationAudioCallbackUserdata*)userdata;
+            GtkWidget *row_item_widget = 
gtk_grid_get_child_at(GTK_GRID(widget), 0, 0);
+            const char *text = "";
+            if(GTK_IS_COMBO_BOX_TEXT(row_item_widget))
+                text = 
gtk_combo_box_get_active_id(GTK_COMBO_BOX(row_item_widget));
+            else if(GTK_IS_ENTRY(row_item_widget))
+                text = gtk_entry_get_text(GTK_ENTRY(row_item_widget));
+            app_audio_callback->args.insert(app_audio_callback->args.end(), { 
app_audio_callback->arg_option, text });
+        }, &app_audio_callback);
+    }
+}
+
+static void add_audio_command_line_args(std::vector<const char*> &args, 
std::string &merge_audio_tracks_arg_value) {
+    
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(audio_devices_radio_button))) 
{
+        add_audio_devices_command_line_args(args, 
merge_audio_tracks_arg_value);
+    } else 
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(application_audio_radio_button)))
 {
+        add_application_audio_command_line_args(args, 
merge_audio_tracks_arg_value);
     }
 }
 
@@ -2189,9 +2287,7 @@
     gtk_widget_set_visible(GTK_WIDGET(audio_codec_grid), advanced_view);
     gtk_widget_set_visible(GTK_WIDGET(framerate_mode_grid), advanced_view);
     gtk_widget_set_visible(GTK_WIDGET(overclock_grid), advanced_view && 
gsr_info.gpu_info.vendor == GpuVendor::NVIDIA && 
gsr_info.system_info.display_server != DisplayServer::WAYLAND);
-    
gtk_widget_set_visible(GTK_WIDGET(show_recording_started_notification_button), 
advanced_view);
-    
gtk_widget_set_visible(GTK_WIDGET(show_recording_stopped_notification_button), 
advanced_view);
-    
gtk_widget_set_visible(GTK_WIDGET(show_recording_saved_notification_button), 
advanced_view);
+    gtk_widget_set_visible(GTK_WIDGET(notifications_frame), advanced_view);
 }
 
 static void quality_combo_box_change_callback(GtkComboBox *widget, gpointer 
userdata) {
@@ -2433,6 +2529,8 @@
             _gsr_info->system_info.display_server = DisplayServer::WAYLAND;
     } else if(attribute_name == "is_steam_deck") {
         _gsr_info->system_info.is_steam_deck = attribute_value == "yes";
+    } else if(attribute_name == "supports_app_audio") {
+        _gsr_info->system_info.supports_app_audio = attribute_value == "yes";
     }
 }
 
@@ -2609,23 +2707,40 @@
     g_free(id);
 }
 
+static void audio_devices_application_audio_radio_toggled(GtkButton *button, 
gpointer) {
+    if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
+        return;
+
+    if(GTK_WIDGET(button) == audio_devices_radio_button) {
+        gtk_widget_set_visible(GTK_WIDGET(audio_devices_grid), true);
+        gtk_widget_set_visible(GTK_WIDGET(application_audio_grid), false);
+    } else if(GTK_WIDGET(button) == application_audio_radio_button) {
+        gtk_widget_set_visible(GTK_WIDGET(audio_devices_grid), false);
+        gtk_widget_set_visible(GTK_WIDGET(application_audio_grid), true);
+    }
+}
+
 static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication 
*app) {
-    GtkGrid *grid = GTK_GRID(gtk_grid_new());
-    gtk_stack_add_named(stack, GTK_WIDGET(grid), "common-settings");
-    gtk_widget_set_vexpand(GTK_WIDGET(grid), true);
-    gtk_widget_set_hexpand(GTK_WIDGET(grid), true);
-    gtk_grid_set_row_spacing(grid, 10);
-    gtk_grid_set_column_spacing(grid, 10);
-    gtk_widget_set_margin(GTK_WIDGET(grid), 10, 10, 10, 10);
+    GtkGrid *main_grid = GTK_GRID(gtk_grid_new());
+    gtk_stack_add_named(stack, GTK_WIDGET(main_grid), "common-settings");
+    gtk_widget_set_vexpand(GTK_WIDGET(main_grid), true);
+    gtk_widget_set_hexpand(GTK_WIDGET(main_grid), true);
+    gtk_grid_set_row_spacing(main_grid, 10);
+    gtk_grid_set_column_spacing(main_grid, 10);
+    gtk_widget_set_margin(GTK_WIDGET(main_grid), 10, 10, 10, 10);
 
+    int main_grid_row = 0;
     int grid_row = 0;
     int record_area_row = 0;
     int audio_input_area_row = 0;
+    int video_input_area_row = 0;
+    int notifications_area_row = 0;
 
     GtkGrid *simple_advanced_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(simple_advanced_grid), 0, grid_row++, 2, 
1);
+    gtk_grid_attach(main_grid, GTK_WIDGET(simple_advanced_grid), 0, 
main_grid_row++, 2, 1);
     gtk_grid_attach(simple_advanced_grid, gtk_label_new("View: "), 0, 0, 1, 1);
     view_combo_box = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(view_combo_box, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     gtk_combo_box_text_append(view_combo_box, "simple", "Simple");
     gtk_combo_box_text_append(view_combo_box, "advanced", "Advanced");
     gtk_widget_set_hexpand(GTK_WIDGET(view_combo_box), true);
@@ -2633,8 +2748,27 @@
     gtk_combo_box_set_active(GTK_COMBO_BOX(view_combo_box), 0);
     g_signal_connect(view_combo_box, "changed", 
G_CALLBACK(view_combo_box_change_callback), view_combo_box);
 
-    GtkFrame *record_area_frame = GTK_FRAME(gtk_frame_new("Record area"));
-    gtk_grid_attach(grid, GTK_WIDGET(record_area_frame), 0, grid_row++, 2, 1);
+    GtkScrolledWindow *scrolled_window = 
GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
+    gtk_scrolled_window_set_min_content_width(scrolled_window, 650);
+    gtk_scrolled_window_set_min_content_height(scrolled_window, 300);
+    gtk_scrolled_window_set_max_content_width(scrolled_window, 650);
+    gtk_scrolled_window_set_max_content_height(scrolled_window, 800);
+    gtk_scrolled_window_set_propagate_natural_width(scrolled_window, true);
+    gtk_scrolled_window_set_propagate_natural_height(scrolled_window, true);
+    gtk_grid_attach(main_grid, GTK_WIDGET(scrolled_window), 0, 
main_grid_row++, 2, 1);
+
+    GtkGrid *grid = GTK_GRID(gtk_grid_new());
+    gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(grid));
+    gtk_widget_set_halign(GTK_WIDGET(grid), GTK_ALIGN_CENTER);
+    gtk_widget_set_valign(GTK_WIDGET(grid), GTK_ALIGN_START);
+    gtk_widget_set_vexpand(GTK_WIDGET(grid), true);
+    gtk_widget_set_hexpand(GTK_WIDGET(grid), true);
+    gtk_grid_set_row_spacing(grid, 10);
+    gtk_grid_set_column_spacing(grid, 10);
+    gtk_widget_set_margin(GTK_WIDGET(grid), 10, 10, 10, 10);
+
+    GtkFrame *capture_target_frame = GTK_FRAME(gtk_frame_new("Capture 
target"));
+    gtk_grid_attach(grid, GTK_WIDGET(capture_target_frame), 0, grid_row++, 2, 
1);
 
     GtkGrid *record_area_grid = GTK_GRID(gtk_grid_new());
     gtk_widget_set_vexpand(GTK_WIDGET(record_area_grid), false);
@@ -2642,7 +2776,7 @@
     gtk_grid_set_row_spacing(record_area_grid, 10);
     gtk_grid_set_column_spacing(record_area_grid, 10);
     gtk_widget_set_margin(GTK_WIDGET(record_area_grid), 10, 10, 10, 10);
-    gtk_container_add(GTK_CONTAINER(record_area_frame), 
GTK_WIDGET(record_area_grid));
+    gtk_container_add(GTK_CONTAINER(capture_target_frame), 
GTK_WIDGET(record_area_grid));
 
     GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
     GtkTreeIter iter;
@@ -2717,6 +2851,7 @@
     }
 
     record_area_selection_menu = 
GTK_COMBO_BOX(gtk_combo_box_new_with_model(record_area_selection_model));
+    g_signal_connect(record_area_selection_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
 
     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(record_area_selection_menu), 
renderer, TRUE);
@@ -2751,6 +2886,7 @@
         gtk_grid_attach(area_size_grid, GTK_WIDGET(video_resolution_label), 0, 
0, 3, 1);
 
         area_width_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 
10000.0, 1.0));
+        g_signal_connect(area_width_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
         gtk_spin_button_set_value(area_width_entry, 1920.0);
         gtk_widget_set_hexpand(GTK_WIDGET(area_width_entry), true);
         gtk_grid_attach(area_size_grid, GTK_WIDGET(area_width_entry), 0, 1, 1, 
1);
@@ -2758,6 +2894,7 @@
         gtk_grid_attach(area_size_grid, gtk_label_new("x"), 1, 1, 1, 1);
 
         area_height_entry = 
GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
+        g_signal_connect(area_height_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
         gtk_spin_button_set_value(area_height_entry, 1080.0);
         gtk_widget_set_hexpand(GTK_WIDGET(area_height_entry), true);
         gtk_grid_attach(area_size_grid, GTK_WIDGET(area_height_entry), 2, 1, 
1, 1);
@@ -2773,6 +2910,7 @@
         gtk_grid_attach(video_resolution_grid, 
GTK_WIDGET(video_resolution_label), 0, 0, 3, 1);
 
         video_width_entry = 
GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
+        g_signal_connect(video_width_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
         gtk_spin_button_set_value(video_width_entry, 1920.0);
         gtk_widget_set_hexpand(GTK_WIDGET(video_width_entry), true);
         gtk_grid_attach(video_resolution_grid, GTK_WIDGET(video_width_entry), 
0, 1, 1, 1);
@@ -2780,6 +2918,7 @@
         gtk_grid_attach(video_resolution_grid, gtk_label_new("x"), 1, 1, 1, 1);
 
         video_height_entry = 
GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
+        g_signal_connect(video_height_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
         gtk_spin_button_set_value(video_height_entry, 1080.0);
         gtk_widget_set_hexpand(GTK_WIDGET(video_height_entry), true);
         gtk_grid_attach(video_resolution_grid, GTK_WIDGET(video_height_entry), 
2, 1, 1, 1);
@@ -2801,52 +2940,118 @@
     gtk_widget_set_margin(GTK_WIDGET(audio_grid), 10, 10, 10, 10);
     gtk_container_add(GTK_CONTAINER(audio_input_frame), 
GTK_WIDGET(audio_grid));
 
-    GtkGrid *add_audio_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_set_row_spacing(add_audio_grid, 10);
-    gtk_grid_set_column_spacing(add_audio_grid, 10);
-    gtk_grid_attach(audio_grid, GTK_WIDGET(add_audio_grid), 0, 
audio_input_area_row++, 1, 1);
-
-    add_audio_input_button = gtk_button_new_with_label("Add audio track");
-    gtk_grid_attach(add_audio_grid, add_audio_input_button, 0, 0, 1, 1);
-    g_signal_connect(add_audio_input_button, "clicked", 
G_CALLBACK(+[](GtkButton*, gpointer){
-        GtkWidget *row = create_used_audio_input_row();
-        gtk_widget_show_all(row);
-        gtk_list_box_insert(GTK_LIST_BOX(audio_input_used_list), row, -1);
-        return true;
-    }), nullptr);
+    audio_type_radio_button_box = 
GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10));
+    gtk_grid_attach(audio_grid, GTK_WIDGET(audio_type_radio_button_box), 0, 
audio_input_area_row++, 2, 1);
+
+    audio_devices_radio_button = 
gtk_radio_button_new_with_label_from_widget(nullptr, "Audio devices");
+    gtk_box_pack_start(audio_type_radio_button_box, 
audio_devices_radio_button, false, false, 0);
+    g_signal_connect(audio_devices_radio_button, "toggled", 
G_CALLBACK(audio_devices_application_audio_radio_toggled), nullptr);
+
+    application_audio_radio_button = 
gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(audio_devices_radio_button),
 "Application audio");
+    gtk_box_pack_start(audio_type_radio_button_box, 
GTK_WIDGET(application_audio_radio_button), false, false, 0);
+    g_signal_connect(application_audio_radio_button, "toggled", 
G_CALLBACK(audio_devices_application_audio_radio_toggled), nullptr);
+
+    
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(audio_devices_radio_button), 
true);
+
+    {
+        int audio_devices_row = 0;
+
+        audio_devices_grid = GTK_GRID(gtk_grid_new());
+        gtk_grid_set_row_spacing(audio_devices_grid, 10);
+        gtk_grid_set_column_spacing(audio_devices_grid, 10);
+        gtk_widget_set_margin(GTK_WIDGET(audio_devices_grid), 0, 0, 0, 0);
+        gtk_grid_attach(audio_grid, GTK_WIDGET(audio_devices_grid), 0, 
audio_input_area_row++, 2, 1);
+
+        GtkGrid *add_audio_grid = GTK_GRID(gtk_grid_new());
+        gtk_grid_set_row_spacing(add_audio_grid, 10);
+        gtk_grid_set_column_spacing(add_audio_grid, 10);
+        gtk_grid_attach(audio_devices_grid, GTK_WIDGET(add_audio_grid), 0, 
audio_devices_row++, 1, 1);
+
+        GtkWidget *add_audio_device_button = gtk_button_new_with_label("Add 
audio device");
+        gtk_grid_attach(add_audio_grid, add_audio_device_button, 0, 0, 1, 1);
+        g_signal_connect(add_audio_device_button, "clicked", 
G_CALLBACK(+[](GtkButton*, gpointer){
+            GtkWidget *row = create_audio_device_combo_box_row("");
+            gtk_widget_show_all(row);
+            gtk_box_pack_start(audio_devices_items_box, row, false, false, 0);
+            return true;
+        }), nullptr);
+
+        audio_devices_items_box = 
GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 10));
+        gtk_grid_attach(audio_devices_grid, 
GTK_WIDGET(audio_devices_items_box), 0, audio_devices_row++, 2, 1);
+    }
 
-    audio_input_used_list = gtk_list_box_new();
-    gtk_widget_set_hexpand (audio_input_used_list, TRUE);
-    gtk_list_box_set_selection_mode (GTK_LIST_BOX (audio_input_used_list), 
GTK_SELECTION_NONE);
-    gtk_grid_attach(audio_grid, audio_input_used_list, 0, 
audio_input_area_row++, 2, 1);
+    {
+        int application_audio_row = 0;
+
+        application_audio_grid = GTK_GRID(gtk_grid_new());
+        gtk_grid_set_row_spacing(application_audio_grid, 10);
+        gtk_grid_set_column_spacing(application_audio_grid, 10);
+        gtk_widget_set_margin(GTK_WIDGET(application_audio_grid), 0, 0, 0, 0);
+        gtk_grid_attach(audio_grid, GTK_WIDGET(application_audio_grid), 0, 
audio_input_area_row++, 2, 1);
+
+        GtkGrid *add_button_grid = GTK_GRID(gtk_grid_new());
+        gtk_grid_set_column_spacing(add_button_grid, 10);
+        gtk_grid_attach(application_audio_grid, GTK_WIDGET(add_button_grid), 
0, application_audio_row++, 2, 1);
+
+        GtkWidget *add_application_audio_button = 
gtk_button_new_with_label("Add application audio");
+        gtk_grid_attach(add_button_grid, add_application_audio_button, 0, 0, 
1, 1);
+        g_signal_connect(add_application_audio_button, "clicked", 
G_CALLBACK(+[](GtkButton*, gpointer){
+            GtkWidget *row = create_application_audio_combo_box_row("");
+            gtk_widget_show_all(row);
+            gtk_box_pack_start(application_audio_items_box, row, false, false, 
0);
+            return true;
+        }), nullptr);
+
+        GtkWidget *add_custom_application_audio_button = 
gtk_button_new_with_label("Add custom application audio");
+        gtk_grid_attach(add_button_grid, add_custom_application_audio_button, 
1, 0, 1, 1);
+        g_signal_connect(add_custom_application_audio_button, "clicked", 
G_CALLBACK(+[](GtkButton*, gpointer){
+            GtkWidget *row = create_application_audio_custom_row("");
+            gtk_widget_show_all(row);
+            gtk_box_pack_start(application_audio_items_box, row, false, false, 
0);
+            return true;
+        }), nullptr);
+
+        application_audio_items_box = 
GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 10));
+        gtk_grid_attach(application_audio_grid, 
GTK_WIDGET(application_audio_items_box), 0, application_audio_row++, 2, 1);
+
+        record_app_audio_inverted_button = 
gtk_check_button_new_with_label("Record audio from all applications except the 
selected ones");
+        
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button),
 false);
+        gtk_widget_set_halign(record_app_audio_inverted_button, 
GTK_ALIGN_START);
+        gtk_grid_attach(application_audio_grid, 
record_app_audio_inverted_button, 0, application_audio_row++, 2, 1);
+    }
 
     merge_audio_tracks_button = gtk_check_button_new_with_label("Merge audio 
tracks");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button), 
true);
     gtk_widget_set_halign(merge_audio_tracks_button, GTK_ALIGN_START);
     gtk_grid_attach(audio_grid, merge_audio_tracks_button, 0, 
audio_input_area_row++, 2, 1);
 
-    GtkGrid *fps_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(fps_grid), 0, grid_row++, 2, 1);
-    gtk_grid_attach(fps_grid, gtk_label_new("Frame rate: "), 0, 0, 1, 1);
-    fps_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1.0, 5000.0, 
1.0));
-    gtk_spin_button_set_value(fps_entry, 60.0);
-    gtk_widget_set_hexpand(GTK_WIDGET(fps_entry), true);
-    gtk_grid_attach(fps_grid, GTK_WIDGET(fps_entry), 1, 0, 1, 1);
+    audio_codec_grid = GTK_GRID(gtk_grid_new());
+    gtk_grid_attach(audio_grid, GTK_WIDGET(audio_codec_grid), 0, 
audio_input_area_row++, 2, 1);
+    gtk_grid_attach(audio_codec_grid, gtk_label_new("Audio codec: "), 0, 0, 1, 
1);
+    audio_codec_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(audio_codec_input_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
+    gtk_combo_box_text_append(audio_codec_input_menu, "opus", "Opus 
(Recommended)");
+    gtk_combo_box_text_append(audio_codec_input_menu, "aac", "AAC");
+    gtk_widget_set_hexpand(GTK_WIDGET(audio_codec_input_menu), true);
+    gtk_grid_attach(audio_codec_grid, GTK_WIDGET(audio_codec_input_menu), 1, 
0, 1, 1);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(audio_codec_input_menu), 0);
 
-    color_range_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(color_range_grid), 0, grid_row++, 2, 1);
-    gtk_grid_attach(color_range_grid, gtk_label_new("Color range: "), 0, 0, 1, 
1);
-    color_range_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-    gtk_combo_box_text_append(color_range_input_menu, "limited", "Limited");
-    gtk_combo_box_text_append(color_range_input_menu, "full", "Full");
-    gtk_widget_set_hexpand(GTK_WIDGET(color_range_input_menu), true);
-    gtk_grid_attach(color_range_grid, GTK_WIDGET(color_range_input_menu), 1, 
0, 1, 1);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(color_range_input_menu), 0);
+    GtkFrame *video_input_frame = GTK_FRAME(gtk_frame_new("Video"));
+    gtk_grid_attach(grid, GTK_WIDGET(video_input_frame), 0, grid_row++, 2, 1);
+
+    GtkGrid *video_grid = GTK_GRID(gtk_grid_new());
+    gtk_widget_set_vexpand(GTK_WIDGET(video_grid), false);
+    gtk_widget_set_hexpand(GTK_WIDGET(video_grid), true);
+    gtk_grid_set_row_spacing(video_grid, 10);
+    gtk_grid_set_column_spacing(video_grid, 10);
+    gtk_widget_set_margin(GTK_WIDGET(video_grid), 10, 10, 10, 10);
+    gtk_container_add(GTK_CONTAINER(video_input_frame), 
GTK_WIDGET(video_grid));
 
     GtkGrid *video_quality_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(video_quality_grid), 0, grid_row++, 2, 1);
+    gtk_grid_attach(video_grid, GTK_WIDGET(video_quality_grid), 0, 
video_input_area_row++, 2, 1);
     gtk_grid_attach(video_quality_grid, gtk_label_new("Video quality: "), 0, 
0, 1, 1);
     quality_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(quality_input_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     gtk_combo_box_text_append(quality_input_menu, "custom", "Constant bitrate 
(Recommended for live streaming and replay)");
     gtk_combo_box_text_append(quality_input_menu, "medium", "Medium");
     gtk_combo_box_text_append(quality_input_menu, "high", "High");
@@ -2858,15 +3063,16 @@
     g_signal_connect(quality_input_menu, "changed", 
G_CALLBACK(quality_combo_box_change_callback), quality_input_menu);
 
     video_bitrate_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(video_bitrate_grid), 0, grid_row++, 2, 1);
+    gtk_grid_attach(video_grid, GTK_WIDGET(video_bitrate_grid), 0, 
video_input_area_row++, 2, 1);
     gtk_grid_attach(video_bitrate_grid, gtk_label_new("Video bitrate (kbps): 
"), 0, 0, 1, 1);
     video_bitrate_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1.0, 
500000.0, 1.0));
+    g_signal_connect(video_bitrate_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     gtk_spin_button_set_value(video_bitrate_entry, 15000.0);
     gtk_widget_set_hexpand(GTK_WIDGET(video_bitrate_entry), true);
     gtk_grid_attach(video_bitrate_grid, GTK_WIDGET(video_bitrate_entry), 1, 0, 
1, 1);
 
     video_codec_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(video_codec_grid), 0, grid_row++, 2, 1);
+    gtk_grid_attach(video_grid, GTK_WIDGET(video_codec_grid), 0, 
video_input_area_row++, 2, 1);
     gtk_grid_attach(video_codec_grid, gtk_label_new("Video codec: "), 0, 0, 1, 
1);
 
     {
@@ -2928,6 +3134,7 @@
         gtk_list_store_set(store, &iter, 1, "h264_software", -1);
 
         video_codec_selection_menu = 
GTK_COMBO_BOX(gtk_combo_box_new_with_model(video_codec_selection_model));
+        g_signal_connect(video_codec_selection_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
 
         renderer = gtk_cell_renderer_text_new();
         
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(video_codec_selection_menu), 
renderer, TRUE);
@@ -2940,20 +3147,31 @@
         gtk_grid_attach(video_codec_grid, 
GTK_WIDGET(video_codec_selection_menu), 1, 0, 1, 1);
     }
 
-    audio_codec_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(audio_codec_grid), 0, grid_row++, 2, 1);
-    gtk_grid_attach(audio_codec_grid, gtk_label_new("Audio codec: "), 0, 0, 1, 
1);
-    audio_codec_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
-    gtk_combo_box_text_append(audio_codec_input_menu, "opus", "Opus 
(Recommended)");
-    gtk_combo_box_text_append(audio_codec_input_menu, "aac", "AAC");
-    gtk_widget_set_hexpand(GTK_WIDGET(audio_codec_input_menu), true);
-    gtk_grid_attach(audio_codec_grid, GTK_WIDGET(audio_codec_input_menu), 1, 
0, 1, 1);
-    gtk_combo_box_set_active(GTK_COMBO_BOX(audio_codec_input_menu), 0);
+    color_range_grid = GTK_GRID(gtk_grid_new());
+    gtk_grid_attach(video_grid, GTK_WIDGET(color_range_grid), 0, 
video_input_area_row++, 2, 1);
+    gtk_grid_attach(color_range_grid, gtk_label_new("Color range: "), 0, 0, 1, 
1);
+    color_range_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(color_range_input_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
+    gtk_combo_box_text_append(color_range_input_menu, "limited", "Limited");
+    gtk_combo_box_text_append(color_range_input_menu, "full", "Full");
+    gtk_widget_set_hexpand(GTK_WIDGET(color_range_input_menu), true);
+    gtk_grid_attach(color_range_grid, GTK_WIDGET(color_range_input_menu), 1, 
0, 1, 1);
+    gtk_combo_box_set_active(GTK_COMBO_BOX(color_range_input_menu), 0);
+
+    GtkGrid *fps_grid = GTK_GRID(gtk_grid_new());
+    gtk_grid_attach(video_grid, GTK_WIDGET(fps_grid), 0, 
video_input_area_row++, 2, 1);
+    gtk_grid_attach(fps_grid, gtk_label_new("Frame rate: "), 0, 0, 1, 1);
+    fps_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(1.0, 5000.0, 
1.0));
+    g_signal_connect(fps_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
+    gtk_spin_button_set_value(fps_entry, 60.0);
+    gtk_widget_set_hexpand(GTK_WIDGET(fps_entry), true);
+    gtk_grid_attach(fps_grid, GTK_WIDGET(fps_entry), 1, 0, 1, 1);
 
     framerate_mode_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(framerate_mode_grid), 0, grid_row++, 2, 
1);
+    gtk_grid_attach(video_grid, GTK_WIDGET(framerate_mode_grid), 0, 
video_input_area_row++, 2, 1);
     gtk_grid_attach(framerate_mode_grid, gtk_label_new("Frame rate mode: "), 
0, 0, 1, 1);
     framerate_mode_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(framerate_mode_input_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     gtk_combo_box_text_append(framerate_mode_input_menu, "auto", "Auto 
(Recommended)");
     gtk_combo_box_text_append(framerate_mode_input_menu, "cfr", "Constant");
     gtk_combo_box_text_append(framerate_mode_input_menu, "vfr", "Variable");
@@ -2962,7 +3180,7 @@
     gtk_combo_box_set_active(GTK_COMBO_BOX(framerate_mode_input_menu), 0);
 
     overclock_grid = GTK_GRID(gtk_grid_new());
-    gtk_grid_attach(grid, GTK_WIDGET(overclock_grid), 0, grid_row++, 2, 1);
+    gtk_grid_attach(video_grid, GTK_WIDGET(overclock_grid), 0, 
video_input_area_row++, 2, 1);
     overclock_button = gtk_check_button_new_with_label("Overclock memory 
transfer rate to workaround NVIDIA driver performance bug");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(overclock_button), false);
     gtk_widget_set_halign(overclock_button, GTK_ALIGN_START);
@@ -2991,22 +3209,33 @@
     record_cursor_button = gtk_check_button_new_with_label("Record cursor");
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(record_cursor_button), 
true);
     gtk_widget_set_halign(record_cursor_button, GTK_ALIGN_START);
-    gtk_grid_attach(grid, record_cursor_button, 0, grid_row++, 2, 1);
+    gtk_grid_attach(video_grid, record_cursor_button, 0, 
video_input_area_row++, 2, 1);
+
+    notifications_frame = GTK_FRAME(gtk_frame_new("Notifications"));
+    gtk_grid_attach(grid, GTK_WIDGET(notifications_frame), 0, grid_row++, 2, 
1);
+
+    GtkGrid *notifications_grid = GTK_GRID(gtk_grid_new());
+    gtk_widget_set_vexpand(GTK_WIDGET(notifications_grid), false);
+    gtk_widget_set_hexpand(GTK_WIDGET(notifications_grid), true);
+    gtk_grid_set_row_spacing(notifications_grid, 10);
+    gtk_grid_set_column_spacing(notifications_grid, 10);
+    gtk_widget_set_margin(GTK_WIDGET(notifications_grid), 10, 10, 10, 10);
+    gtk_container_add(GTK_CONTAINER(notifications_frame), 
GTK_WIDGET(notifications_grid));
 
     show_recording_started_notification_button = 
gtk_check_button_new_with_label("Show recording/streaming/replay started 
notification");
     
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_recording_started_notification_button),
 false);
     gtk_widget_set_halign(show_recording_started_notification_button, 
GTK_ALIGN_START);
-    gtk_grid_attach(grid, show_recording_started_notification_button, 0, 
grid_row++, 2, 1);
+    gtk_grid_attach(notifications_grid, 
show_recording_started_notification_button, 0, notifications_area_row++, 2, 1);
 
     show_recording_stopped_notification_button = 
gtk_check_button_new_with_label("Show streaming/replay stopped notification");
     
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_recording_stopped_notification_button),
 false);
     gtk_widget_set_halign(show_recording_stopped_notification_button, 
GTK_ALIGN_START);
-    gtk_grid_attach(grid, show_recording_stopped_notification_button, 0, 
grid_row++, 2, 1);
+    gtk_grid_attach(notifications_grid, 
show_recording_stopped_notification_button, 0, notifications_area_row++, 2, 1);
 
     show_recording_saved_notification_button = 
gtk_check_button_new_with_label("Show video saved notification");
     
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_recording_saved_notification_button),
 true);
     gtk_widget_set_halign(show_recording_saved_notification_button, 
GTK_ALIGN_START);
-    gtk_grid_attach(grid, show_recording_saved_notification_button, 0, 
grid_row++, 2, 1);
+    gtk_grid_attach(notifications_grid, 
show_recording_saved_notification_button, 0, notifications_area_row++, 2, 1);
 
     GtkGrid *start_button_grid = GTK_GRID(gtk_grid_new());
     gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, grid_row++, 2, 1);
@@ -3040,7 +3269,7 @@
     gtk_widget_set_sensitive(GTK_WIDGET(record_button), false);
     gtk_widget_set_sensitive(GTK_WIDGET(stream_button), false);
 
-    return GTK_WIDGET(grid);
+    return GTK_WIDGET(main_grid);
 }
 
 static void replace_meta_with_super(std::string &str) {
@@ -3218,6 +3447,7 @@
     gtk_grid_attach(grid, GTK_WIDGET(container_grid), 0, row++, num_columns, 
1);
     gtk_grid_attach(container_grid, gtk_label_new("Container: "), 0, 0, 1, 1);
     replay_container = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(replay_container, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     for(auto &supported_container : supported_containers) {
         gtk_combo_box_text_append(replay_container, 
supported_container.container_name, supported_container.file_extension);
     }
@@ -3232,6 +3462,7 @@
     gtk_grid_attach(grid, GTK_WIDGET(replay_time_grid), 0, row++, num_columns, 
1);
     gtk_grid_attach(replay_time_grid, gtk_label_new("Replay time in seconds: 
"), 0, 0, 1, 1);
     replay_time_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 
1200.0, 1.0));
+    g_signal_connect(replay_time_entry, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     gtk_spin_button_set_value(replay_time_entry, 30.0);
     gtk_widget_set_hexpand(GTK_WIDGET(replay_time_entry), true);
     gtk_grid_attach(replay_time_grid, GTK_WIDGET(replay_time_entry), 1, 0, 1, 
1);
@@ -3385,6 +3616,7 @@
     gtk_grid_attach(grid, GTK_WIDGET(container_grid), 0, row++, num_columns, 
1);
     gtk_grid_attach(container_grid, gtk_label_new("Container: "), 0, 0, 1, 1);
     record_container = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(record_container, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     for(auto &supported_container : supported_containers) {
         gtk_combo_box_text_append(record_container, 
supported_container.container_name, supported_container.file_extension);
     }
@@ -3520,6 +3752,7 @@
     gtk_grid_attach(grid, GTK_WIDGET(stream_service_grid), 0, row++, 
num_columns, 1);
     gtk_grid_attach(stream_service_grid, gtk_label_new("Stream service: "), 0, 
0, 1, 1);
     stream_service_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(stream_service_input_menu, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     gtk_combo_box_text_append(stream_service_input_menu, "twitch", "Twitch");
     gtk_combo_box_text_append(stream_service_input_menu, "youtube", "Youtube");
     gtk_combo_box_text_append(stream_service_input_menu, "custom", "Custom");
@@ -3550,6 +3783,7 @@
     gtk_grid_attach(grid, GTK_WIDGET(custom_stream_container_grid), 0, row++, 
num_columns, 1);
     gtk_grid_attach(custom_stream_container_grid, gtk_label_new("Container: 
"), 0, 0, 1, 1);
     custom_stream_container = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
+    g_signal_connect(custom_stream_container, "scroll-event", 
G_CALLBACK(scroll_event_ignore), NULL);
     for(auto &supported_container : supported_containers) {
         gtk_combo_box_text_append(custom_stream_container, 
supported_container.container_name, supported_container.file_extension);
     }
@@ -3692,17 +3926,12 @@
     return G_SOURCE_CONTINUE;
 }
 
-static void add_audio_input_track(const char *name) {
-    GtkWidget *row = create_used_audio_input_row();
-
-    const AudioRow *audio_row = (AudioRow*)g_object_get_data(G_OBJECT(row), 
"audio-row");
-    std::string audio_id;
-    gint target_combo_box_index = 
combo_box_text_get_row_by_label(GTK_COMBO_BOX(audio_row->input_list), name, 
audio_id);
-    if(target_combo_box_index != -1)
-        gtk_combo_box_set_active(GTK_COMBO_BOX(audio_row->input_list), 
target_combo_box_index);
-
-    gtk_widget_show_all(row);
-    gtk_list_box_insert (GTK_LIST_BOX(audio_input_used_list), row, -1);
+static const std::string* get_application_audio_by_name_case_insensitive(const 
std::vector<std::string> &application_audio, const std::string &name) {
+    for(const auto &app_audio : application_audio) {
+        if(strcasecmp(app_audio.c_str(), name.c_str()) == 0)
+            return &app_audio;
+    }
+    return nullptr;
 }
 
 static void load_config() {
@@ -3800,14 +4029,32 @@
     gtk_spin_button_set_value(fps_entry, config.main_config.fps);
     gtk_spin_button_set_value(video_bitrate_entry, 
config.main_config.video_bitrate);
     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button), 
config.main_config.merge_audio_tracks);
+    
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button),
 config.main_config.record_app_audio_inverted);
     
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(change_video_resolution_button), 
config.main_config.change_video_resolution);
 
     for(const std::string &audio_input : config.main_config.audio_input) {
-        add_audio_input_track(audio_input.c_str());
+        GtkWidget *row = create_audio_device_combo_box_row(audio_input);
+        gtk_widget_show_all(row);
+        gtk_box_pack_start(audio_devices_items_box, row, false, false, 0);
+    }
+
+    if(config_empty && config.main_config.audio_input.empty()) {
+        GtkWidget *row = create_audio_device_combo_box_row("Default output");
+        gtk_widget_show_all(row);
+        gtk_box_pack_start(audio_devices_items_box, row, false, false, 0);
     }
 
-    if(config_empty && config.main_config.audio_input.empty())
-        add_audio_input_track("Default output");
+    for(const std::string &app_audio : config.main_config.application_audio) {
+        const std::string *app_audio_existing = 
get_application_audio_by_name_case_insensitive(application_audio, app_audio);
+        GtkWidget *row = nullptr;
+        if(app_audio_existing)
+            row = create_application_audio_combo_box_row(*app_audio_existing);
+        else
+            row = create_application_audio_custom_row(app_audio);
+
+        gtk_widget_show_all(row);
+        gtk_box_pack_start(application_audio_items_box, row, false, false, 0);
+    }
 
     gtk_combo_box_set_active_id(GTK_COMBO_BOX(color_range_input_menu), 
config.main_config.color_range.c_str());
     gtk_combo_box_set_active_id(GTK_COMBO_BOX(quality_input_menu), 
config.main_config.quality.c_str());
@@ -3858,6 +4105,19 @@
 
     
on_change_video_resolution_button_click(GTK_BUTTON(change_video_resolution_button),
 nullptr);
 
+    if(!gsr_info.system_info.supports_app_audio) {
+        gtk_widget_set_visible(GTK_WIDGET(audio_type_radio_button_box), false);
+        gtk_widget_set_visible(GTK_WIDGET(application_audio_grid), false);
+    }
+
+    if(config.main_config.audio_type_view == "app_audio" && 
gsr_info.system_info.supports_app_audio) {
+        
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(application_audio_radio_button), 
true);
+        
audio_devices_application_audio_radio_toggled(GTK_BUTTON(application_audio_radio_button),
 nullptr);
+    } else /*if(config.main_config.audio_type_view == "audio_devices") */{
+        
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(audio_devices_radio_button), 
true);
+        
audio_devices_application_audio_radio_toggled(GTK_BUTTON(audio_devices_radio_button),
 nullptr);
+    }
+
     std::string dummy;
     if(!config.main_config.software_encoding_warning_shown && 
!switch_video_codec_to_usable_hardware_encoder(dummy)) {
         GtkWidget *dialog = 
gtk_message_dialog_new_with_markup(GTK_WINDOW(window), GTK_DIALOG_MODAL, 
GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
@@ -4004,8 +4264,24 @@
     gtk_window_set_title(GTK_WINDOW(window), window_title.c_str());
     gtk_window_set_resizable(GTK_WINDOW(window), false);
 
+    GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
+#ifdef GSR_ICONS_PATH
+    const char *icon_path = GSR_ICONS_PATH;
+#else
+    const char *icon_path = "/usr/share/icons";
+#endif
+    gtk_icon_theme_set_search_path(icon_theme, &icon_path, 1);
+
+    const char *icon_name = "com.dec05eba.gpu_screen_recorder";
+    if(!gtk_icon_theme_has_icon(icon_theme, icon_name))
+        fprintf(stderr, "Error: failed to find icon %s in %s\n", icon_name, 
icon_path);
+
+    gtk_window_set_default_icon_name(icon_name);
+    gtk_window_set_icon_name(GTK_WINDOW(window), icon_name);
+
     select_window_userdata.app = app;
     audio_inputs = get_audio_devices();
+    application_audio = get_application_audio();
 
     if(gsr_info.system_info.display_server != DisplayServer::WAYLAND)
         crosshair_cursor = XCreateFontCursor(gdk_x11_get_default_xdisplay(), 
XC_crosshair);
@@ -4092,7 +4368,12 @@
         }
     }
 
-    GtkApplication *app = 
gtk_application_new("com.dec05eba.gpu_screen_recorder", 
G_APPLICATION_NON_UNIQUE);
+    char app_id[] = "com.dec05eba.gpu_screen_recorder";
+    // Gtk sets wayland app id / x11 wm class from the binary name, so we 
override it here.
+    // This is needed for the correct window icon on wayland (app id needs to 
match the desktop file name).
+    argv[0] = app_id;
+
+    GtkApplication *app = gtk_application_new(app_id, 
G_APPLICATION_NON_UNIQUE);
     g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
     int status = g_application_run(G_APPLICATION(app), argc, argv);
     g_object_unref(app);

Reply via email to