137 lines
5.7 KiB
C
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;
|
|
}
|