32 #define USE_OLD_GTK_POPUP
34 static int _gtkgui_active = 0;
35 static int _gtkgui_popup_is_open = 0;
48 static int xemugtkgui_iteration (
void )
52 while (gtk_events_pending()) {
57 DEBUGGUI(
"GUI: GTK used %d iterations." NL, n);
58 if (n == 0 && _gtkgui_active == 2) {
68 static void xemugtkgui_shutdown (
void );
70 #ifndef XEMU_NO_SDL_DIALOG_OVERRIDE
71 static int SDL_ShowSimpleMessageBox_xemuguigtk (
Uint32 flags,
const char *title,
const char *message, SDL_Window *window );
72 static int SDL_ShowMessageBox_xemuguigtk (
const SDL_MessageBoxData* messageboxdata,
int *buttonid );
75 static int xemugtkgui_init (
void )
81 static const char gdk_backend_var_name[] =
"GDK_BACKEND";
82 static const char gdk_backend_var_value[] =
"x11";
83 DEBUGPRINT(
"GTK: setting environment variable %s=%s to avoid possible GTK backend mismatch with SDL" NL, gdk_backend_var_name, gdk_backend_var_value);
84 setenv(gdk_backend_var_name, gdk_backend_var_value, 1);
88 _gtkgui_popup_is_open = 0;
90 if (!gtk_init_check(NULL, NULL)) {
91 DEBUGGUI(
"GUI: GTK3 cannot be initialized, no GUI is available ..." NL);
96 xemugtkmenu.num_of_menus = 0;
98 int n = xemugtkgui_iteration();
99 DEBUGPRINT(
"GUI: GTK3 initialized, %d iterations." NL, n);
100 atexit(xemugtkgui_shutdown);
101 #ifndef XEMU_NO_SDL_DIALOG_OVERRIDE
110 static void xemugtkgui_shutdown (
void )
114 xemugtkgui_iteration();
121 static int _gtkgui_bgtask_running_want = 0;
122 static int _gtkgui_bgtask_running_status = 0;
123 static int _gtkgui_bgtask_callback (
void *unused ) {
129 while (SDL_PollEvent(&event))
131 if (_gtkgui_bgtask_running_want) {
132 _gtkgui_bgtask_running_status = 1;
136 _gtkgui_bgtask_running_status = 0;
141 static void _gtkgui_bgtask_set (
void ) {
142 if (_gtkgui_bgtask_running_want)
144 _gtkgui_bgtask_running_want = 1;
145 _gtkgui_bgtask_running_status = 1;
146 g_timeout_add(100, _gtkgui_bgtask_callback, NULL);
148 static void _gtkgui_bgtask_clear (
void ) {
149 if (!_gtkgui_bgtask_running_want)
151 _gtkgui_bgtask_running_want = 0;
152 while (_gtkgui_bgtask_running_status || gtk_events_pending()) {
153 gtk_main_iteration();
161 static GtkFileChooserConfirmation xemugtkgui_confirm_overwrite ( GtkFileChooser *chooser, gpointer
data ) {
162 return GTK_FILE_CHOOSER_CONFIRMATION_CONFIRM;
166 static int xemugtkgui_file_selector (
int dialog_mode,
const char *dialog_title,
char *default_dir,
char *selected,
int path_max_size )
171 const char *button_text;
172 GtkFileChooserAction action;
173 switch (dialog_mode & 3) {
175 action = GTK_FILE_CHOOSER_ACTION_OPEN;
176 button_text =
"_Open";
179 action = GTK_FILE_CHOOSER_ACTION_SAVE;
180 button_text =
"_Save";
183 FATAL(
"Invalid mode for UI selector: %d", dialog_mode & 3);
185 GtkWidget *dialog = gtk_file_chooser_dialog_new(
196 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), default_dir);
199 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
200 g_signal_connect(dialog,
"confirm-overwrite", G_CALLBACK(xemugtkgui_confirm_overwrite), NULL);
202 _gtkgui_bgtask_set();
203 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
204 _gtkgui_bgtask_clear();
205 if (res == GTK_RESPONSE_ACCEPT) {
207 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
208 if (strlen(filename) < path_max_size) {
209 strcpy(selected, filename);
210 store_dir_from_file_selection(default_dir, filename, dialog_mode);
212 res = GTK_RESPONSE_CANCEL;
215 gtk_widget_destroy(dialog);
216 while (gtk_events_pending())
217 gtk_main_iteration();
220 return res != GTK_RESPONSE_ACCEPT;
226 static void _gtkgui_destroy_menu (
void )
230 while (xemugtkmenu.num_of_menus > 0) {
231 gtk_widget_destroy(xemugtkmenu.menus[--xemugtkmenu.num_of_menus]);
232 DEBUGGUI(
"GUI: destroyed menu #%d at %p" NL, xemugtkmenu.num_of_menus, xemugtkmenu.menus[xemugtkmenu.num_of_menus]);
234 xemugtkmenu.num_of_menus = 0;
239 static void _gtkgui_callback (
const struct menu_st *item )
241 _gtkgui_destroy_menu();
244 DEBUGGUI(
"GUI: menu point \"%s\" has been activated." NL, item->
name);
249 static GtkWidget *_gtkgui_recursive_menu_builder (
const struct menu_st desc[],
const char *parent_name)
255 GtkWidget *menu = gtk_menu_new();
256 xemugtkmenu.menus[xemugtkmenu.num_of_menus++] = menu;
257 for (
int a = 0; desc[a].
name; a++) {
258 int type = desc[a].
type;
265 DEBUGPRINT(
"GUI: invalid menu entry found, skipping it (item #%d of menu \"%s\")" NL, a, parent_name);
268 GtkWidget *item = NULL;
269 switch (type & 0xFF) {
271 item = gtk_menu_item_new_with_label(desc[a].
name);
274 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
276 GtkWidget *submenu = _gtkgui_recursive_menu_builder(desc[a].user_data, desc[a].
name);
279 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
289 item = gtk_check_menu_item_new_with_label(desc[a].
name);
292 item = gtk_menu_item_new_with_label(desc[a].
name);
295 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
296 g_signal_connect_swapped(
299 G_CALLBACK(_gtkgui_callback),
304 DEBUGPRINT(
"GUI: invalid menu item type: %d" NL, type & 0xFF);
308 gtk_widget_show(item);
311 item = gtk_separator_menu_item_new();
314 gtk_widget_show(item);
315 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
319 gtk_widget_show(menu);
322 xemugtkmenu.problem = 1;
327 static GtkWidget *_gtkgui_create_menu (
const struct menu_st desc[] )
329 _gtkgui_destroy_menu();
330 xemugtkmenu.problem = 0;
332 if (!menu || xemugtkmenu.problem) {
333 _gtkgui_destroy_menu();
340 static void _gtkgui_disappear (
const char *signal_name )
345 DEBUGGUI(
"GUI: requesting iterator stop on g-signal \"%s\"" NL, signal_name);
348 _gtkgui_popup_is_open = 0;
352 #ifndef USE_OLD_GTK_POPUP
353 #include <gdk/gdkx.h>
354 #include <SDL_syswm.h>
355 static GdkWindow *super_ugly_gtk_hack (
void )
360 SDL_VERSION(&info.version);
361 SDL_GetWindowWMInfo(
sdl_win, &info);
362 if (info.subsystem != SDL_SYSWM_X11)
363 ERROR_WINDOW(
"Sorry, it won't work, GTK GUI is supported only on top of X11, because of GTK3 makes it no possible to get uniform window-ID from non-GTK window.");
366 GdkDisplay *gdisp = gdk_x11_lookup_xdisplay(info.info.x11.display);
368 DEBUGGUI(
"GUI: gdk_x11_lookup_xdisplay() failed :( reverting to gdk_display_get_default() ..." NL);
369 gdisp = gdk_display_get_default();
371 GdkWindow *gwin = gdk_x11_window_foreign_new_for_display(
383 static int xemugtkgui_popup (
const struct menu_st desc[] )
385 static const char disappear_signal[] =
"deactivate";
386 if (_gtkgui_popup_is_open) {
387 DEBUGGUI(
"GUI: trying to enter popup mode, while we're already there" NL);
391 DEBUGGUI(
"GUI: MENU: GUI was not successfully initialized yet, or GTK menu creation problem occured back to the first attempt" NL);
395 GtkWidget *menu = _gtkgui_create_menu(desc);
402 g_signal_connect_swapped(menu, disappear_signal, G_CALLBACK(_gtkgui_disappear), (gpointer)disappear_signal);
406 #ifdef USE_OLD_GTK_POPUP
413 GdkEventButton fake_event = {
414 .type = GDK_BUTTON_PRESS,
420 .window = super_ugly_gtk_hack()
422 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent*)&fake_event);
424 _gtkgui_popup_is_open = 1;
425 xemugtkgui_iteration();
431 static int _xemugtkgui_info_window_response;
432 static void _xemugtkgui_info_window_response_cb (GtkDialog *dialog, gint response, gpointer user_data) {
433 _xemugtkgui_info_window_response = response;
436 static int xemugtkgui_info_window ( GtkMessageType msg_class,
const char *msg,
const char *msg2 )
438 GtkWidget* dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, msg_class, GTK_BUTTONS_OK,
"%s", msg);
442 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", msg2);
443 _gtkgui_bgtask_set();
444 gtk_dialog_run(GTK_DIALOG(dialog));
445 _gtkgui_bgtask_clear();
447 g_signal_connect_swapped(dialog,
"response", G_CALLBACK(_xemugtkgui_info_window_response_cb), NULL);
448 _xemugtkgui_info_window_response = 0;
449 gtk_widget_show(dialog);
450 while (!_xemugtkgui_info_window_response) {
451 while (gtk_events_pending())
452 gtk_main_iteration();
457 gtk_widget_destroy(dialog);
458 while (gtk_events_pending())
459 gtk_main_iteration();
462 static int xemugtkgui_info (
int sdl_class,
const char *msg )
464 GtkMessageType msg_class;
467 case SDL_MESSAGEBOX_INFORMATION:
469 msg_class = GTK_MESSAGE_INFO;
471 case SDL_MESSAGEBOX_WARNING:
472 title =
"Xemu warning";
473 msg_class = GTK_MESSAGE_WARNING;
475 case SDL_MESSAGEBOX_ERROR:
476 title =
"Xemu error";
477 msg_class = GTK_MESSAGE_ERROR;
481 msg_class = GTK_MESSAGE_OTHER;
484 return xemugtkgui_info_window(msg_class, title, msg);
487 #ifndef XEMU_NO_SDL_DIALOG_OVERRIDE
488 static int SDL_ShowSimpleMessageBox_xemuguigtk (
Uint32 flags,
const char *title,
const char *message, SDL_Window *window )
491 if (!xemugtkgui_info(
flags, message))
494 DEBUGPRINT(
"GUI: SDL_ShowSimpleMessageBox_xemuguigtk() has problems, reverting to SDL_ShowSimpleMessageBox()" NL);
496 DEBUGPRINT(
"GUI: not initialized yet, SDL_ShowSimpleMessageBox_xemuguigtk() reverts to SDL_ShowSimpleMessageBox()" NL);
497 if (!SDL_ShowSimpleMessageBox(
flags, title, message, window))
499 DEBUGPRINT(
"GUI: SDL_ShowSimpleMessageBox() error: %s" NL, SDL_GetError());
502 static int SDL_ShowMessageBox_xemuguigtk (
const SDL_MessageBoxData *box,
int *buttonid )
505 DEBUGPRINT(
"GUI: not initialized yet, SDL_ShowMessageBox_xemuguigtk() reverts to SDL_ShowMessageBox()" NL);
509 FATAL(
"Less than one button for %s?!", __func__);
510 GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
"%s",
"Xemu question");
511 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s", box->message);
512 gtk_window_set_focus(GTK_WINDOW(dialog), NULL);
513 int escape_default_value = -1;
514 GtkWidget *return_default_button = NULL;
515 for (
int a = 0; a < box->numbuttons; a++) {
516 GtkWidget *
button = gtk_dialog_add_button(GTK_DIALOG(dialog), box->buttons[a].text, a);
517 if ((box->buttons[a].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) || escape_default_value < 0)
518 escape_default_value = a;
519 if ((box->buttons[a].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) || !return_default_button)
520 return_default_button =
button;
523 for (
int a = 0; a < 10; a++) {
525 sprintf(
name,
"Element-%d", a);
526 gtk_dialog_add_button(GTK_DIALOG(dialog),
name, box->numbuttons + a);
529 gtk_widget_set_can_default(return_default_button, TRUE);
530 gtk_window_set_default(GTK_WINDOW(dialog), return_default_button);
531 _gtkgui_bgtask_set();
532 gint res = gtk_dialog_run(GTK_DIALOG(dialog));
533 _gtkgui_bgtask_clear();
534 gtk_widget_destroy(dialog);
538 res = escape_default_value;
540 while (gtk_events_pending())
541 gtk_main_iteration();
546 return SDL_ShowMessageBox(box, buttonid);
552 .description =
"GTK3 based Xemu UI implementation",
553 .init = xemugtkgui_init,
554 .shutdown = xemugtkgui_shutdown,
555 .iteration = xemugtkgui_iteration,
556 .file_selector = xemugtkgui_file_selector,
557 .popup = xemugtkgui_popup,
558 .info = xemugtkgui_info