Mercurial > repos > blastem
comparison nuklear_ui/filechooser_gtk.c @ 2355:94cf5cc89227
Add an option to use the system file picker on Linux and Windows
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 21 Oct 2023 19:22:01 -0700 |
parents | |
children | 12d594e69e04 |
comparison
equal
deleted
inserted
replaced
2354:a773b8f09292 | 2355:94cf5cc89227 |
---|---|
1 #include <stddef.h> | |
2 #include <stdint.h> | |
3 #include <stdio.h> | |
4 #include <gtk/gtk.h> | |
5 #include <dlfcn.h> | |
6 | |
7 typedef GtkWidget* (*gtk_file_chooser_dialog_new_t)(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...); | |
8 typedef gint (*gtk_dialog_run_t)(GtkDialog *dialog); | |
9 typedef void (*gtk_widget_destroy_t)(GtkWidget *widget); | |
10 typedef gchar* (*gtk_file_chooser_get_filename_t)(GtkFileChooser *chooser); | |
11 typedef gboolean (*gtk_init_check_t)(int *argc, char **argv); | |
12 typedef gboolean (*gtk_file_chooser_set_current_folder_t)(GtkFileChooser *chooser, const gchar *filename); | |
13 typedef void (*gtk_file_chooser_setadd_filter_t)(GtkFileChooser *chooser, GtkFileFilter *filter); | |
14 typedef gboolean (*gtk_events_pending_t)(void); | |
15 typedef gboolean (*gtk_main_iteration_t)(void); | |
16 typedef GtkFileFilter* (*gtk_file_filter_new_t)(void); | |
17 typedef void (*gtk_file_filter_set_name_t)(GtkFileFilter *filter, const gchar *name); | |
18 typedef void (*gtk_file_filter_add_pattern_t)(GtkFileFilter *filter, const gchar *pattern); | |
19 | |
20 typedef struct { | |
21 gtk_file_chooser_dialog_new_t gtk_file_chooser_dialog_new; | |
22 gtk_dialog_run_t gtk_dialog_run; | |
23 gtk_widget_destroy_t gtk_widget_destroy; | |
24 gtk_file_chooser_get_filename_t gtk_file_chooser_get_filename; | |
25 gtk_file_chooser_set_current_folder_t gtk_file_chooser_set_current_folder; | |
26 gtk_file_chooser_setadd_filter_t gtk_file_chooser_add_filter; | |
27 gtk_file_chooser_setadd_filter_t gtk_file_chooser_set_filter; | |
28 gtk_file_filter_new_t gtk_file_filter_new; | |
29 gtk_file_filter_set_name_t gtk_file_filter_set_name; | |
30 gtk_file_filter_add_pattern_t gtk_file_filter_add_pattern; | |
31 gtk_init_check_t gtk_init_check; | |
32 gtk_events_pending_t gtk_events_pending; | |
33 gtk_main_iteration_t gtk_main_iteration; | |
34 } gtk; | |
35 | |
36 #define LOAD_SYM(s, t, name) t->name = dlsym(s, #name); if (!t->name) { fputs("filechooser_gtk: Failed to load " #name "\n", stderr); goto error_cleanup; } | |
37 | |
38 static gtk* check_init_gtk(void) | |
39 { | |
40 static const char *so_paths[] = { | |
41 #ifdef X86_64 | |
42 "/usr/lib/x86_64-linux-gnu/libgtk-3.so.0", | |
43 "/usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0", | |
44 #elif X86_32 | |
45 "/usr/lib/i386-linux-gnu/libgtk-3.so.0", | |
46 "/usr/lib/i386-linux-gnu/libgtk-x11-2.0.so.0", | |
47 #else | |
48 //TODO: what are these paths on ARM? | |
49 #endif | |
50 }; | |
51 static gtk *funcs; | |
52 static uint8_t already_init; | |
53 if (!already_init) { | |
54 void *so = NULL; | |
55 for (int i = 0; !so && i < sizeof(so_paths)/sizeof(*so_paths); i++) | |
56 { | |
57 so = dlopen(so_paths[i], RTLD_NOW | RTLD_LOCAL); | |
58 } | |
59 if (so) { | |
60 funcs = calloc(1, sizeof(gtk)); | |
61 | |
62 LOAD_SYM(so, funcs, gtk_file_chooser_dialog_new) | |
63 LOAD_SYM(so, funcs, gtk_dialog_run) | |
64 LOAD_SYM(so, funcs, gtk_widget_destroy) | |
65 LOAD_SYM(so, funcs, gtk_file_chooser_get_filename) | |
66 LOAD_SYM(so, funcs, gtk_file_chooser_set_current_folder) | |
67 LOAD_SYM(so, funcs, gtk_file_chooser_add_filter) | |
68 LOAD_SYM(so, funcs, gtk_file_chooser_set_filter) | |
69 LOAD_SYM(so, funcs, gtk_file_filter_new) | |
70 LOAD_SYM(so, funcs, gtk_file_filter_set_name) | |
71 LOAD_SYM(so, funcs, gtk_file_filter_add_pattern) | |
72 LOAD_SYM(so, funcs, gtk_init_check) | |
73 LOAD_SYM(so, funcs, gtk_events_pending) | |
74 LOAD_SYM(so, funcs, gtk_main_iteration) | |
75 | |
76 if (funcs->gtk_init_check(NULL, NULL)) { | |
77 return funcs; | |
78 } | |
79 | |
80 error_cleanup: | |
81 free(funcs); | |
82 dlclose(so); | |
83 } | |
84 } | |
85 return funcs; | |
86 } | |
87 | |
88 uint8_t native_filechooser_available(void) | |
89 { | |
90 return !!check_init_gtk(); | |
91 } | |
92 | |
93 char* native_filechooser_pick(const char *title, const char *start_directory) | |
94 { | |
95 gtk *g = check_init_gtk(); | |
96 if (!g) { | |
97 return NULL; | |
98 } | |
99 GtkFileChooser *chooser = (GtkFileChooser *)g->gtk_file_chooser_dialog_new( | |
100 title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, | |
101 "Cancel", GTK_RESPONSE_CANCEL, | |
102 "Open", GTK_RESPONSE_ACCEPT, | |
103 NULL | |
104 ); | |
105 if (!chooser) { | |
106 return NULL; | |
107 } | |
108 if (start_directory) { | |
109 g->gtk_file_chooser_set_current_folder(chooser, start_directory); | |
110 } | |
111 GtkFileFilter *filter = g->gtk_file_filter_new(); | |
112 g->gtk_file_filter_set_name(filter, "All Files"); | |
113 g->gtk_file_filter_add_pattern(filter, "*"); | |
114 g->gtk_file_chooser_add_filter(chooser, filter); | |
115 | |
116 filter = g->gtk_file_filter_new(); | |
117 g->gtk_file_filter_set_name(filter, "All Supported Types"); | |
118 g->gtk_file_filter_add_pattern(filter, "*.zip"); | |
119 g->gtk_file_filter_add_pattern(filter, "*.bin"); | |
120 g->gtk_file_filter_add_pattern(filter, "*.bin.gz"); | |
121 g->gtk_file_filter_add_pattern(filter, "*.gen"); | |
122 g->gtk_file_filter_add_pattern(filter, "*.gen.gz"); | |
123 g->gtk_file_filter_add_pattern(filter, "*.md"); | |
124 g->gtk_file_filter_add_pattern(filter, "*.md.gz"); | |
125 g->gtk_file_filter_add_pattern(filter, "*.sms"); | |
126 g->gtk_file_filter_add_pattern(filter, "*.sms.gz"); | |
127 g->gtk_file_filter_add_pattern(filter, "*.gg"); | |
128 g->gtk_file_filter_add_pattern(filter, "*.gg.gz"); | |
129 g->gtk_file_filter_add_pattern(filter, "*.sg"); | |
130 g->gtk_file_filter_add_pattern(filter, "*.sg.gz"); | |
131 g->gtk_file_filter_add_pattern(filter, "*.cue"); | |
132 g->gtk_file_filter_add_pattern(filter, "*.toc"); | |
133 g->gtk_file_filter_add_pattern(filter, "*.flac"); | |
134 g->gtk_file_filter_add_pattern(filter, "*.vgm"); | |
135 g->gtk_file_filter_add_pattern(filter, "*.vgz"); | |
136 g->gtk_file_filter_add_pattern(filter, "*.vgm.gz"); | |
137 g->gtk_file_chooser_add_filter(chooser, filter); | |
138 g->gtk_file_chooser_set_filter(chooser, filter); | |
139 | |
140 filter = g->gtk_file_filter_new(); | |
141 g->gtk_file_filter_set_name(filter, "Genesis/MD"); | |
142 g->gtk_file_filter_add_pattern(filter, "*.zip"); | |
143 g->gtk_file_filter_add_pattern(filter, "*.bin"); | |
144 g->gtk_file_filter_add_pattern(filter, "*.bin.gz"); | |
145 g->gtk_file_filter_add_pattern(filter, "*.gen"); | |
146 g->gtk_file_filter_add_pattern(filter, "*.gen.gz"); | |
147 g->gtk_file_filter_add_pattern(filter, "*.md"); | |
148 g->gtk_file_filter_add_pattern(filter, "*.md.gz"); | |
149 g->gtk_file_chooser_add_filter(chooser, filter); | |
150 | |
151 filter = g->gtk_file_filter_new(); | |
152 g->gtk_file_filter_set_name(filter, "Sega/Mega CD"); | |
153 g->gtk_file_filter_add_pattern(filter, "*.cue"); | |
154 g->gtk_file_filter_add_pattern(filter, "*.toc"); | |
155 g->gtk_file_chooser_add_filter(chooser, filter); | |
156 | |
157 filter = g->gtk_file_filter_new(); | |
158 g->gtk_file_filter_set_name(filter, "Sega 8-bit"); | |
159 g->gtk_file_filter_add_pattern(filter, "*.sms"); | |
160 g->gtk_file_filter_add_pattern(filter, "*.sms.gz"); | |
161 g->gtk_file_filter_add_pattern(filter, "*.gg"); | |
162 g->gtk_file_filter_add_pattern(filter, "*.gg.gz"); | |
163 g->gtk_file_filter_add_pattern(filter, "*.sg"); | |
164 g->gtk_file_filter_add_pattern(filter, "*.sg.gz"); | |
165 g->gtk_file_chooser_add_filter(chooser, filter); | |
166 | |
167 filter = g->gtk_file_filter_new(); | |
168 g->gtk_file_filter_set_name(filter, "Audio/VGM"); | |
169 g->gtk_file_filter_add_pattern(filter, "*.flac"); | |
170 g->gtk_file_filter_add_pattern(filter, "*.vgm"); | |
171 g->gtk_file_filter_add_pattern(filter, "*.vgz"); | |
172 g->gtk_file_filter_add_pattern(filter, "*.vgm.gz"); | |
173 g->gtk_file_chooser_add_filter(chooser, filter); | |
174 | |
175 char *ret = NULL; | |
176 if (GTK_RESPONSE_ACCEPT == g->gtk_dialog_run((GtkDialog*)chooser)) { | |
177 ret = g->gtk_file_chooser_get_filename(chooser); | |
178 } | |
179 g->gtk_widget_destroy((GtkWidget *)chooser); | |
180 while (g->gtk_events_pending()) | |
181 { | |
182 g->gtk_main_iteration(); | |
183 } | |
184 return ret; | |
185 } |