Files

137 lines
5.7 KiB
C

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
#include "native_applications.h"
#include <atspi/atspi.h>
// Used only in the isolated preferences helper. The reader's AT-SPI loop
// and timeout remain untouched.
AccessibleApplication *prefs_application_names(int *count) {
*count = 0;
if (atspi_init() != 0) return NULL;
atspi_set_timeout(250, 250);
AtspiAccessible *desktop = atspi_get_desktop(0);
if (!desktop) { atspi_exit(); return NULL; }
GError *error = NULL;
int children = MIN(atspi_accessible_get_child_count(desktop, &error), 4096);
g_clear_error(&error);
AccessibleApplication *names = g_new0(AccessibleApplication, MAX(children, 1));
for (int i = 0; i < children; i++) {
AtspiAccessible *application = atspi_accessible_get_child_at_index(desktop, i, &error);
g_clear_error(&error);
if (!application) continue;
char *name = atspi_accessible_get_name(application, &error);
g_clear_error(&error);
unsigned int pid = atspi_accessible_get_process_id(application, &error);
g_clear_error(&error);
if ((name && *name) || pid) names[(*count)++] = (AccessibleApplication){name, pid};
else g_free(name);
g_object_unref(application);
}
g_object_unref(desktop);
atspi_exit();
return names;
}
void prefs_free_application_names(AccessibleApplication *names, int count) {
for (int i = 0; i < count; i++) g_free(names[i].name);
g_free(names);
}
static GtkWidget *chooser, *applicationTree;
static GtkListStore *applicationStore;
static int chooserAction;
static void chooser_response(GtkDialog *dialog, gint response, gpointer data) {
(void)dialog; (void)data;
chooserAction = response == GTK_RESPONSE_OK ? 1 : 2;
}
static void chooser_activate(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) {
(void)view; (void)path; (void)column; (void)data;
chooserAction = 1;
}
static gboolean chooser_delete(GtkWidget *widget, GdkEvent *event, gpointer data) {
(void)widget; (void)event; (void)data;
chooserAction = 2;
return TRUE;
}
static gboolean chooser_key(GtkWidget *widget, GdkEventKey *event, gpointer data) {
(void)widget; (void)data;
if (event->keyval != GDK_KEY_Escape) return FALSE;
chooserAction = 2;
return TRUE;
}
int prefs_application_chooser_init(void) {
if (!gtk_init_check(NULL, NULL)) return 0;
chooserAction = 0;
chooser = gtk_dialog_new_with_buttons("Choose Application Preferences", NULL, 0,
"_Cancel", GTK_RESPONSE_CANCEL, "_Open Preferences", GTK_RESPONSE_OK, NULL);
gtk_window_set_default_size(GTK_WINDOW(chooser), 480, 420);
gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_OK);
GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(chooser));
GtkWidget *label = gtk_label_new_with_mnemonic("Choose an _application:");
gtk_box_pack_start(GTK_BOX(content), label, FALSE, FALSE, 6);
GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_widget_set_vexpand(scroll, TRUE);
gtk_box_pack_start(GTK_BOX(content), scroll, TRUE, TRUE, 6);
applicationStore = gtk_list_store_new(1, G_TYPE_STRING);
applicationTree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(applicationStore));
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(applicationTree), -1,
"Application", gtk_cell_renderer_text_new(), "text", 0, NULL);
gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(applicationTree)), GTK_SELECTION_SINGLE);
gtk_label_set_mnemonic_widget(GTK_LABEL(label), applicationTree);
atk_object_set_name(gtk_widget_get_accessible(applicationTree), "Applications");
atk_object_set_description(gtk_widget_get_accessible(applicationTree),
"Use arrow keys to choose a running application. Enter opens its preferences. Escape cancels. Tab leaves the list.");
gtk_container_add(GTK_CONTAINER(scroll), applicationTree);
g_signal_connect(chooser, "response", G_CALLBACK(chooser_response), NULL);
g_signal_connect(chooser, "delete-event", G_CALLBACK(chooser_delete), NULL);
g_signal_connect(chooser, "key-press-event", G_CALLBACK(chooser_key), NULL);
g_signal_connect(applicationTree, "row-activated", G_CALLBACK(chooser_activate), NULL);
return 1;
}
void prefs_application_chooser_add(const char *name) {
GtkTreeIter iter;
gtk_list_store_append(applicationStore, &iter);
gtk_list_store_set(applicationStore, &iter, 0, name, -1);
}
void prefs_application_chooser_present(void) {
gtk_widget_show_all(chooser);
gtk_window_present(GTK_WINDOW(chooser));
GtkTreePath *path = NULL;
gtk_tree_view_get_cursor(GTK_TREE_VIEW(applicationTree), &path, NULL);
if (!path && gtk_tree_model_iter_n_children(GTK_TREE_MODEL(applicationStore), NULL)) {
path = gtk_tree_path_new_first();
gtk_tree_view_set_cursor(GTK_TREE_VIEW(applicationTree), path, NULL, FALSE);
}
if (path) gtk_tree_path_free(path);
gtk_widget_grab_focus(applicationTree);
}
void prefs_application_chooser_iterate(void) {
while (gtk_events_pending()) gtk_main_iteration_do(FALSE);
}
int prefs_application_chooser_action(void) { return chooserAction; }
char *prefs_application_chooser_selected(void) {
GtkTreeIter iter;
if (!gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(applicationTree)), NULL, &iter)) {
chooserAction = 0;
return NULL;
}
char *name = NULL;
gtk_tree_model_get(GTK_TREE_MODEL(applicationStore), &iter, 0, &name, -1);
return name;
}
void prefs_application_chooser_close(void) {
gtk_widget_destroy(chooser);
g_object_unref(applicationStore);
chooser = applicationTree = NULL;
applicationStore = NULL;
}