ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/prefs_editor_gtk.cpp
Revision: 1.17
Committed: 2006-05-01T22:33:34Z (18 years, 1 month ago) by gbeauche
Branch: MAIN
Changes since 1.16: +274 -1 lines
Log Message:
Port --enable-standalone-gui support to SheepShaver

Others changes include:
- Factor out STR_SIG_INSTALL_ERR messages
- Process command line arguments early (prior to calling PrefsInit())
- GUI: set start_clicked only if the "Start" button was clicked
- GUI: save changes to the "Input" pane when the "Start" button was clicked

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * prefs_editor_linux.cpp - Preferences editor, Linux implementation using GTK+
3     *
4 gbeauche 1.11 * SheepShaver (C) 1997-2005 Christian Bauer and Marc Hellwig
5 cebix 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #include "sysdeps.h"
22    
23     #include <gtk/gtk.h>
24     #include <stdlib.h>
25     #include <dirent.h>
26     #include <sys/socket.h>
27     #include <sys/ioctl.h>
28     #include <net/if.h>
29     #include <net/if_arp.h>
30    
31     #include "user_strings.h"
32     #include "version.h"
33     #include "cdrom.h"
34     #include "xpram.h"
35     #include "prefs.h"
36     #include "prefs_editor.h"
37    
38 gbeauche 1.17 #define DEBUG 0
39     #include "debug.h"
40    
41 cebix 1.1
42     // Global variables
43     static GtkWidget *win; // Preferences window
44 gbeauche 1.17 static bool start_clicked = false; // Return value of PrefsEditor() function
45 gbeauche 1.13 static int screen_width, screen_height; // Screen dimensions
46 cebix 1.1
47    
48     // Prototypes
49     static void create_volumes_pane(GtkWidget *top);
50     static void create_graphics_pane(GtkWidget *top);
51 gbeauche 1.2 static void create_input_pane(GtkWidget *top);
52 cebix 1.1 static void create_serial_pane(GtkWidget *top);
53     static void create_memory_pane(GtkWidget *top);
54 gbeauche 1.3 static void create_jit_pane(GtkWidget *top);
55 cebix 1.1 static void read_settings(void);
56    
57    
58     /*
59     * Utility functions
60     */
61    
62 gbeauche 1.15 #if ! GLIB_CHECK_VERSION(2,0,0)
63     #define G_OBJECT(obj) GTK_OBJECT(obj)
64     #define g_object_get_data(obj, key) gtk_object_get_data((obj), (key))
65     #define g_object_set_data(obj, key, data) gtk_object_set_data((obj), (key), (data))
66     #endif
67    
68 cebix 1.1 struct opt_desc {
69     int label_id;
70     GtkSignalFunc func;
71     };
72    
73 gbeauche 1.15 struct combo_desc {
74     int label_id;
75     };
76    
77     struct file_req_assoc {
78     file_req_assoc(GtkWidget *r, GtkWidget *e) : req(r), entry(e) {}
79     GtkWidget *req;
80     GtkWidget *entry;
81     };
82    
83     static void cb_browse_ok(GtkWidget *button, file_req_assoc *assoc)
84     {
85     gchar *file = (char *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
86     gtk_entry_set_text(GTK_ENTRY(assoc->entry), file);
87     gtk_widget_destroy(assoc->req);
88     delete assoc;
89     }
90    
91     static void cb_browse(GtkWidget *widget, void *user_data)
92     {
93     GtkWidget *req = gtk_file_selection_new(GetString(STR_BROWSE_TITLE));
94     gtk_signal_connect_object(GTK_OBJECT(req), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
95     gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(req)->ok_button), "clicked", GTK_SIGNAL_FUNC(cb_browse_ok), new file_req_assoc(req, (GtkWidget *)user_data));
96     gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(req)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
97     gtk_widget_show(req);
98     }
99    
100     static GtkWidget *make_browse_button(GtkWidget *entry)
101     {
102     GtkWidget *button;
103    
104     button = gtk_button_new_with_label(GetString(STR_BROWSE_CTRL));
105     gtk_widget_show(button);
106     gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc)cb_browse, (void *)entry);
107     return button;
108     }
109    
110 cebix 1.1 static void add_menu_item(GtkWidget *menu, int label_id, GtkSignalFunc func)
111     {
112     GtkWidget *item = gtk_menu_item_new_with_label(GetString(label_id));
113     gtk_widget_show(item);
114     gtk_signal_connect(GTK_OBJECT(item), "activate", func, NULL);
115     gtk_menu_append(GTK_MENU(menu), item);
116     }
117    
118     static GtkWidget *make_pane(GtkWidget *notebook, int title_id)
119     {
120     GtkWidget *frame, *label, *box;
121    
122     frame = gtk_frame_new(NULL);
123     gtk_widget_show(frame);
124     gtk_container_border_width(GTK_CONTAINER(frame), 4);
125    
126     label = gtk_label_new(GetString(title_id));
127     gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label);
128    
129     box = gtk_vbox_new(FALSE, 4);
130     gtk_widget_show(box);
131     gtk_container_set_border_width(GTK_CONTAINER(box), 4);
132     gtk_container_add(GTK_CONTAINER(frame), box);
133     return box;
134     }
135    
136     static GtkWidget *make_button_box(GtkWidget *top, int border, const opt_desc *buttons)
137     {
138     GtkWidget *bb, *button;
139    
140     bb = gtk_hbutton_box_new();
141     gtk_widget_show(bb);
142     gtk_container_set_border_width(GTK_CONTAINER(bb), border);
143     gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_DEFAULT_STYLE);
144     gtk_button_box_set_spacing(GTK_BUTTON_BOX(bb), 4);
145     gtk_box_pack_start(GTK_BOX(top), bb, FALSE, FALSE, 0);
146    
147     while (buttons->label_id) {
148     button = gtk_button_new_with_label(GetString(buttons->label_id));
149     gtk_widget_show(button);
150     gtk_signal_connect_object(GTK_OBJECT(button), "clicked", buttons->func, NULL);
151     gtk_box_pack_start(GTK_BOX(bb), button, TRUE, TRUE, 0);
152     buttons++;
153     }
154     return bb;
155     }
156    
157     static GtkWidget *make_separator(GtkWidget *top)
158     {
159     GtkWidget *sep = gtk_hseparator_new();
160     gtk_box_pack_start(GTK_BOX(top), sep, FALSE, FALSE, 0);
161     gtk_widget_show(sep);
162     return sep;
163     }
164    
165     static GtkWidget *make_table(GtkWidget *top, int x, int y)
166     {
167     GtkWidget *table = gtk_table_new(x, y, FALSE);
168     gtk_widget_show(table);
169     gtk_box_pack_start(GTK_BOX(top), table, FALSE, FALSE, 0);
170     return table;
171     }
172    
173 gbeauche 1.15 static GtkWidget *table_make_option_menu(GtkWidget *table, int row, int label_id, const opt_desc *options, int active)
174     {
175     GtkWidget *label, *opt, *menu;
176    
177     label = gtk_label_new(GetString(label_id));
178     gtk_widget_show(label);
179     gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
180    
181     opt = gtk_option_menu_new();
182     gtk_widget_show(opt);
183     menu = gtk_menu_new();
184    
185     while (options->label_id) {
186     add_menu_item(menu, options->label_id, options->func);
187     options++;
188     }
189     gtk_menu_set_active(GTK_MENU(menu), active);
190    
191     gtk_option_menu_set_menu(GTK_OPTION_MENU(opt), menu);
192     gtk_table_attach(GTK_TABLE(table), opt, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
193     return menu;
194     }
195    
196     static GtkWidget *table_make_combobox(GtkWidget *table, int row, int label_id, const char *default_value, GList *glist)
197     {
198     GtkWidget *label, *combo;
199     char str[32];
200    
201     label = gtk_label_new(GetString(label_id));
202     gtk_widget_show(label);
203     gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
204    
205     combo = gtk_combo_new();
206     gtk_widget_show(combo);
207     gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
208    
209     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), default_value);
210     gtk_table_attach(GTK_TABLE(table), combo, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
211    
212     return combo;
213     }
214    
215     static GtkWidget *table_make_combobox(GtkWidget *table, int row, int label_id, const char *default_value, const combo_desc *options)
216     {
217     GList *glist = NULL;
218     while (options->label_id) {
219     glist = g_list_append(glist, (void *)GetString(options->label_id));
220     options++;
221     }
222    
223     return table_make_combobox(table, row, label_id, default_value, glist);
224     }
225    
226     static GtkWidget *table_make_file_entry(GtkWidget *table, int row, int label_id, const char *prefs_item, bool only_dirs = false)
227     {
228     GtkWidget *box, *label, *entry, *button;
229    
230     label = gtk_label_new(GetString(label_id));
231     gtk_widget_show(label);
232     gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
233    
234     const char *str = PrefsFindString(prefs_item);
235     if (str == NULL)
236     str = "";
237    
238     box = gtk_hbox_new(FALSE, 4);
239     gtk_widget_show(box);
240     gtk_table_attach(GTK_TABLE(table), box, 1, 2, row, row + 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
241    
242     entry = gtk_entry_new();
243     gtk_entry_set_text(GTK_ENTRY(entry), str);
244     gtk_widget_show(entry);
245     gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0);
246    
247     button = make_browse_button(entry);
248     gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 0);
249     g_object_set_data(G_OBJECT(entry), "chooser_button", button);
250     return entry;
251     }
252    
253 cebix 1.1 static GtkWidget *make_option_menu(GtkWidget *top, int label_id, const opt_desc *options, int active)
254     {
255     GtkWidget *box, *label, *opt, *menu;
256    
257     box = gtk_hbox_new(FALSE, 4);
258     gtk_widget_show(box);
259     gtk_box_pack_start(GTK_BOX(top), box, FALSE, FALSE, 0);
260    
261     label = gtk_label_new(GetString(label_id));
262     gtk_widget_show(label);
263     gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
264    
265     opt = gtk_option_menu_new();
266     gtk_widget_show(opt);
267     menu = gtk_menu_new();
268    
269     while (options->label_id) {
270     add_menu_item(menu, options->label_id, options->func);
271     options++;
272     }
273     gtk_menu_set_active(GTK_MENU(menu), active);
274    
275     gtk_option_menu_set_menu(GTK_OPTION_MENU(opt), menu);
276     gtk_box_pack_start(GTK_BOX(box), opt, FALSE, FALSE, 0);
277     return menu;
278     }
279    
280     static GtkWidget *make_entry(GtkWidget *top, int label_id, const char *prefs_item)
281     {
282     GtkWidget *box, *label, *entry;
283    
284     box = gtk_hbox_new(FALSE, 4);
285     gtk_widget_show(box);
286     gtk_box_pack_start(GTK_BOX(top), box, FALSE, FALSE, 0);
287    
288     label = gtk_label_new(GetString(label_id));
289     gtk_widget_show(label);
290     gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
291    
292     entry = gtk_entry_new();
293     gtk_widget_show(entry);
294     const char *str = PrefsFindString(prefs_item);
295     if (str == NULL)
296     str = "";
297     gtk_entry_set_text(GTK_ENTRY(entry), str);
298     gtk_box_pack_start(GTK_BOX(box), entry, TRUE, TRUE, 0);
299     return entry;
300     }
301    
302 gbeauche 1.12 static const gchar *get_file_entry_path(GtkWidget *entry)
303 gbeauche 1.2 {
304     return gtk_entry_get_text(GTK_ENTRY(entry));
305     }
306    
307 cebix 1.1 static GtkWidget *make_checkbox(GtkWidget *top, int label_id, const char *prefs_item, GtkSignalFunc func)
308     {
309     GtkWidget *button = gtk_check_button_new_with_label(GetString(label_id));
310     gtk_widget_show(button);
311     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), PrefsFindBool(prefs_item));
312     gtk_signal_connect(GTK_OBJECT(button), "toggled", func, button);
313     gtk_box_pack_start(GTK_BOX(top), button, FALSE, FALSE, 0);
314     return button;
315     }
316    
317     static GtkWidget *make_checkbox(GtkWidget *top, int label_id, bool active, GtkSignalFunc func)
318     {
319     GtkWidget *button = gtk_check_button_new_with_label(GetString(label_id));
320     gtk_widget_show(button);
321     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), active);
322     gtk_signal_connect(GTK_OBJECT(button), "toggled", func, button);
323     gtk_box_pack_start(GTK_BOX(top), button, FALSE, FALSE, 0);
324     return button;
325     }
326    
327    
328     /*
329     * Show preferences editor
330     * Returns true when user clicked on "Start", false otherwise
331     */
332    
333     // Window closed
334     static gint window_closed(void)
335     {
336     return FALSE;
337     }
338    
339     // Window destroyed
340     static void window_destroyed(void)
341     {
342     gtk_main_quit();
343     }
344    
345     // "Start" button clicked
346     static void cb_start(...)
347     {
348     start_clicked = true;
349     read_settings();
350     SavePrefs();
351     gtk_widget_destroy(win);
352     }
353    
354     // "Quit" button clicked
355     static void cb_quit(...)
356     {
357     start_clicked = false;
358     gtk_widget_destroy(win);
359     }
360    
361     // "OK" button of "About" dialog clicked
362     static void dl_quit(GtkWidget *dialog)
363     {
364     gtk_widget_destroy(dialog);
365     }
366    
367     // "About" selected
368     static void mn_about(...)
369     {
370     GtkWidget *dialog, *label, *button;
371    
372     char str[512];
373     sprintf(str,
374     "SheepShaver\nVersion %d.%d\n\n"
375 gbeauche 1.16 "Copyright (C) 1997-2005 Christian Bauer and Marc Hellwig\n"
376     "E-mail: cb@cebix.net\n"
377     "http://sheepshaver.cebix.net/\n\n"
378 cebix 1.1 "SheepShaver comes with ABSOLUTELY NO\n"
379     "WARRANTY. This is free software, and\n"
380     "you are welcome to redistribute it\n"
381     "under the terms of the GNU General\n"
382     "Public License.\n",
383     VERSION_MAJOR, VERSION_MINOR
384     );
385    
386     dialog = gtk_dialog_new();
387     gtk_window_set_title(GTK_WINDOW(dialog), GetString(STR_ABOUT_TITLE));
388     gtk_container_border_width(GTK_CONTAINER(dialog), 5);
389     gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
390    
391     label = gtk_label_new(str);
392     gtk_widget_show(label);
393     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
394    
395     button = gtk_button_new_with_label(GetString(STR_OK_BUTTON));
396     gtk_widget_show(button);
397     gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
398     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
399     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
400     gtk_widget_grab_default(button);
401     gtk_widget_show(dialog);
402     }
403    
404     // "Zap NVRAM" selected
405     static void mn_zap_pram(...)
406     {
407     ZapPRAM();
408     }
409    
410     // Menu item descriptions
411     static GtkItemFactoryEntry menu_items[] = {
412     {(gchar *)GetString(STR_PREFS_MENU_FILE_GTK), NULL, NULL, 0, "<Branch>"},
413 gbeauche 1.15 {(gchar *)GetString(STR_PREFS_ITEM_START_GTK), "<control>S", GTK_SIGNAL_FUNC(cb_start), 0, NULL},
414 cebix 1.1 {(gchar *)GetString(STR_PREFS_ITEM_ZAP_PRAM_GTK), NULL, GTK_SIGNAL_FUNC(mn_zap_pram), 0, NULL},
415     {(gchar *)GetString(STR_PREFS_ITEM_SEPL_GTK), NULL, NULL, 0, "<Separator>"},
416     {(gchar *)GetString(STR_PREFS_ITEM_QUIT_GTK), "<control>Q", GTK_SIGNAL_FUNC(cb_quit), 0, NULL},
417     {(gchar *)GetString(STR_HELP_MENU_GTK), NULL, NULL, 0, "<LastBranch>"},
418     {(gchar *)GetString(STR_HELP_ITEM_ABOUT_GTK), NULL, GTK_SIGNAL_FUNC(mn_about), 0, NULL}
419     };
420    
421     bool PrefsEditor(void)
422     {
423 gbeauche 1.13 // Get screen dimensions
424     screen_width = gdk_screen_width();
425     screen_height = gdk_screen_height();
426    
427 cebix 1.1 // Create window
428     win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
429     gtk_window_set_title(GTK_WINDOW(win), GetString(STR_PREFS_TITLE));
430     gtk_signal_connect(GTK_OBJECT(win), "delete_event", GTK_SIGNAL_FUNC(window_closed), NULL);
431     gtk_signal_connect(GTK_OBJECT(win), "destroy", GTK_SIGNAL_FUNC(window_destroyed), NULL);
432    
433     // Create window contents
434     GtkWidget *box = gtk_vbox_new(FALSE, 4);
435     gtk_widget_show(box);
436     gtk_container_add(GTK_CONTAINER(win), box);
437    
438     GtkAccelGroup *accel_group = gtk_accel_group_new();
439     GtkItemFactory *item_factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group);
440     gtk_item_factory_create_items(item_factory, sizeof(menu_items) / sizeof(menu_items[0]), menu_items, NULL);
441 gbeauche 1.12 #if GTK_CHECK_VERSION(1,3,15)
442     gtk_window_add_accel_group(GTK_WINDOW(win), accel_group);
443     #else
444 cebix 1.1 gtk_accel_group_attach(accel_group, GTK_OBJECT(win));
445 gbeauche 1.12 #endif
446 cebix 1.1 GtkWidget *menu_bar = gtk_item_factory_get_widget(item_factory, "<main>");
447     gtk_widget_show(menu_bar);
448     gtk_box_pack_start(GTK_BOX(box), menu_bar, FALSE, TRUE, 0);
449    
450     GtkWidget *notebook = gtk_notebook_new();
451     gtk_widget_show(notebook);
452     gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
453     gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), FALSE);
454     gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, 0);
455    
456     create_volumes_pane(notebook);
457     create_graphics_pane(notebook);
458 gbeauche 1.2 create_input_pane(notebook);
459 cebix 1.1 create_serial_pane(notebook);
460     create_memory_pane(notebook);
461 gbeauche 1.3 create_jit_pane(notebook);
462 cebix 1.1
463     static const opt_desc buttons[] = {
464     {STR_START_BUTTON, GTK_SIGNAL_FUNC(cb_start)},
465     {STR_QUIT_BUTTON, GTK_SIGNAL_FUNC(cb_quit)},
466     {0, NULL}
467     };
468     make_button_box(box, 4, buttons);
469    
470     // Show window and enter main loop
471     gtk_widget_show(win);
472     gtk_main();
473     return start_clicked;
474     }
475    
476    
477     /*
478     * "Volumes" pane
479     */
480    
481     static GtkWidget *volume_list, *w_extfs;
482     static int selected_volume;
483    
484     // Volume in list selected
485     static void cl_selected(GtkWidget *list, int row, int column)
486     {
487     selected_volume = row;
488     }
489    
490     // Volume selected for addition
491     static void add_volume_ok(GtkWidget *button, file_req_assoc *assoc)
492     {
493 gbeauche 1.12 gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
494 cebix 1.1 gtk_clist_append(GTK_CLIST(volume_list), &file);
495     gtk_widget_destroy(assoc->req);
496     delete assoc;
497     }
498    
499     // Volume selected for creation
500     static void create_volume_ok(GtkWidget *button, file_req_assoc *assoc)
501     {
502 gbeauche 1.12 gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
503 cebix 1.1
504 gbeauche 1.12 const gchar *str = gtk_entry_get_text(GTK_ENTRY(assoc->entry));
505 cebix 1.1 int size = atoi(str);
506    
507     char cmd[1024];
508     sprintf(cmd, "dd if=/dev/zero \"of=%s\" bs=1024k count=%d", file, size);
509     int ret = system(cmd);
510     if (ret == 0)
511     gtk_clist_append(GTK_CLIST(volume_list), &file);
512     gtk_widget_destroy(GTK_WIDGET(assoc->req));
513     delete assoc;
514     }
515    
516     // "Add Volume" button clicked
517     static void cb_add_volume(...)
518     {
519     GtkWidget *req = gtk_file_selection_new(GetString(STR_ADD_VOLUME_TITLE));
520     gtk_signal_connect_object(GTK_OBJECT(req), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
521     gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(req)->ok_button), "clicked", GTK_SIGNAL_FUNC(add_volume_ok), new file_req_assoc(req, NULL));
522     gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(req)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
523     gtk_widget_show(req);
524     }
525    
526     // "Create Hardfile" button clicked
527     static void cb_create_volume(...)
528     {
529     GtkWidget *req = gtk_file_selection_new(GetString(STR_CREATE_VOLUME_TITLE));
530    
531     GtkWidget *box = gtk_hbox_new(FALSE, 4);
532     gtk_widget_show(box);
533     GtkWidget *label = gtk_label_new(GetString(STR_HARDFILE_SIZE_CTRL));
534     gtk_widget_show(label);
535     GtkWidget *entry = gtk_entry_new();
536     gtk_widget_show(entry);
537     char str[32];
538     sprintf(str, "%d", 40);
539     gtk_entry_set_text(GTK_ENTRY(entry), str);
540     gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
541     gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
542     gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(req)->main_vbox), box, FALSE, FALSE, 0);
543    
544     gtk_signal_connect_object(GTK_OBJECT(req), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
545     gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(req)->ok_button), "clicked", GTK_SIGNAL_FUNC(create_volume_ok), new file_req_assoc(req, entry));
546     gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(req)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
547     gtk_widget_show(req);
548     }
549    
550     // "Remove Volume" button clicked
551     static void cb_remove_volume(...)
552     {
553     gtk_clist_remove(GTK_CLIST(volume_list), selected_volume);
554     }
555    
556     // "Boot From" selected
557     static void mn_boot_any(...) {PrefsReplaceInt32("bootdriver", 0);}
558     static void mn_boot_cdrom(...) {PrefsReplaceInt32("bootdriver", CDROMRefNum);}
559    
560     // "No CD-ROM Driver" button toggled
561     static void tb_nocdrom(GtkWidget *widget)
562     {
563     PrefsReplaceBool("nocdrom", GTK_TOGGLE_BUTTON(widget)->active);
564     }
565    
566     // Read settings from widgets and set preferences
567     static void read_volumes_settings(void)
568     {
569     while (PrefsFindString("disk"))
570     PrefsRemoveItem("disk");
571    
572     for (int i=0; i<GTK_CLIST(volume_list)->rows; i++) {
573     char *str;
574     gtk_clist_get_text(GTK_CLIST(volume_list), i, 0, &str);
575     PrefsAddString("disk", str);
576     }
577    
578     PrefsReplaceString("extfs", gtk_entry_get_text(GTK_ENTRY(w_extfs)));
579     }
580    
581     // Create "Volumes" pane
582     static void create_volumes_pane(GtkWidget *top)
583     {
584     GtkWidget *box, *scroll, *menu;
585    
586     box = make_pane(top, STR_VOLUMES_PANE_TITLE);
587    
588     scroll = gtk_scrolled_window_new(NULL, NULL);
589     gtk_widget_show(scroll);
590     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
591     volume_list = gtk_clist_new(1);
592     gtk_widget_show(volume_list);
593     gtk_clist_set_selection_mode(GTK_CLIST(volume_list), GTK_SELECTION_SINGLE);
594     gtk_clist_set_shadow_type(GTK_CLIST(volume_list), GTK_SHADOW_NONE);
595     gtk_clist_set_reorderable(GTK_CLIST(volume_list), true);
596     gtk_signal_connect(GTK_OBJECT(volume_list), "select_row", GTK_SIGNAL_FUNC(cl_selected), NULL);
597     char *str;
598     int32 index = 0;
599     while ((str = (char *)PrefsFindString("disk", index++)) != NULL)
600     gtk_clist_append(GTK_CLIST(volume_list), &str);
601     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), volume_list);
602     gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0);
603     selected_volume = 0;
604    
605     static const opt_desc buttons[] = {
606     {STR_ADD_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_add_volume)},
607     {STR_CREATE_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_create_volume)},
608     {STR_REMOVE_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_remove_volume)},
609     {0, NULL},
610     };
611     make_button_box(box, 0, buttons);
612     make_separator(box);
613    
614     w_extfs = make_entry(box, STR_EXTFS_CTRL, "extfs");
615    
616     static const opt_desc options[] = {
617     {STR_BOOT_ANY_LAB, GTK_SIGNAL_FUNC(mn_boot_any)},
618     {STR_BOOT_CDROM_LAB, GTK_SIGNAL_FUNC(mn_boot_cdrom)},
619     {0, NULL}
620     };
621     int bootdriver = PrefsFindInt32("bootdriver"), active = 0;
622     switch (bootdriver) {
623     case 0: active = 0; break;
624     case CDROMRefNum: active = 1; break;
625     }
626     menu = make_option_menu(box, STR_BOOTDRIVER_CTRL, options, active);
627    
628     make_checkbox(box, STR_NOCDROM_CTRL, "nocdrom", GTK_SIGNAL_FUNC(tb_nocdrom));
629     }
630    
631    
632     /*
633 gbeauche 1.3 * "JIT Compiler" pane
634     */
635    
636     // Set sensitivity of widgets
637     static void set_jit_sensitive(void)
638     {
639     const bool jit_enabled = PrefsFindBool("jit");
640     }
641    
642     // "Use JIT Compiler" button toggled
643     static void tb_jit(GtkWidget *widget)
644     {
645     PrefsReplaceBool("jit", GTK_TOGGLE_BUTTON(widget)->active);
646     set_jit_sensitive();
647     }
648    
649     // Read settings from widgets and set preferences
650     static void read_jit_settings(void)
651     {
652     #if USE_JIT
653     bool jit_enabled = PrefsFindBool("jit");
654     #endif
655     }
656    
657 gbeauche 1.10 // "Use built-in 68k DR emulator" button toggled
658     static void tb_jit_68k(GtkWidget *widget)
659     {
660     PrefsReplaceBool("jit68k", GTK_TOGGLE_BUTTON(widget)->active);
661     }
662    
663 gbeauche 1.3 // Create "JIT Compiler" pane
664     static void create_jit_pane(GtkWidget *top)
665     {
666     GtkWidget *box, *table, *label, *menu;
667     char str[32];
668    
669     box = make_pane(top, STR_JIT_PANE_TITLE);
670 gbeauche 1.10 #if USE_JIT
671 gbeauche 1.3 make_checkbox(box, STR_JIT_CTRL, "jit", GTK_SIGNAL_FUNC(tb_jit));
672     set_jit_sensitive();
673     #endif
674 gbeauche 1.10 make_checkbox(box, STR_JIT_68K_CTRL, "jit68k", GTK_SIGNAL_FUNC(tb_jit_68k));
675 gbeauche 1.3 }
676    
677    
678     /*
679 cebix 1.1 * "Graphics/Sound" pane
680     */
681    
682 gbeauche 1.13 // Display types
683     enum {
684     DISPLAY_WINDOW,
685     DISPLAY_SCREEN
686     };
687    
688     static GtkWidget *w_frameskip, *w_display_x, *w_display_y;
689     static GtkWidget *l_frameskip, *l_display_x, *l_display_y;
690     static int display_type;
691     static int dis_width, dis_height;
692 gbeauche 1.14 static bool is_fbdev_dga_mode = false;
693 cebix 1.1
694 gbeauche 1.4 static GtkWidget *w_dspdevice_file, *w_mixerdevice_file;
695    
696 gbeauche 1.13 // Hide/show graphics widgets
697     static void hide_show_graphics_widgets(void)
698     {
699     switch (display_type) {
700     case DISPLAY_WINDOW:
701     gtk_widget_show(w_frameskip); gtk_widget_show(l_frameskip);
702     break;
703     case DISPLAY_SCREEN:
704     gtk_widget_hide(w_frameskip); gtk_widget_hide(l_frameskip);
705     break;
706     }
707     }
708    
709     // "Window" video type selected
710     static void mn_window(...)
711     {
712     display_type = DISPLAY_WINDOW;
713     hide_show_graphics_widgets();
714     }
715    
716     // "Fullscreen" video type selected
717     static void mn_fullscreen(...)
718     {
719     display_type = DISPLAY_SCREEN;
720     hide_show_graphics_widgets();
721     }
722    
723 cebix 1.1 // "5 Hz".."60Hz" selected
724     static void mn_5hz(...) {PrefsReplaceInt32("frameskip", 12);}
725     static void mn_7hz(...) {PrefsReplaceInt32("frameskip", 8);}
726     static void mn_10hz(...) {PrefsReplaceInt32("frameskip", 6);}
727     static void mn_15hz(...) {PrefsReplaceInt32("frameskip", 4);}
728     static void mn_30hz(...) {PrefsReplaceInt32("frameskip", 2);}
729     static void mn_60hz(...) {PrefsReplaceInt32("frameskip", 1);}
730    
731 gbeauche 1.9 // QuickDraw acceleration
732     static void tb_gfxaccel(GtkWidget *widget)
733     {
734     PrefsReplaceBool("gfxaccel", GTK_TOGGLE_BUTTON(widget)->active);
735     }
736    
737 gbeauche 1.4 // Set sensitivity of widgets
738     static void set_graphics_sensitive(void)
739     {
740     const bool sound_enabled = !PrefsFindBool("nosound");
741     gtk_widget_set_sensitive(w_dspdevice_file, sound_enabled);
742     gtk_widget_set_sensitive(w_mixerdevice_file, sound_enabled);
743     }
744    
745 cebix 1.1 // "Disable Sound Output" button toggled
746     static void tb_nosound(GtkWidget *widget)
747     {
748     PrefsReplaceBool("nosound", GTK_TOGGLE_BUTTON(widget)->active);
749 gbeauche 1.4 set_graphics_sensitive();
750 cebix 1.1 }
751    
752 gbeauche 1.13 // Read and convert graphics preferences
753     static void parse_graphics_prefs(void)
754     {
755     display_type = DISPLAY_WINDOW;
756     dis_width = 640;
757     dis_height = 480;
758    
759     const char *str = PrefsFindString("screen");
760     if (str) {
761     if (sscanf(str, "win/%d/%d", &dis_width, &dis_height) == 2)
762     display_type = DISPLAY_WINDOW;
763     else if (sscanf(str, "dga/%d/%d", &dis_width, &dis_height) == 2)
764     display_type = DISPLAY_SCREEN;
765 gbeauche 1.14 #ifdef ENABLE_FBDEV_DGA
766     else if (sscanf(str, "fbdev/%d/%d", &dis_width, &dis_height) == 2) {
767     is_fbdev_dga_mode = true;
768     display_type = DISPLAY_SCREEN;
769     }
770     #endif
771 gbeauche 1.13 }
772     else {
773     uint32 window_modes = PrefsFindInt32("windowmodes");
774     uint32 screen_modes = PrefsFindInt32("screenmodes");
775     if (screen_modes) {
776     display_type = DISPLAY_SCREEN;
777     static const struct {
778     int id;
779     int width;
780     int height;
781     }
782     modes[] = {
783     { 1, 640, 480 },
784     { 2, 800, 600 },
785     { 4, 1024, 768 },
786     { 64, 1152, 768 },
787     { 8, 1152, 900 },
788     { 16, 1280, 1024 },
789     { 32, 1600, 1200 },
790     { 0, }
791     };
792     for (int i = 0; modes[i].id != 0; i++) {
793     if (screen_modes & modes[i].id) {
794     if (modes[i].width <= screen_width && modes[i].height <= screen_height) {
795     dis_width = modes[i].width;
796     dis_height = modes[i].height;
797     }
798     }
799     }
800     }
801     else if (window_modes) {
802     display_type = DISPLAY_WINDOW;
803     if (window_modes & 1)
804     dis_width = 640, dis_height = 480;
805     if (window_modes & 2)
806     dis_width = 800, dis_height = 600;
807     }
808     }
809     if (dis_width == screen_width)
810     dis_width = 0;
811     if (dis_height == screen_height)
812     dis_height = 0;
813     }
814    
815 cebix 1.1 // Read settings from widgets and set preferences
816     static void read_graphics_settings(void)
817     {
818 gbeauche 1.13 const char *str;
819    
820     str = gtk_entry_get_text(GTK_ENTRY(w_display_x));
821     dis_width = atoi(str);
822    
823     str = gtk_entry_get_text(GTK_ENTRY(w_display_y));
824     dis_height = atoi(str);
825    
826     char pref[256];
827 gbeauche 1.14 bool use_screen_mode = true;
828 gbeauche 1.13 switch (display_type) {
829     case DISPLAY_WINDOW:
830     sprintf(pref, "win/%d/%d", dis_width, dis_height);
831     break;
832     case DISPLAY_SCREEN:
833     sprintf(pref, "dga/%d/%d", dis_width, dis_height);
834     break;
835     default:
836 gbeauche 1.14 use_screen_mode = false;
837 gbeauche 1.13 PrefsRemoveItem("screen");
838     return;
839     }
840 gbeauche 1.14 if (use_screen_mode) {
841     PrefsReplaceString("screen", pref);
842     // Old prefs are now migrated
843     PrefsRemoveItem("windowmodes");
844     PrefsRemoveItem("screenmodes");
845     }
846 gbeauche 1.13
847 gbeauche 1.4 PrefsReplaceString("dsp", get_file_entry_path(w_dspdevice_file));
848     PrefsReplaceString("mixer", get_file_entry_path(w_mixerdevice_file));
849 cebix 1.1 }
850    
851     // Create "Graphics/Sound" pane
852     static void create_graphics_pane(GtkWidget *top)
853     {
854 gbeauche 1.13 GtkWidget *box, *table, *label, *opt, *menu, *combo;
855     char str[32];
856    
857     parse_graphics_prefs();
858 cebix 1.1
859     box = make_pane(top, STR_GRAPHICS_SOUND_PANE_TITLE);
860 gbeauche 1.13 table = make_table(box, 2, 4);
861    
862     label = gtk_label_new(GetString(STR_VIDEO_TYPE_CTRL));
863     gtk_widget_show(label);
864     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
865 cebix 1.1
866 gbeauche 1.13 opt = gtk_option_menu_new();
867     gtk_widget_show(opt);
868     menu = gtk_menu_new();
869     add_menu_item(menu, STR_WINDOW_CTRL, GTK_SIGNAL_FUNC(mn_window));
870     add_menu_item(menu, STR_FULLSCREEN_CTRL, GTK_SIGNAL_FUNC(mn_fullscreen));
871     switch (display_type) {
872     case DISPLAY_WINDOW:
873     gtk_menu_set_active(GTK_MENU(menu), 0);
874     break;
875     case DISPLAY_SCREEN:
876     gtk_menu_set_active(GTK_MENU(menu), 1);
877     break;
878     }
879     gtk_option_menu_set_menu(GTK_OPTION_MENU(opt), menu);
880     gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 0, 1, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
881    
882     l_frameskip = gtk_label_new(GetString(STR_FRAMESKIP_CTRL));
883     gtk_widget_show(l_frameskip);
884     gtk_table_attach(GTK_TABLE(table), l_frameskip, 0, 1, 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
885    
886     w_frameskip = gtk_option_menu_new();
887     gtk_widget_show(w_frameskip);
888     menu = gtk_menu_new();
889     add_menu_item(menu, STR_REF_5HZ_LAB, GTK_SIGNAL_FUNC(mn_5hz));
890     add_menu_item(menu, STR_REF_7_5HZ_LAB, GTK_SIGNAL_FUNC(mn_7hz));
891     add_menu_item(menu, STR_REF_10HZ_LAB, GTK_SIGNAL_FUNC(mn_10hz));
892     add_menu_item(menu, STR_REF_15HZ_LAB, GTK_SIGNAL_FUNC(mn_15hz));
893     add_menu_item(menu, STR_REF_30HZ_LAB, GTK_SIGNAL_FUNC(mn_30hz));
894     add_menu_item(menu, STR_REF_60HZ_LAB, GTK_SIGNAL_FUNC(mn_60hz));
895     int frameskip = PrefsFindInt32("frameskip");
896     int item = -1;
897 cebix 1.1 switch (frameskip) {
898 gbeauche 1.13 case 12: item = 0; break;
899     case 8: item = 1; break;
900     case 6: item = 2; break;
901     case 4: item = 3; break;
902     case 2: item = 4; break;
903     case 1: item = 5; break;
904     case 0: item = 5; break;
905 cebix 1.1 }
906 gbeauche 1.13 if (item >= 0)
907     gtk_menu_set_active(GTK_MENU(menu), item);
908     gtk_option_menu_set_menu(GTK_OPTION_MENU(w_frameskip), menu);
909     gtk_table_attach(GTK_TABLE(table), w_frameskip, 1, 2, 1, 2, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
910    
911     l_display_x = gtk_label_new(GetString(STR_DISPLAY_X_CTRL));
912     gtk_widget_show(l_display_x);
913     gtk_table_attach(GTK_TABLE(table), l_display_x, 0, 1, 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
914 cebix 1.1
915 gbeauche 1.13 combo = gtk_combo_new();
916     gtk_widget_show(combo);
917     GList *glist1 = NULL;
918     glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_512_LAB));
919     glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_640_LAB));
920     glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_800_LAB));
921     glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_1024_LAB));
922     glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_MAX_LAB));
923     gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist1);
924     if (dis_width)
925     sprintf(str, "%d", dis_width);
926     else
927     strcpy(str, GetString(STR_SIZE_MAX_LAB));
928     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
929     gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 2, 3, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
930     w_display_x = GTK_COMBO(combo)->entry;
931 gbeauche 1.9
932 gbeauche 1.13 l_display_y = gtk_label_new(GetString(STR_DISPLAY_Y_CTRL));
933     gtk_widget_show(l_display_y);
934     gtk_table_attach(GTK_TABLE(table), l_display_y, 0, 1, 3, 4, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
935 cebix 1.1
936 gbeauche 1.13 combo = gtk_combo_new();
937     gtk_widget_show(combo);
938     GList *glist2 = NULL;
939     glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_384_LAB));
940     glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_480_LAB));
941     glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_600_LAB));
942     glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_768_LAB));
943     glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_MAX_LAB));
944     gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist2);
945     if (dis_height)
946     sprintf(str, "%d", dis_height);
947     else
948     strcpy(str, GetString(STR_SIZE_MAX_LAB));
949     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
950     gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 3, 4, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
951     w_display_y = GTK_COMBO(combo)->entry;
952 cebix 1.1
953 gbeauche 1.13 make_checkbox(box, STR_GFXACCEL_CTRL, PrefsFindBool("gfxaccel"), GTK_SIGNAL_FUNC(tb_gfxaccel));
954 cebix 1.1
955 gbeauche 1.4 make_separator(box);
956 cebix 1.1 make_checkbox(box, STR_NOSOUND_CTRL, "nosound", GTK_SIGNAL_FUNC(tb_nosound));
957 gbeauche 1.4 w_dspdevice_file = make_entry(box, STR_DSPDEVICE_FILE_CTRL, "dsp");
958     w_mixerdevice_file = make_entry(box, STR_MIXERDEVICE_FILE_CTRL, "mixer");
959    
960     set_graphics_sensitive();
961 gbeauche 1.13
962     hide_show_graphics_widgets();
963 gbeauche 1.2 }
964    
965    
966     /*
967     * "Input" pane
968     */
969    
970     static GtkWidget *w_keycode_file;
971 gbeauche 1.5 static GtkWidget *w_mouse_wheel_lines;
972 gbeauche 1.2
973     // Set sensitivity of widgets
974     static void set_input_sensitive(void)
975     {
976 gbeauche 1.15 const bool use_keycodes = PrefsFindBool("keycodes");
977     gtk_widget_set_sensitive(w_keycode_file, use_keycodes);
978     gtk_widget_set_sensitive(GTK_WIDGET(g_object_get_data(G_OBJECT(w_keycode_file), "chooser_button")), use_keycodes);
979 gbeauche 1.5 gtk_widget_set_sensitive(w_mouse_wheel_lines, PrefsFindInt32("mousewheelmode") == 1);
980 gbeauche 1.2 }
981    
982     // "Use Raw Keycodes" button toggled
983     static void tb_keycodes(GtkWidget *widget)
984     {
985     PrefsReplaceBool("keycodes", GTK_TOGGLE_BUTTON(widget)->active);
986     set_input_sensitive();
987     }
988    
989 gbeauche 1.5 // "Mouse Wheel Mode" selected
990     static void mn_wheel_page(...) {PrefsReplaceInt32("mousewheelmode", 0); set_input_sensitive();}
991     static void mn_wheel_cursor(...) {PrefsReplaceInt32("mousewheelmode", 1); set_input_sensitive();}
992    
993 gbeauche 1.2 // Read settings from widgets and set preferences
994     static void read_input_settings(void)
995     {
996     const char *str = get_file_entry_path(w_keycode_file);
997     if (str && strlen(str))
998     PrefsReplaceString("keycodefile", str);
999     else
1000     PrefsRemoveItem("keycodefile");
1001 gbeauche 1.5
1002     PrefsReplaceInt32("mousewheellines", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w_mouse_wheel_lines)));
1003 gbeauche 1.2 }
1004    
1005     // Create "Input" pane
1006     static void create_input_pane(GtkWidget *top)
1007     {
1008 gbeauche 1.15 GtkWidget *box, *hbox, *menu, *label, *button;
1009 gbeauche 1.2 GtkObject *adj;
1010    
1011     box = make_pane(top, STR_INPUT_PANE_TITLE);
1012    
1013     make_checkbox(box, STR_KEYCODES_CTRL, "keycodes", GTK_SIGNAL_FUNC(tb_keycodes));
1014 gbeauche 1.15
1015     hbox = gtk_hbox_new(FALSE, 4);
1016     gtk_widget_show(hbox);
1017     gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1018    
1019     label = gtk_label_new(GetString(STR_KEYCODES_CTRL));
1020     gtk_widget_show(label);
1021     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1022    
1023     const char *str = PrefsFindString("keycodefile");
1024     if (str == NULL)
1025     str = "";
1026    
1027     w_keycode_file = gtk_entry_new();
1028     gtk_entry_set_text(GTK_ENTRY(w_keycode_file), str);
1029     gtk_widget_show(w_keycode_file);
1030     gtk_box_pack_start(GTK_BOX(hbox), w_keycode_file, TRUE, TRUE, 0);
1031    
1032     button = make_browse_button(w_keycode_file);
1033     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1034     g_object_set_data(G_OBJECT(w_keycode_file), "chooser_button", button);
1035 gbeauche 1.5
1036     make_separator(box);
1037    
1038     static const opt_desc options[] = {
1039     {STR_MOUSEWHEELMODE_PAGE_LAB, GTK_SIGNAL_FUNC(mn_wheel_page)},
1040     {STR_MOUSEWHEELMODE_CURSOR_LAB, GTK_SIGNAL_FUNC(mn_wheel_cursor)},
1041     {0, NULL}
1042     };
1043     int wheelmode = PrefsFindInt32("mousewheelmode"), active = 0;
1044     switch (wheelmode) {
1045     case 0: active = 0; break;
1046     case 1: active = 1; break;
1047     }
1048     menu = make_option_menu(box, STR_MOUSEWHEELMODE_CTRL, options, active);
1049    
1050     hbox = gtk_hbox_new(FALSE, 4);
1051     gtk_widget_show(hbox);
1052     gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1053    
1054     label = gtk_label_new(GetString(STR_MOUSEWHEELLINES_CTRL));
1055     gtk_widget_show(label);
1056     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1057    
1058     adj = gtk_adjustment_new(PrefsFindInt32("mousewheellines"), 1, 1000, 1, 5, 0);
1059     w_mouse_wheel_lines = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0.0, 0);
1060     gtk_widget_show(w_mouse_wheel_lines);
1061     gtk_box_pack_start(GTK_BOX(hbox), w_mouse_wheel_lines, FALSE, FALSE, 0);
1062 gbeauche 1.2
1063     set_input_sensitive();
1064 cebix 1.1 }
1065    
1066    
1067     /*
1068     * "Serial/Network" pane
1069     */
1070    
1071     static GtkWidget *w_seriala, *w_serialb, *w_ether;
1072    
1073     // Read settings from widgets and set preferences
1074     static void read_serial_settings(void)
1075     {
1076     const char *str;
1077    
1078     str = gtk_entry_get_text(GTK_ENTRY(w_seriala));
1079     PrefsReplaceString("seriala", str);
1080    
1081     str = gtk_entry_get_text(GTK_ENTRY(w_serialb));
1082     PrefsReplaceString("serialb", str);
1083    
1084     str = gtk_entry_get_text(GTK_ENTRY(w_ether));
1085     if (str && strlen(str))
1086     PrefsReplaceString("ether", str);
1087     else
1088     PrefsRemoveItem("ether");
1089     }
1090    
1091     // Add names of serial devices
1092     static gint gl_str_cmp(gconstpointer a, gconstpointer b)
1093     {
1094     return strcmp((char *)a, (char *)b);
1095     }
1096    
1097     static GList *add_serial_names(void)
1098     {
1099     GList *glist = NULL;
1100    
1101     // Search /dev for ttyS* and lp*
1102     DIR *d = opendir("/dev");
1103     if (d) {
1104     struct dirent *de;
1105     while ((de = readdir(d)) != NULL) {
1106 gbeauche 1.7 #if defined(__linux__)
1107 cebix 1.1 if (strncmp(de->d_name, "ttyS", 4) == 0 || strncmp(de->d_name, "lp", 2) == 0) {
1108 gbeauche 1.7 #elif defined(__FreeBSD__)
1109     if (strncmp(de->d_name, "cuaa", 4) == 0 || strncmp(de->d_name, "lpt", 3) == 0) {
1110     #elif defined(__NetBSD__)
1111     if (strncmp(de->d_name, "tty0", 4) == 0 || strncmp(de->d_name, "lpt", 3) == 0) {
1112     #elif defined(sgi)
1113     if (strncmp(de->d_name, "ttyf", 4) == 0 || strncmp(de->d_name, "plp", 3) == 0) {
1114     #else
1115     if (false) {
1116     #endif
1117 cebix 1.1 char *str = new char[64];
1118     sprintf(str, "/dev/%s", de->d_name);
1119     glist = g_list_append(glist, str);
1120     }
1121     }
1122     closedir(d);
1123     }
1124     if (glist)
1125     g_list_sort(glist, gl_str_cmp);
1126     else
1127     glist = g_list_append(glist, (void *)"<none>");
1128     return glist;
1129     }
1130    
1131     // Add names of ethernet interfaces
1132     static GList *add_ether_names(void)
1133     {
1134     GList *glist = NULL;
1135    
1136     // Get list of all Ethernet interfaces
1137     int s = socket(PF_INET, SOCK_DGRAM, 0);
1138     if (s >= 0) {
1139     char inbuf[8192];
1140     struct ifconf ifc;
1141     ifc.ifc_len = sizeof(inbuf);
1142     ifc.ifc_buf = inbuf;
1143     if (ioctl(s, SIOCGIFCONF, &ifc) == 0) {
1144     struct ifreq req, *ifr = ifc.ifc_req;
1145     for (int i=0; i<ifc.ifc_len; i+=sizeof(ifreq), ifr++) {
1146     req = *ifr;
1147 gbeauche 1.7 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(sgi)
1148     if (ioctl(s, SIOCGIFADDR, &req) == 0 && (req.ifr_addr.sa_family == ARPHRD_ETHER || req.ifr_addr.sa_family == ARPHRD_ETHER+1)) {
1149     #elif defined(__linux__)
1150 cebix 1.1 if (ioctl(s, SIOCGIFHWADDR, &req) == 0 && req.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
1151 gbeauche 1.7 #else
1152     if (false) {
1153     #endif
1154 cebix 1.1 char *str = new char[64];
1155     strncpy(str, ifr->ifr_name, 63);
1156     glist = g_list_append(glist, str);
1157     }
1158     }
1159     }
1160     close(s);
1161     }
1162 gbeauche 1.15 #ifdef HAVE_SLIRP
1163     static char s_slirp[] = "slirp";
1164     glist = g_list_append(glist, s_slirp);
1165     #endif
1166 cebix 1.1 if (glist)
1167     g_list_sort(glist, gl_str_cmp);
1168     else
1169     glist = g_list_append(glist, (void *)"<none>");
1170     return glist;
1171     }
1172    
1173     // Create "Serial/Network" pane
1174     static void create_serial_pane(GtkWidget *top)
1175     {
1176     GtkWidget *box, *table, *label, *combo;
1177     GList *glist = add_serial_names();
1178    
1179     box = make_pane(top, STR_SERIAL_NETWORK_PANE_TITLE);
1180     table = make_table(box, 2, 3);
1181    
1182     label = gtk_label_new(GetString(STR_SERPORTA_CTRL));
1183     gtk_widget_show(label);
1184     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
1185    
1186     combo = gtk_combo_new();
1187     gtk_widget_show(combo);
1188     gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
1189     const char *str = PrefsFindString("seriala");
1190     if (str == NULL)
1191     str = "";
1192     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
1193     gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
1194     w_seriala = GTK_COMBO(combo)->entry;
1195    
1196     label = gtk_label_new(GetString(STR_SERPORTB_CTRL));
1197     gtk_widget_show(label);
1198     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
1199    
1200     combo = gtk_combo_new();
1201     gtk_widget_show(combo);
1202     gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
1203     str = PrefsFindString("serialb");
1204     if (str == NULL)
1205     str = "";
1206     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
1207     gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
1208     w_serialb = GTK_COMBO(combo)->entry;
1209    
1210     label = gtk_label_new(GetString(STR_ETHERNET_IF_CTRL));
1211     gtk_widget_show(label);
1212     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
1213    
1214     glist = add_ether_names();
1215     combo = gtk_combo_new();
1216     gtk_widget_show(combo);
1217     gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
1218     str = PrefsFindString("ether");
1219     if (str == NULL)
1220     str = "";
1221     gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
1222     gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 2, 3, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
1223     w_ether = GTK_COMBO(combo)->entry;
1224     }
1225    
1226    
1227     /*
1228     * "Memory/Misc" pane
1229     */
1230    
1231 gbeauche 1.15 static GtkWidget *w_ramsize;
1232 cebix 1.1 static GtkWidget *w_rom_file;
1233    
1234 gbeauche 1.9 // Don't use CPU when idle?
1235     static void tb_idlewait(GtkWidget *widget)
1236     {
1237     PrefsReplaceBool("idlewait", GTK_TOGGLE_BUTTON(widget)->active);
1238     }
1239    
1240 cebix 1.1 // "Ignore SEGV" button toggled
1241     static void tb_ignoresegv(GtkWidget *widget)
1242     {
1243     PrefsReplaceBool("ignoresegv", GTK_TOGGLE_BUTTON(widget)->active);
1244     }
1245    
1246     // Read settings from widgets and set preferences
1247     static void read_memory_settings(void)
1248     {
1249 gbeauche 1.15 const char *str = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(w_ramsize)->entry));
1250     PrefsReplaceInt32("ramsize", atoi(str) << 20);
1251 cebix 1.1
1252 gbeauche 1.15 str = gtk_entry_get_text(GTK_ENTRY(w_rom_file));
1253 cebix 1.1 if (str && strlen(str))
1254     PrefsReplaceString("rom", str);
1255     else
1256     PrefsRemoveItem("rom");
1257     }
1258    
1259     // Create "Memory/Misc" pane
1260     static void create_memory_pane(GtkWidget *top)
1261     {
1262 gbeauche 1.15 GtkWidget *box, *hbox, *table, *label, *menu;
1263 cebix 1.1
1264     box = make_pane(top, STR_MEMORY_MISC_PANE_TITLE);
1265 gbeauche 1.15 table = make_table(box, 2, 5);
1266 cebix 1.1
1267 gbeauche 1.15 static const combo_desc options[] = {
1268     STR_RAMSIZE_4MB_LAB,
1269     STR_RAMSIZE_8MB_LAB,
1270     STR_RAMSIZE_16MB_LAB,
1271     STR_RAMSIZE_32MB_LAB,
1272     STR_RAMSIZE_64MB_LAB,
1273     STR_RAMSIZE_128MB_LAB,
1274     STR_RAMSIZE_256MB_LAB,
1275     STR_RAMSIZE_512MB_LAB,
1276     0
1277     };
1278     char default_ramsize[10];
1279     sprintf(default_ramsize, "%d", PrefsFindInt32("ramsize") >> 20);
1280     w_ramsize = table_make_combobox(table, 0, STR_RAMSIZE_CTRL, default_ramsize, options);
1281 cebix 1.1
1282 gbeauche 1.15 w_rom_file = table_make_file_entry(table, 1, STR_ROM_FILE_CTRL, "rom");
1283 cebix 1.1
1284     make_checkbox(box, STR_IGNORESEGV_CTRL, "ignoresegv", GTK_SIGNAL_FUNC(tb_ignoresegv));
1285 gbeauche 1.9 make_checkbox(box, STR_IDLEWAIT_CTRL, "idlewait", GTK_SIGNAL_FUNC(tb_idlewait));
1286 cebix 1.1 }
1287    
1288    
1289     /*
1290     * Read settings from widgets and set preferences
1291     */
1292    
1293     static void read_settings(void)
1294     {
1295     read_volumes_settings();
1296     read_graphics_settings();
1297 gbeauche 1.17 read_input_settings();
1298 cebix 1.1 read_serial_settings();
1299     read_memory_settings();
1300 gbeauche 1.3 read_jit_settings();
1301 cebix 1.1 }
1302 gbeauche 1.17
1303    
1304     #ifdef STANDALONE_GUI
1305     #include <errno.h>
1306     #include <sys/wait.h>
1307     #include "rpc.h"
1308    
1309     /*
1310     * Fake unused data and functions
1311     */
1312    
1313     uint8 XPRAM[XPRAM_SIZE];
1314     void MountVolume(void *fh) { }
1315     void FileDiskLayout(loff_t size, uint8 *data, loff_t &start_byte, loff_t &real_size) { }
1316    
1317     #if defined __APPLE__ && defined __MACH__
1318     void DarwinAddCDROMPrefs(void) { }
1319     void DarwinAddFloppyPrefs(void) { }
1320     void DarwinAddSerialPrefs(void) { }
1321     bool DarwinCDReadTOC(char *, uint8 *) { }
1322     #endif
1323    
1324    
1325     /*
1326     * Display alert
1327     */
1328    
1329     static void dl_destroyed(void)
1330     {
1331     gtk_main_quit();
1332     }
1333    
1334     static void display_alert(int title_id, int prefix_id, int button_id, const char *text)
1335     {
1336     char str[256];
1337     sprintf(str, GetString(prefix_id), text);
1338    
1339     GtkWidget *dialog = gtk_dialog_new();
1340     gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
1341     gtk_container_border_width(GTK_CONTAINER(dialog), 5);
1342     gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
1343     gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
1344    
1345     GtkWidget *label = gtk_label_new(str);
1346     gtk_widget_show(label);
1347     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1348    
1349     GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
1350     gtk_widget_show(button);
1351     gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
1352     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
1353     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1354     gtk_widget_grab_default(button);
1355     gtk_widget_show(dialog);
1356    
1357     gtk_main();
1358     }
1359    
1360    
1361     /*
1362     * Display error alert
1363     */
1364    
1365     void ErrorAlert(const char *text)
1366     {
1367     display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
1368     }
1369    
1370    
1371     /*
1372     * Display warning alert
1373     */
1374    
1375     void WarningAlert(const char *text)
1376     {
1377     display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
1378     }
1379    
1380    
1381     /*
1382     * RPC handlers
1383     */
1384    
1385     static GMainLoop *g_gui_loop;
1386    
1387     static int handle_ErrorAlert(rpc_connection_t *connection)
1388     {
1389     D(bug("handle_ErrorAlert\n"));
1390    
1391     int error;
1392     char *str;
1393     if ((error = rpc_method_get_args(connection, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID)) < 0)
1394     return error;
1395    
1396     ErrorAlert(str);
1397     free(str);
1398     return RPC_ERROR_NO_ERROR;
1399     }
1400    
1401     static int handle_WarningAlert(rpc_connection_t *connection)
1402     {
1403     D(bug("handle_WarningAlert\n"));
1404    
1405     int error;
1406     char *str;
1407     if ((error = rpc_method_get_args(connection, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID)) < 0)
1408     return error;
1409    
1410     WarningAlert(str);
1411     free(str);
1412     return RPC_ERROR_NO_ERROR;
1413     }
1414    
1415     static int handle_Exit(rpc_connection_t *connection)
1416     {
1417     D(bug("handle_Exit\n"));
1418    
1419     g_main_quit(g_gui_loop);
1420     return RPC_ERROR_NO_ERROR;
1421     }
1422    
1423    
1424     /*
1425     * SIGCHLD handler
1426     */
1427    
1428     static char g_app_path[PATH_MAX];
1429     static rpc_connection_t *g_gui_connection = NULL;
1430    
1431     static void sigchld_handler(int sig, siginfo_t *sip, void *)
1432     {
1433     D(bug("Child %d exitted with status = %x\n", sip->si_pid, sip->si_status));
1434    
1435     // XXX perform a new wait because sip->si_status is sometimes not
1436     // the exit _value_ on MacOS X but rather the usual status field
1437     // from waitpid() -- we could arrange this code in some other way...
1438     int status;
1439     if (waitpid(sip->si_pid, &status, 0) < 0)
1440     status = sip->si_status;
1441     if (WIFEXITED(status))
1442     status = WEXITSTATUS(status);
1443     if (status & 0x80)
1444     status |= -1 ^0xff;
1445    
1446     if (status < 0) { // negative -> execlp/-errno
1447     char str[256];
1448     sprintf(str, GetString(STR_NO_B2_EXE_FOUND), g_app_path, strerror(-status));
1449     ErrorAlert(str);
1450     status = 1;
1451     }
1452    
1453     if (status != 0) {
1454     if (g_gui_connection)
1455     rpc_exit(g_gui_connection);
1456     exit(status);
1457     }
1458     }
1459    
1460    
1461     /*
1462     * Start standalone GUI
1463     */
1464    
1465     int main(int argc, char *argv[])
1466     {
1467     // Init GTK
1468     gtk_set_locale();
1469     gtk_init(&argc, &argv);
1470    
1471     // Read preferences
1472     PrefsInit(argc, argv);
1473    
1474     // Show preferences editor
1475     bool start = PrefsEditor();
1476    
1477     // Exit preferences
1478     PrefsExit();
1479    
1480     // Transfer control to the executable
1481     if (start) {
1482     char gui_connection_path[64];
1483     sprintf(gui_connection_path, "/org/SheepShaver/GUI/%d", getpid());
1484    
1485     // Catch exits from the child process
1486     struct sigaction sigchld_sa, old_sigchld_sa;
1487     sigemptyset(&sigchld_sa.sa_mask);
1488     sigchld_sa.sa_sigaction = sigchld_handler;
1489     sigchld_sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1490     if (sigaction(SIGCHLD, &sigchld_sa, &old_sigchld_sa) < 0) {
1491     char str[256];
1492     sprintf(str, GetString(STR_SIG_INSTALL_ERR), SIGCHLD, strerror(errno));
1493     ErrorAlert(str);
1494     return 1;
1495     }
1496    
1497     // Search and run the SheepShaver executable
1498     char *p;
1499     strcpy(g_app_path, argv[0]);
1500     if ((p = strstr(g_app_path, "SheepShaverGUI.app/Contents/MacOS")) != NULL) {
1501     strcpy(p, "SheepShaver.app/Contents/MacOS/SheepShaver");
1502     if (access(g_app_path, X_OK) < 0) {
1503     char str[256];
1504     sprintf(str, GetString(STR_NO_B2_EXE_FOUND), g_app_path, strerror(errno));
1505     WarningAlert(str);
1506     strcpy(g_app_path, "/Applications/SheepShaver.app/Contents/MacOS/SheepShaver");
1507     }
1508     } else {
1509     p = strrchr(g_app_path, '/');
1510     p = p ? p + 1 : g_app_path;
1511     strcpy(p, "SheepShaver");
1512     }
1513    
1514     int pid = fork();
1515     if (pid == 0) {
1516     D(bug("Trying to execute %s\n", g_app_path));
1517     execlp(g_app_path, g_app_path, "--gui-connection", gui_connection_path, (char *)NULL);
1518     #ifdef _POSIX_PRIORITY_SCHEDULING
1519     // XXX get a chance to run the parent process so that to not confuse/upset GTK...
1520     sched_yield();
1521     #endif
1522     _exit(-errno);
1523     }
1524    
1525     // Establish a connection to Basilisk II
1526     if ((g_gui_connection = rpc_init_server(gui_connection_path)) == NULL) {
1527     printf("ERROR: failed to initialize GUI-side RPC server connection\n");
1528     return 1;
1529     }
1530     static const rpc_method_descriptor_t vtable[] = {
1531     { RPC_METHOD_ERROR_ALERT, handle_ErrorAlert },
1532     { RPC_METHOD_WARNING_ALERT, handle_WarningAlert },
1533     { RPC_METHOD_EXIT, handle_Exit }
1534     };
1535     if (rpc_method_add_callbacks(g_gui_connection, vtable, sizeof(vtable) / sizeof(vtable[0])) < 0) {
1536     printf("ERROR: failed to setup GUI method callbacks\n");
1537     return 1;
1538     }
1539     int socket;
1540     if ((socket = rpc_listen_socket(g_gui_connection)) < 0) {
1541     printf("ERROR: failed to initialize RPC server thread\n");
1542     return 1;
1543     }
1544    
1545     g_gui_loop = g_main_new(TRUE);
1546     while (g_main_is_running(g_gui_loop)) {
1547    
1548     // Process a few events pending
1549     const int N_EVENTS_DISPATCH = 10;
1550     for (int i = 0; i < N_EVENTS_DISPATCH; i++) {
1551     if (!g_main_iteration(FALSE))
1552     break;
1553     }
1554    
1555     // Check for RPC events (100 ms timeout)
1556     int ret = rpc_wait_dispatch(g_gui_connection, 100000);
1557     if (ret == 0)
1558     continue;
1559     if (ret < 0)
1560     break;
1561     rpc_dispatch(g_gui_connection);
1562     }
1563    
1564     rpc_exit(g_gui_connection);
1565     return 0;
1566     }
1567    
1568     return 0;
1569     }
1570     #endif