/* * prefs_editor_linux.cpp - Preferences editor, Linux implementation using GTK+ * * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdeps.h" #include #include #include #include #include #include #include #include #ifdef __sun__ #include #endif #include #include "user_strings.h" #include "version.h" #include "cdrom.h" #include "xpram.h" #include "prefs.h" #include "prefs_editor.h" #define DEBUG 0 #include "debug.h" // Global variables static GtkWidget *win; // Preferences window static bool start_clicked = false; // Return value of PrefsEditor() function static int screen_width, screen_height; // Screen dimensions // Prototypes static void create_volumes_pane(GtkWidget *top); static void create_graphics_pane(GtkWidget *top); static void create_input_pane(GtkWidget *top); static void create_serial_pane(GtkWidget *top); static void create_memory_pane(GtkWidget *top); static void create_jit_pane(GtkWidget *top); static void read_settings(void); /* * Utility functions */ #if ! GLIB_CHECK_VERSION(2,0,0) #define G_OBJECT(obj) GTK_OBJECT(obj) #define g_object_get_data(obj, key) gtk_object_get_data((obj), (key)) #define g_object_set_data(obj, key, data) gtk_object_set_data((obj), (key), (data)) #endif struct opt_desc { int label_id; GCallback func; }; struct combo_desc { int label_id; }; // User closed the file chooser dialog, possibly selecting a file static void cb_browse_response(GtkWidget *chooser, int response, GtkEntry *entry) { if (response == GTK_RESPONSE_ACCEPT) { gchar *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); gtk_entry_set_text(GTK_ENTRY(entry), filename); g_free (filename); } gtk_widget_destroy (chooser); } // Open the file chooser dialog to select a file static void cb_browse(GtkWidget *button, GtkWidget *entry) { GtkWidget *chooser = gtk_file_chooser_dialog_new(GetString(STR_BROWSE_TITLE), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Open", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), g_dirname(gtk_entry_get_text(GTK_ENTRY(entry)))); gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_ACCEPT); gtk_window_set_transient_for(GTK_WINDOW(chooser), GTK_WINDOW(win)); gtk_window_set_modal(GTK_WINDOW(chooser), true); g_signal_connect(chooser, "response", G_CALLBACK(cb_browse_response), GTK_ENTRY(entry)); gtk_widget_show(chooser); } // Open the file chooser dialog to select a folder static void cb_browse_dir(GtkWidget *button, GtkWidget *entry) { GtkWidget *chooser = gtk_file_chooser_dialog_new(GetString(STR_BROWSE_FOLDER_TITLE), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, "Cancel", GTK_RESPONSE_CANCEL, "Select", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), gtk_entry_get_text(GTK_ENTRY(entry))); gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_ACCEPT); gtk_window_set_transient_for(GTK_WINDOW(chooser), GTK_WINDOW(win)); gtk_window_set_modal(GTK_WINDOW(chooser), true); g_signal_connect(chooser, "response", G_CALLBACK(cb_browse_response), GTK_WIDGET(entry)); gtk_widget_show(chooser); } static GtkWidget *make_browse_button(GtkWidget *entry, bool only_dirs) { GtkWidget *button; button = gtk_button_new_with_label(GetString(STR_BROWSE_CTRL)); gtk_widget_show(button); g_signal_connect(button, "clicked", only_dirs ? G_CALLBACK(cb_browse_dir) : G_CALLBACK(cb_browse), entry); return button; } static void add_menu_item(GtkWidget *menu, int label_id, GCallback func) { GtkWidget *item = gtk_menu_item_new_with_label(GetString(label_id)); gtk_widget_show(item); g_signal_connect(item, "activate", func, NULL); gtk_menu_append(GTK_MENU(menu), item); } static GtkWidget *make_pane(GtkWidget *notebook, int title_id) { GtkWidget *frame, *label, *box; frame = gtk_frame_new(NULL); gtk_container_border_width(GTK_CONTAINER(frame), 4); box = gtk_vbox_new(FALSE, 4); gtk_container_set_border_width(GTK_CONTAINER(box), 4); gtk_container_add(GTK_CONTAINER(frame), box); gtk_widget_show_all(frame); label = gtk_label_new(GetString(title_id)); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label); return box; } static GtkWidget *make_button_box(GtkWidget *top, int border, const opt_desc *buttons) { GtkWidget *bb, *button; bb = gtk_hbutton_box_new(); gtk_widget_show(bb); gtk_container_set_border_width(GTK_CONTAINER(bb), border); gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_DEFAULT_STYLE); gtk_button_box_set_spacing(GTK_BUTTON_BOX(bb), 4); gtk_box_pack_start(GTK_BOX(top), bb, FALSE, FALSE, 0); while (buttons->label_id) { button = gtk_button_new_with_label(GetString(buttons->label_id)); gtk_widget_show(button); g_signal_connect_object(button, "clicked", buttons->func, NULL, (GConnectFlags) 0); gtk_box_pack_start(GTK_BOX(bb), button, TRUE, TRUE, 0); buttons++; } return bb; } static GtkWidget *make_separator(GtkWidget *top) { GtkWidget *sep = gtk_hseparator_new(); gtk_box_pack_start(GTK_BOX(top), sep, FALSE, FALSE, 0); gtk_widget_show(sep); return sep; } static GtkWidget *make_table(GtkWidget *top, int x, int y) { GtkWidget *table = gtk_table_new(x, y, FALSE); gtk_widget_show(table); gtk_box_pack_start(GTK_BOX(top), table, FALSE, FALSE, 0); return table; } static GtkWidget *table_make_combobox(GtkWidget *table, int row, int label_id, const char *pref, GList *list) { GtkWidget *label, *combo; char str[32]; label = gtk_label_new(GetString(label_id)); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); combo = gtk_combo_box_entry_new_text(); gtk_widget_show(combo); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); while(list) { gtk_combo_box_append_text(GTK_COMBO_BOX(combo), ((gchar *) list->data)); list = list->next; } if (pref != NULL) gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN (combo))), pref); gtk_table_attach(GTK_TABLE(table), combo, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4); return combo; } static GtkWidget *table_make_combobox(GtkWidget *table, int row, int label_id, const char *default_value, const combo_desc *options) { GList *glist = NULL; while (options->label_id) { glist = g_list_append(glist, (void *)GetString(options->label_id)); options++; } return table_make_combobox(table, row, label_id, default_value, glist); } static GtkWidget *table_make_file_entry(GtkWidget *table, int row, int label_id, const char *prefs_item, bool only_dirs = false) { GtkWidget *box, *label, *entry, *button; label = gtk_label_new(GetString(label_id)); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); const char *str = PrefsFindString(prefs_item); if (str == NULL) str = ""; box = gtk_hbox_new(FALSE, 4); gtk_widget_show(box); gtk_table_attach(GTK_TABLE(table), box, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4); entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), str); gtk_widget_show(entry); gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0); button = make_browse_button(entry, false); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0); g_object_set_data(G_OBJECT(entry), "chooser_button", button); return entry; } static GtkWidget *make_option_menu(GtkWidget *top, int label_id, const combo_desc *options, GCallback func, int active) { GtkWidget *box, *label, *combo; box = gtk_hbox_new(FALSE, 4); gtk_widget_show(box); gtk_box_pack_start(GTK_BOX(top), box, FALSE, FALSE, 0); label = gtk_label_new(GetString(label_id)); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); combo = gtk_combo_box_new_text(); gtk_widget_show(combo); while (options->label_id) { gtk_combo_box_append_text(GTK_COMBO_BOX(combo), GetString(options->label_id)); options++; } gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active); gtk_box_pack_start(GTK_BOX(box), combo, FALSE, FALSE, 0); g_signal_connect(combo, "changed", func, NULL); return combo; } static GtkWidget *make_file_entry(GtkWidget *top, int label_id, const char *prefs_item, bool only_dirs = false) { GtkWidget *box, *label, *entry, *button; box = gtk_hbox_new(FALSE, 4); gtk_widget_show(box); gtk_box_pack_start(GTK_BOX(top), box, FALSE, FALSE, 0); label = gtk_label_new(GetString(label_id)); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); entry = gtk_entry_new(); gtk_widget_show(entry); const char *str = PrefsFindString(prefs_item); if (str == NULL) str = ""; gtk_entry_set_text(GTK_ENTRY(entry), str); button = make_browse_button(entry, only_dirs); gtk_widget_show(entry); #if GLIB_CHECK_VERSION(2,26,0) g_object_bind_property(entry, "sensitive", button, "sensitive", G_BINDING_SYNC_CREATE); #endif gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0); return entry; } static const gchar *get_file_entry_path(GtkWidget *entry) { return gtk_entry_get_text(GTK_ENTRY(entry)); } static GtkWidget *make_checkbox(GtkWidget *top, int label_id, const char *prefs_item, GCallback func) { GtkWidget *button = gtk_check_button_new_with_label(GetString(label_id)); gtk_widget_show(button); gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), PrefsFindBool(prefs_item)); g_signal_connect(button, "toggled", func, NULL); gtk_box_pack_start(GTK_BOX(top), button, FALSE, FALSE, 0); return button; } static GtkWidget *make_checkbox(GtkWidget *top, int label_id, bool active, GCallback func) { GtkWidget *button = gtk_check_button_new_with_label(GetString(label_id)); gtk_widget_show(button); gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), active); g_signal_connect(button, "toggled", func, NULL); gtk_box_pack_start(GTK_BOX(top), button, FALSE, FALSE, 0); return button; } /* * Show preferences editor * Returns true when user clicked on "Start", false otherwise */ // Window closed static gint window_closed(void) { return FALSE; } // Window destroyed static void window_destroyed(void) { gtk_main_quit(); } // "Save" button clicked static void cb_save(...) { read_settings(); SavePrefs(); } // "Start" button clicked static void cb_start(...) { start_clicked = true; read_settings(); SavePrefs(); gtk_widget_destroy(win); } // "Quit" button clicked static void cb_quit(...) { start_clicked = false; gtk_widget_destroy(win); } // "OK" button of "About" dialog clicked static void dl_quit(GtkWidget *dialog) { gtk_widget_destroy(dialog); } // "About" selected static void mn_about(...) { GtkWidget *dialog, *label, *button; const char *authors[] = { "Christian Bauer", "Marc Hellwig", "Gwenolé Beauchesne", NULL }; char version[64]; sprintf(version, "%d.%d", VERSION_MAJOR, VERSION_MINOR); gtk_show_about_dialog(GTK_WINDOW(win), "version", version, "copyright", GetString(STR_ABOUT_COPYRIGHT), "authors", authors, "comments", GetString(STR_ABOUT_COMMENTS), "website", GetString(STR_ABOUT_WEBSITE), "website-label", GetString(STR_ABOUT_WEBSITE_LABEL), "license", GetString(STR_ABOUT_LICENSE), "wrap-license", true, "logo-icon-name", "SheepShaver", NULL); } // "Zap NVRAM" selected static void mn_zap_pram(...) { ZapPRAM(); } // Menu item descriptions static GtkItemFactoryEntry menu_items[] = { {(gchar *)GetString(STR_PREFS_MENU_FILE_GTK), NULL, NULL, 0, ""}, {(gchar *)GetString(STR_PREFS_ITEM_START_GTK), "S", G_CALLBACK(cb_start), 0, NULL}, {(gchar *)GetString(STR_PREFS_ITEM_SAVE_GTK), NULL, G_CALLBACK(cb_save), 0, NULL}, {(gchar *)GetString(STR_PREFS_ITEM_ZAP_PRAM_GTK), NULL, G_CALLBACK(mn_zap_pram), 0, NULL}, {(gchar *)GetString(STR_PREFS_ITEM_SEPL_GTK), NULL, NULL, 0, ""}, {(gchar *)GetString(STR_PREFS_ITEM_QUIT_GTK), "Q", G_CALLBACK(cb_quit), 0, NULL}, {(gchar *)GetString(STR_HELP_MENU_GTK), NULL, NULL, 0, ""}, {(gchar *)GetString(STR_HELP_ITEM_ABOUT_GTK), NULL, G_CALLBACK(mn_about), 0, NULL} }; bool PrefsEditor(void) { // Get screen dimensions screen_width = gdk_screen_width(); screen_height = gdk_screen_height(); // Create window win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(win), GetString(STR_PREFS_TITLE)); g_signal_connect(win, "delete_event", G_CALLBACK(window_closed), NULL); g_signal_connect(win, "destroy", G_CALLBACK(window_destroyed), NULL); // Create window contents GtkWidget *box = gtk_vbox_new(FALSE, 4); gtk_widget_show(box); gtk_container_add(GTK_CONTAINER(win), box); GtkAccelGroup *accel_group = gtk_accel_group_new(); GtkItemFactory *item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "
", accel_group); gtk_item_factory_create_items(item_factory, sizeof(menu_items) / sizeof(menu_items[0]), menu_items, NULL); gtk_window_add_accel_group(GTK_WINDOW(win), accel_group); GtkWidget *menu_bar = gtk_item_factory_get_widget(item_factory, "
"); gtk_widget_show(menu_bar); gtk_box_pack_start(GTK_BOX(box), menu_bar, FALSE, TRUE, 0); GtkWidget *notebook = gtk_notebook_new(); gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), FALSE); gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, 0); gtk_widget_realize(notebook); create_volumes_pane(notebook); create_graphics_pane(notebook); create_input_pane(notebook); create_serial_pane(notebook); create_memory_pane(notebook); create_jit_pane(notebook); gtk_widget_show(notebook); static const opt_desc buttons[] = { {STR_START_BUTTON, G_CALLBACK(cb_start)}, {STR_QUIT_BUTTON, G_CALLBACK(cb_quit)}, {0, NULL} }; make_button_box(box, 4, buttons); // Show window and enter main loop gtk_widget_show(win); gtk_main(); return start_clicked; } /* * "Volumes" pane */ static GtkWidget *volume_list, *w_extfs; static int selected_volume; // Volume in list selected static void cl_selected(GtkWidget *list, int row, int column) { selected_volume = row; } // Volume selected for addition static void cb_add_volume_response (GtkWidget *chooser, int response) { if (response == GTK_RESPONSE_ACCEPT) { char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser)); gtk_clist_append(GTK_CLIST(volume_list), &file); } gtk_widget_destroy(chooser); } // Volume selected for creation static void cb_create_volume_response (GtkWidget *chooser, int response, GtkEntry *size_entry) { if (response == GTK_RESPONSE_ACCEPT) { char *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser)); const gchar *str = gtk_entry_get_text(GTK_ENTRY(size_entry)); int disk_size = atoi(str); if (disk_size < 1 || disk_size > 2000) { GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(win), (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "Enter a valid size", NULL); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "The volume size should be between 1 and 2000."); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(chooser)); g_signal_connect(dialog, "response", G_CALLBACK(dl_quit), NULL); gtk_widget_show(dialog); return; // Don't close the file chooser dialog } int fd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd < 0) { fprintf(stderr, "Could not create %s (%s)\n", file, strerror(errno)); } else { ftruncate(fd, disk_size * 1024 * 1024); gtk_clist_append(GTK_CLIST(volume_list), &file); } } gtk_widget_destroy (chooser); } // "Add Volume" button clicked static void cb_add_volume (...) { GtkWidget *chooser = gtk_file_chooser_dialog_new(GetString(STR_ADD_VOLUME_TITLE), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN, "Cancel", GTK_RESPONSE_CANCEL, "Add", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), g_get_home_dir()); gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_ACCEPT); gtk_window_set_modal(GTK_WINDOW(chooser), true); g_signal_connect(chooser, "response", G_CALLBACK(cb_add_volume_response), NULL); gtk_widget_show(chooser); } // "Create Hardfile" button clicked static void cb_create_volume (...) { GtkWidget *chooser = gtk_file_chooser_dialog_new(GetString(STR_CREATE_VOLUME_TITLE), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", GTK_RESPONSE_CANCEL, "Create", GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), g_get_home_dir()); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser), TRUE); gtk_dialog_set_default_response(GTK_DIALOG(chooser), GTK_RESPONSE_ACCEPT); gtk_window_set_transient_for(GTK_WINDOW(chooser), GTK_WINDOW(win)); gtk_window_set_modal(GTK_WINDOW(chooser), true); GtkWidget *box = gtk_hbox_new(false, 8); gtk_widget_show(box); GtkWidget *label = gtk_label_new(GetString(STR_HARDFILE_SIZE_CTRL)); gtk_widget_show(label); GtkWidget *size_entry = gtk_entry_new(); gtk_widget_show(size_entry); gtk_entry_set_text(GTK_ENTRY(size_entry), "40"); gtk_box_pack_end(GTK_BOX(box), size_entry, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0); gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(chooser), box); g_signal_connect(chooser, "response", G_CALLBACK(cb_create_volume_response), size_entry); gtk_widget_show(chooser); } // "Remove Volume" button clicked static void cb_remove_volume(...) { gtk_clist_remove(GTK_CLIST(volume_list), selected_volume); } // "Boot From" selected static void mn_bootdriver(GtkWidget *widget) { if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget))) PrefsReplaceInt32("bootdriver", CDROMRefNum); else PrefsReplaceInt32("bootdriver", 0); } // "No CD-ROM Driver" button toggled static void tb_nocdrom(GtkWidget *widget) { PrefsReplaceBool("nocdrom", GTK_TOGGLE_BUTTON(widget)->active); } // Read settings from widgets and set preferences static void read_volumes_settings(void) { while (PrefsFindString("disk")) PrefsRemoveItem("disk"); for (int i=0; irows; i++) { char *str; gtk_clist_get_text(GTK_CLIST(volume_list), i, 0, &str); PrefsAddString("disk", str); } PrefsReplaceString("extfs", gtk_entry_get_text(GTK_ENTRY(w_extfs))); } // Create "Volumes" pane static void create_volumes_pane(GtkWidget *top) { GtkWidget *box, *scroll, *menu; box = make_pane(top, STR_VOLUMES_PANE_TITLE); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_widget_show(scroll); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); volume_list = gtk_clist_new(1); gtk_widget_show(volume_list); gtk_clist_set_selection_mode(GTK_CLIST(volume_list), GTK_SELECTION_SINGLE); gtk_clist_set_shadow_type(GTK_CLIST(volume_list), GTK_SHADOW_NONE); gtk_clist_set_reorderable(GTK_CLIST(volume_list), true); g_signal_connect(volume_list, "select_row", G_CALLBACK(cl_selected), NULL); char *str; int32 index = 0; while ((str = (char *)PrefsFindString("disk", index++)) != NULL) gtk_clist_append(GTK_CLIST(volume_list), &str); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), volume_list); gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0); selected_volume = 0; static const opt_desc buttons[] = { {STR_ADD_VOLUME_BUTTON, G_CALLBACK(cb_add_volume)}, {STR_CREATE_VOLUME_BUTTON, G_CALLBACK(cb_create_volume)}, {STR_REMOVE_VOLUME_BUTTON, G_CALLBACK(cb_remove_volume)}, {0, NULL}, }; make_button_box(box, 0, buttons); make_separator(box); w_extfs = make_file_entry(box, STR_EXTFS_CTRL, "extfs", true); static const combo_desc options[] = { STR_BOOT_ANY_LAB, STR_BOOT_CDROM_LAB, 0 }; int bootdriver = PrefsFindInt32("bootdriver"), active = 0; switch (bootdriver) { case 0: active = 0; break; case CDROMRefNum: active = 1; break; } menu = make_option_menu(box, STR_BOOTDRIVER_CTRL, options, G_CALLBACK(mn_bootdriver), active); make_checkbox(box, STR_NOCDROM_CTRL, "nocdrom", G_CALLBACK(tb_nocdrom)); } /* * "JIT Compiler" pane */ // Are we running a JIT capable CPU? static bool is_jit_capable(void) { #if USE_JIT return true; #elif defined __APPLE__ && defined __MACH__ // XXX run-time detect so that we can use a PPC GUI prefs editor static char cpu[10]; if (cpu[0] == 0) { FILE *fp = popen("uname -p", "r"); if (fp == NULL) return false; fgets(cpu, sizeof(cpu) - 1, fp); fclose(fp); } if (cpu[0] == 'i' && cpu[2] == '8' && cpu[3] == '6') // XXX assuming i?86 return true; #endif return false; } // Set sensitivity of widgets static void set_jit_sensitive(void) { const bool jit_enabled = PrefsFindBool("jit"); } // "Use JIT Compiler" button toggled static void tb_jit(GtkWidget *widget) { PrefsReplaceBool("jit", GTK_TOGGLE_BUTTON(widget)->active); set_jit_sensitive(); } // Read settings from widgets and set preferences static void read_jit_settings(void) { bool jit_enabled = is_jit_capable() && PrefsFindBool("jit"); } // "Use built-in 68k DR emulator" button toggled static void tb_jit_68k(GtkWidget *widget) { PrefsReplaceBool("jit68k", GTK_TOGGLE_BUTTON(widget)->active); } // Create "JIT Compiler" pane static void create_jit_pane(GtkWidget *top) { GtkWidget *box, *table, *label, *menu; char str[32]; box = make_pane(top, STR_JIT_PANE_TITLE); if (is_jit_capable()) { make_checkbox(box, STR_JIT_CTRL, "jit", G_CALLBACK(tb_jit)); set_jit_sensitive(); } make_checkbox(box, STR_JIT_68K_CTRL, "jit68k", G_CALLBACK(tb_jit_68k)); } /* * "Graphics/Sound" pane */ // Display types enum { DISPLAY_WINDOW, DISPLAY_SCREEN }; static GtkWidget *w_frameskip, *w_display_x, *w_display_y; static GtkWidget *l_frameskip, *l_display_x, *l_display_y; static int display_type; static int dis_width, dis_height; static bool is_fbdev_dga_mode = false; static GtkWidget *w_dspdevice_file, *w_mixerdevice_file; // "Window"/"Fullscreen" video type selected static void mn_display(GtkWidget *widget) { if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget))) display_type = DISPLAY_SCREEN; else display_type = DISPLAY_WINDOW; } // "5 Hz".."60Hz" selected static void mn_frameskip(GtkWidget *widget) { int frameskip = 1; switch(gtk_combo_box_get_active(GTK_COMBO_BOX(widget))) { case 0: frameskip = 12; break; case 1: frameskip = 8; break; case 2: frameskip = 6; break; case 3: frameskip = 4; break; case 4: frameskip = 2; break; case 5: frameskip = 1; break; } PrefsReplaceInt32("frameskip", frameskip); } // QuickDraw acceleration static void tb_gfxaccel(GtkWidget *widget) { PrefsReplaceBool("gfxaccel", GTK_TOGGLE_BUTTON(widget)->active); } // Set sensitivity of widgets static void set_graphics_sensitive(void) { const bool sound_enabled = !PrefsFindBool("nosound"); gtk_widget_set_sensitive(w_dspdevice_file, sound_enabled); gtk_widget_set_sensitive(w_mixerdevice_file, sound_enabled); } // "Disable Sound Output" button toggled static void tb_nosound(GtkWidget *widget) { PrefsReplaceBool("nosound", GTK_TOGGLE_BUTTON(widget)->active); set_graphics_sensitive(); } // Read and convert graphics preferences static void parse_graphics_prefs(void) { display_type = DISPLAY_WINDOW; dis_width = 640; dis_height = 480; const char *str = PrefsFindString("screen"); if (str) { if (sscanf(str, "win/%d/%d", &dis_width, &dis_height) == 2) display_type = DISPLAY_WINDOW; else if (sscanf(str, "dga/%d/%d", &dis_width, &dis_height) == 2) display_type = DISPLAY_SCREEN; #ifdef ENABLE_FBDEV_DGA else if (sscanf(str, "fbdev/%d/%d", &dis_width, &dis_height) == 2) { is_fbdev_dga_mode = true; display_type = DISPLAY_SCREEN; } #endif } else { uint32 window_modes = PrefsFindInt32("windowmodes"); uint32 screen_modes = PrefsFindInt32("screenmodes"); if (screen_modes) { display_type = DISPLAY_SCREEN; static const struct { int id; int width; int height; } modes[] = { { 1, 640, 480 }, { 2, 800, 600 }, { 4, 1024, 768 }, { 64, 1152, 768 }, { 8, 1152, 900 }, { 16, 1280, 1024 }, { 32, 1600, 1200 }, { 0, } }; for (int i = 0; modes[i].id != 0; i++) { if (screen_modes & modes[i].id) { if (modes[i].width <= screen_width && modes[i].height <= screen_height) { dis_width = modes[i].width; dis_height = modes[i].height; } } } } else if (window_modes) { display_type = DISPLAY_WINDOW; if (window_modes & 1) dis_width = 640, dis_height = 480; if (window_modes & 2) dis_width = 800, dis_height = 600; } } if (dis_width == screen_width) dis_width = 0; if (dis_height == screen_height) dis_height = 0; } // Read settings from widgets and set preferences static void read_graphics_settings(void) { const char *str; str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w_display_x)); dis_width = atoi(str); str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w_display_y)); dis_height = atoi(str); char pref[256]; bool use_screen_mode = true; switch (display_type) { case DISPLAY_WINDOW: sprintf(pref, "win/%d/%d", dis_width, dis_height); break; case DISPLAY_SCREEN: sprintf(pref, "dga/%d/%d", dis_width, dis_height); break; default: use_screen_mode = false; PrefsRemoveItem("screen"); return; } if (use_screen_mode) { PrefsReplaceString("screen", pref); // Old prefs are now migrated PrefsRemoveItem("windowmodes"); PrefsRemoveItem("screenmodes"); } PrefsReplaceString("dsp", get_file_entry_path(w_dspdevice_file)); PrefsReplaceString("mixer", get_file_entry_path(w_mixerdevice_file)); } // Create "Graphics/Sound" pane static void create_graphics_pane(GtkWidget *top) { GtkWidget *box, *table, *label, *combo; char str[32]; parse_graphics_prefs(); box = make_pane(top, STR_GRAPHICS_SOUND_PANE_TITLE); table = make_table(box, 2, 4); label = gtk_label_new(GetString(STR_VIDEO_TYPE_CTRL)); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); combo = gtk_combo_box_new_text(); gtk_widget_show(combo); gtk_combo_box_append_text(GTK_COMBO_BOX(combo), GetString(STR_WINDOW_CTRL)); gtk_combo_box_append_text(GTK_COMBO_BOX(combo), GetString(STR_FULLSCREEN_CTRL)); switch (display_type) { case DISPLAY_WINDOW: gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); break; case DISPLAY_SCREEN: gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 1); break; } g_signal_connect(combo, "changed", G_CALLBACK(mn_display), NULL); gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 0, 1, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4); l_frameskip = gtk_label_new(GetString(STR_FRAMESKIP_CTRL)); gtk_widget_show(l_frameskip); gtk_table_attach(GTK_TABLE(table), l_frameskip, 0, 1, 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); w_frameskip = gtk_combo_box_new_text(); gtk_widget_show(w_frameskip); gtk_combo_box_append_text(GTK_COMBO_BOX(w_frameskip), GetString(STR_REF_5HZ_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_frameskip), GetString(STR_REF_7_5HZ_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_frameskip), GetString(STR_REF_10HZ_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_frameskip), GetString(STR_REF_15HZ_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_frameskip), GetString(STR_REF_30HZ_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_frameskip), GetString(STR_REF_60HZ_LAB)); int frameskip = PrefsFindInt32("frameskip"); int item = -1; switch (frameskip) { case 12: item = 0; break; case 8: item = 1; break; case 6: item = 2; break; case 4: item = 3; break; case 2: item = 4; break; case 1: item = 5; break; case 0: item = 5; break; } if (item >= 0) gtk_combo_box_set_active(GTK_COMBO_BOX(w_frameskip), item); g_signal_connect(w_frameskip, "changed", G_CALLBACK(mn_frameskip), NULL); gtk_table_attach(GTK_TABLE(table), w_frameskip, 1, 2, 1, 2, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4); l_display_x = gtk_label_new(GetString(STR_DISPLAY_X_CTRL)); gtk_widget_show(l_display_x); gtk_table_attach(GTK_TABLE(table), l_display_x, 0, 1, 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); w_display_x = gtk_combo_box_entry_new_text(); gtk_widget_show(w_display_x); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_x), GetString(STR_SIZE_512_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_x), GetString(STR_SIZE_640_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_x), GetString(STR_SIZE_800_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_x), GetString(STR_SIZE_1024_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_x), GetString(STR_SIZE_MAX_LAB)); if (dis_width) sprintf(str, "%d", dis_width); else strcpy(str, GetString(STR_SIZE_MAX_LAB)); gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w_display_x))), str); gtk_table_attach(GTK_TABLE(table), w_display_x, 1, 2, 2, 3, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4); l_display_y = gtk_label_new(GetString(STR_DISPLAY_Y_CTRL)); gtk_widget_show(l_display_y); gtk_table_attach(GTK_TABLE(table), l_display_y, 0, 1, 3, 4, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); w_display_y = gtk_combo_box_entry_new_text(); gtk_widget_show(w_display_y); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_y), GetString(STR_SIZE_384_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_y), GetString(STR_SIZE_480_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_y), GetString(STR_SIZE_600_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_y), GetString(STR_SIZE_768_LAB)); gtk_combo_box_append_text(GTK_COMBO_BOX(w_display_y), GetString(STR_SIZE_MAX_LAB)); if (dis_height) sprintf(str, "%d", dis_height); else strcpy(str, GetString(STR_SIZE_MAX_LAB)); gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w_display_y))), str); gtk_table_attach(GTK_TABLE(table), w_display_y, 1, 2, 3, 4, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4); make_checkbox(box, STR_GFXACCEL_CTRL, PrefsFindBool("gfxaccel"), G_CALLBACK(tb_gfxaccel)); make_separator(box); make_checkbox(box, STR_NOSOUND_CTRL, "nosound", G_CALLBACK(tb_nosound)); w_dspdevice_file = make_file_entry(box, STR_DSPDEVICE_FILE_CTRL, "dsp"); w_mixerdevice_file = make_file_entry(box, STR_MIXERDEVICE_FILE_CTRL, "mixer"); set_graphics_sensitive(); } /* * "Input" pane */ static GtkWidget *w_keycode_file; static GtkWidget *w_mouse_wheel_lines; // Set sensitivity of widgets static void set_input_sensitive(void) { const bool use_keycodes = PrefsFindBool("keycodes"); gtk_widget_set_sensitive(w_keycode_file, use_keycodes); gtk_widget_set_sensitive(GTK_WIDGET(g_object_get_data(G_OBJECT(w_keycode_file), "chooser_button")), use_keycodes); gtk_widget_set_sensitive(w_mouse_wheel_lines, PrefsFindInt32("mousewheelmode") == 1); } // "Use Raw Keycodes" button toggled static void tb_keycodes(GtkWidget *widget) { PrefsReplaceBool("keycodes", GTK_TOGGLE_BUTTON(widget)->active); set_input_sensitive(); } // "Mouse Wheel Mode" selected static void mn_wheelmode(GtkWidget *widget) { PrefsReplaceInt32("mousewheelmode", gtk_combo_box_get_active(GTK_COMBO_BOX(widget))); set_input_sensitive(); } // Read settings from widgets and set preferences static void read_input_settings(void) { const char *str = get_file_entry_path(w_keycode_file); if (str && strlen(str)) PrefsReplaceString("keycodefile", str); else PrefsRemoveItem("keycodefile"); PrefsReplaceInt32("mousewheellines", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w_mouse_wheel_lines))); } // Create "Input" pane static void create_input_pane(GtkWidget *top) { GtkWidget *box, *hbox, *menu, *label, *button; GtkObject *adj; box = make_pane(top, STR_INPUT_PANE_TITLE); make_checkbox(box, STR_KEYCODES_CTRL, "keycodes", G_CALLBACK(tb_keycodes)); hbox = gtk_hbox_new(FALSE, 4); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); label = gtk_label_new(GetString(STR_KEYCODES_CTRL)); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); const char *str = PrefsFindString("keycodefile"); if (str == NULL) str = ""; w_keycode_file = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(w_keycode_file), str); gtk_widget_show(w_keycode_file); gtk_box_pack_start(GTK_BOX(hbox), w_keycode_file, TRUE, TRUE, 0); button = make_browse_button(w_keycode_file, false); gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); g_object_set_data(G_OBJECT(w_keycode_file), "chooser_button", button); make_separator(box); static const combo_desc options[] = { STR_MOUSEWHEELMODE_PAGE_LAB, STR_MOUSEWHEELMODE_CURSOR_LAB, 0 }; int wheelmode = PrefsFindInt32("mousewheelmode"), active = 0; switch (wheelmode) { case 0: active = 0; break; case 1: active = 1; break; } menu = make_option_menu(box, STR_MOUSEWHEELMODE_CTRL, options, G_CALLBACK(mn_wheelmode), active); hbox = gtk_hbox_new(FALSE, 4); gtk_widget_show(hbox); gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); label = gtk_label_new(GetString(STR_MOUSEWHEELLINES_CTRL)); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); adj = gtk_adjustment_new(PrefsFindInt32("mousewheellines"), 1, 1000, 1, 5, 0); w_mouse_wheel_lines = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0.0, 0); gtk_widget_show(w_mouse_wheel_lines); gtk_box_pack_start(GTK_BOX(hbox), w_mouse_wheel_lines, FALSE, FALSE, 0); set_input_sensitive(); } /* * "Serial/Network" pane */ static GtkWidget *w_seriala, *w_serialb, *w_ether; // Read settings from widgets and set preferences static void read_serial_settings(void) { const char *str; str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w_seriala)); PrefsReplaceString("seriala", str); str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w_serialb)); PrefsReplaceString("serialb", str); str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w_ether)); if (str && strlen(str)) PrefsReplaceString("ether", str); else PrefsRemoveItem("ether"); } // Add names of serial devices static gint gl_str_cmp(gconstpointer a, gconstpointer b) { return strcmp((char *)a, (char *)b); } static GList *add_serial_names(void) { GList *glist = NULL; // Search /dev for ttyS* and lp* DIR *d = opendir("/dev"); if (d) { struct dirent *de; while ((de = readdir(d)) != NULL) { #if defined(__linux__) if (strncmp(de->d_name, "ttyS", 4) == 0 || strncmp(de->d_name, "lp", 2) == 0) { #elif defined(__FreeBSD__) if (strncmp(de->d_name, "cuaa", 4) == 0 || strncmp(de->d_name, "lpt", 3) == 0) { #elif defined(__NetBSD__) if (strncmp(de->d_name, "tty0", 4) == 0 || strncmp(de->d_name, "lpt", 3) == 0) { #elif defined(sgi) if (strncmp(de->d_name, "ttyf", 4) == 0 || strncmp(de->d_name, "plp", 3) == 0) { #else if (false) { #endif char *str = new char[64]; sprintf(str, "/dev/%s", de->d_name); glist = g_list_append(glist, str); } } closedir(d); } if (glist) glist = g_list_sort(glist, gl_str_cmp); else glist = g_list_append(glist, (void *)""); return glist; } // Add names of ethernet interfaces static GList *add_ether_names(void) { GList *glist = NULL; // Get list of all Ethernet interfaces int s = socket(PF_INET, SOCK_DGRAM, 0); if (s >= 0) { char inbuf[8192]; struct ifconf ifc; ifc.ifc_len = sizeof(inbuf); ifc.ifc_buf = inbuf; if (ioctl(s, SIOCGIFCONF, &ifc) == 0) { struct ifreq req, *ifr = ifc.ifc_req; for (int i=0; iifr_name, 63); glist = g_list_append(glist, str); } } } close(s); } #ifdef HAVE_SLIRP static char s_slirp[] = "slirp"; glist = g_list_append(glist, s_slirp); #endif if (glist) glist = g_list_sort(glist, gl_str_cmp); else glist = g_list_append(glist, (void *)""); return glist; } // Create "Serial/Network" pane static void create_serial_pane(GtkWidget *top) { GtkWidget *box, *table, *label, *combo; GList *glist = add_serial_names(); box = make_pane(top, STR_SERIAL_NETWORK_PANE_TITLE); table = make_table(box, 2, 3); label = gtk_label_new(GetString(STR_SERPORTA_CTRL)); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); label = gtk_label_new(GetString(STR_SERPORTB_CTRL)); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); w_seriala = gtk_combo_box_entry_new_text(); gtk_widget_show(w_seriala); w_serialb = gtk_combo_box_entry_new_text(); gtk_widget_show(w_serialb); while (glist) { gtk_combo_box_append_text(GTK_COMBO_BOX(w_seriala), (gchar *)glist->data); gtk_combo_box_append_text(GTK_COMBO_BOX(w_serialb), (gchar *)glist->data); glist = glist->next; } const char *str = PrefsFindString("seriala"); if (str == NULL) str = ""; gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w_seriala))), str); str = PrefsFindString("serialb"); if (str == NULL) str = ""; gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w_serialb))), str); gtk_table_attach(GTK_TABLE(table), w_seriala, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4); gtk_table_attach(GTK_TABLE(table), w_serialb, 1, 2, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4); label = gtk_label_new(GetString(STR_ETHERNET_IF_CTRL)); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4); glist = add_ether_names(); w_ether = gtk_combo_box_entry_new_text(); gtk_widget_show(w_ether); while (glist) { gtk_combo_box_append_text(GTK_COMBO_BOX(w_ether), (gchar *)glist->data); glist = glist->next; } str = PrefsFindString("ether"); if (str == NULL) str = ""; gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(w_ether))), str); gtk_table_attach(GTK_TABLE(table), w_ether, 1, 2, 2, 3, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4); } /* * "Memory/Misc" pane */ static GtkWidget *w_ramsize; static GtkWidget *w_rom_file; // Don't use CPU when idle? static void tb_idlewait(GtkWidget *widget) { PrefsReplaceBool("idlewait", GTK_TOGGLE_BUTTON(widget)->active); } // "Ignore SEGV" button toggled static void tb_ignoresegv(GtkWidget *widget) { PrefsReplaceBool("ignoresegv", GTK_TOGGLE_BUTTON(widget)->active); } // Read settings from widgets and set preferences static void read_memory_settings(void) { const char *str = gtk_combo_box_get_active_text(GTK_COMBO_BOX(w_ramsize)); PrefsReplaceInt32("ramsize", atoi(str) << 20); str = gtk_entry_get_text(GTK_ENTRY(w_rom_file)); if (str && strlen(str)) PrefsReplaceString("rom", str); else PrefsRemoveItem("rom"); } // Create "Memory/Misc" pane static void create_memory_pane(GtkWidget *top) { GtkWidget *box, *hbox, *table, *label, *menu; box = make_pane(top, STR_MEMORY_MISC_PANE_TITLE); table = make_table(box, 2, 5); static const combo_desc options[] = { STR_RAMSIZE_16MB_LAB, STR_RAMSIZE_32MB_LAB, STR_RAMSIZE_64MB_LAB, STR_RAMSIZE_128MB_LAB, STR_RAMSIZE_256MB_LAB, STR_RAMSIZE_512MB_LAB, STR_RAMSIZE_1024MB_LAB, 0 }; char default_ramsize[16]; sprintf(default_ramsize, "%d", PrefsFindInt32("ramsize") >> 20); w_ramsize = table_make_combobox(table, 0, STR_RAMSIZE_CTRL, default_ramsize, options); w_rom_file = table_make_file_entry(table, 1, STR_ROM_FILE_CTRL, "rom"); make_checkbox(box, STR_IGNORESEGV_CTRL, "ignoresegv", G_CALLBACK(tb_ignoresegv)); make_checkbox(box, STR_IDLEWAIT_CTRL, "idlewait", G_CALLBACK(tb_idlewait)); } /* * Read settings from widgets and set preferences */ static void read_settings(void) { read_volumes_settings(); read_graphics_settings(); read_input_settings(); read_serial_settings(); read_memory_settings(); read_jit_settings(); } #ifdef STANDALONE_GUI #include #include #include "rpc.h" /* * Fake unused data and functions */ uint8 XPRAM[XPRAM_SIZE]; void MountVolume(void *fh) { } void FileDiskLayout(loff_t size, uint8 *data, loff_t &start_byte, loff_t &real_size) { } #if defined __APPLE__ && defined __MACH__ void DarwinSysInit(void) { } void DarwinSysExit(void) { } void DarwinAddFloppyPrefs(void) { } void DarwinAddSerialPrefs(void) { } bool DarwinCDReadTOC(char *, uint8 *) { } #endif /* * Display alert */ static void dl_destroyed(void) { gtk_main_quit(); } static void display_alert(int title_id, int prefix_id, int button_id, const char *text) { char str[256]; sprintf(str, GetString(prefix_id), text); GtkWidget *dialog = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id)); gtk_container_border_width(GTK_CONTAINER(dialog), 5); gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150); g_signal_connect(dialog, "destroy", G_CALLBACK(dl_destroyed), NULL); GtkWidget *label = gtk_label_new(str); gtk_widget_show(label); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0); GtkWidget *button = gtk_button_new_with_label(GetString(button_id)); gtk_widget_show(button); g_signal_connect_object(button, "clicked", G_CALLBACK(dl_quit), dialog, (GConnectFlags) 0) gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_widget_grab_default(button); gtk_widget_show(dialog); gtk_main(); } /* * Display error alert */ void ErrorAlert(const char *text) { display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text); } /* * Display warning alert */ void WarningAlert(const char *text) { display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text); } /* * RPC handlers */ static GMainLoop *g_gui_loop; static int handle_ErrorAlert(rpc_connection_t *connection) { D(bug("handle_ErrorAlert\n")); int error; char *str; if ((error = rpc_method_get_args(connection, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID)) < 0) return error; ErrorAlert(str); free(str); return RPC_ERROR_NO_ERROR; } static int handle_WarningAlert(rpc_connection_t *connection) { D(bug("handle_WarningAlert\n")); int error; char *str; if ((error = rpc_method_get_args(connection, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID)) < 0) return error; WarningAlert(str); free(str); return RPC_ERROR_NO_ERROR; } static int handle_Exit(rpc_connection_t *connection) { D(bug("handle_Exit\n")); g_main_quit(g_gui_loop); return RPC_ERROR_NO_ERROR; } /* * SIGCHLD handler */ static char g_app_path[PATH_MAX]; static rpc_connection_t *g_gui_connection = NULL; static void sigchld_handler(int sig, siginfo_t *sip, void *) { D(bug("Child %d exitted with status = %x\n", sip->si_pid, sip->si_status)); // XXX perform a new wait because sip->si_status is sometimes not // the exit _value_ on MacOS X but rather the usual status field // from waitpid() -- we could arrange this code in some other way... int status; if (waitpid(sip->si_pid, &status, 0) < 0) status = sip->si_status; if (WIFEXITED(status)) status = WEXITSTATUS(status); if (status & 0x80) status |= -1 ^0xff; if (status < 0) { // negative -> execlp/-errno char str[256]; sprintf(str, GetString(STR_NO_B2_EXE_FOUND), g_app_path, strerror(-status)); ErrorAlert(str); status = 1; } if (status != 0) { if (g_gui_connection) rpc_exit(g_gui_connection); exit(status); } } /* * Start standalone GUI */ int main(int argc, char *argv[]) { // Init GTK gtk_set_locale(); gtk_init(&argc, &argv); // Read preferences PrefsInit(0, argc, argv); // Show preferences editor bool start = PrefsEditor(); // Exit preferences PrefsExit(); // Transfer control to the executable if (start) { char gui_connection_path[64]; sprintf(gui_connection_path, "/org/SheepShaver/GUI/%d", getpid()); // Catch exits from the child process struct sigaction sigchld_sa, old_sigchld_sa; sigemptyset(&sigchld_sa.sa_mask); sigchld_sa.sa_sigaction = sigchld_handler; sigchld_sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; if (sigaction(SIGCHLD, &sigchld_sa, &old_sigchld_sa) < 0) { char str[256]; sprintf(str, GetString(STR_SIG_INSTALL_ERR), SIGCHLD, strerror(errno)); ErrorAlert(str); return 1; } // Search and run the SheepShaver executable char *p; strcpy(g_app_path, argv[0]); if ((p = strstr(g_app_path, "SheepShaverGUI.app/Contents/MacOS")) != NULL) { strcpy(p, "SheepShaver.app/Contents/MacOS/SheepShaver"); if (access(g_app_path, X_OK) < 0) { char str[256]; sprintf(str, GetString(STR_NO_B2_EXE_FOUND), g_app_path, strerror(errno)); WarningAlert(str); strcpy(g_app_path, "/Applications/SheepShaver.app/Contents/MacOS/SheepShaver"); } } else { p = strrchr(g_app_path, '/'); p = p ? p + 1 : g_app_path; strcpy(p, "SheepShaver"); } int pid = fork(); if (pid == 0) { D(bug("Trying to execute %s\n", g_app_path)); execlp(g_app_path, g_app_path, "--gui-connection", gui_connection_path, (char *)NULL); #ifdef _POSIX_PRIORITY_SCHEDULING // XXX get a chance to run the parent process so that to not confuse/upset GTK... sched_yield(); #endif _exit(-errno); } // Establish a connection to Basilisk II if ((g_gui_connection = rpc_init_server(gui_connection_path)) == NULL) { printf("ERROR: failed to initialize GUI-side RPC server connection\n"); return 1; } static const rpc_method_descriptor_t vtable[] = { { RPC_METHOD_ERROR_ALERT, handle_ErrorAlert }, { RPC_METHOD_WARNING_ALERT, handle_WarningAlert }, { RPC_METHOD_EXIT, handle_Exit } }; if (rpc_method_add_callbacks(g_gui_connection, vtable, sizeof(vtable) / sizeof(vtable[0])) < 0) { printf("ERROR: failed to setup GUI method callbacks\n"); return 1; } int socket; if ((socket = rpc_listen_socket(g_gui_connection)) < 0) { printf("ERROR: failed to initialize RPC server thread\n"); return 1; } g_gui_loop = g_main_new(TRUE); while (g_main_is_running(g_gui_loop)) { // Process a few events pending const int N_EVENTS_DISPATCH = 10; for (int i = 0; i < N_EVENTS_DISPATCH; i++) { if (!g_main_iteration(FALSE)) break; } // Check for RPC events (100 ms timeout) int ret = rpc_wait_dispatch(g_gui_connection, 100000); if (ret == 0) continue; if (ret < 0) break; rpc_dispatch(g_gui_connection); } rpc_exit(g_gui_connection); return 0; } return 0; } #endif