ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/SheepShaver/src/Unix/prefs_editor_gtk.cpp
Revision: 1.21
Committed: 2008-01-01T09:47:38Z (16 years, 4 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.20: +2 -2 lines
Log Message:
Happy New Year!

File Contents

# Content
1 /*
2 * prefs_editor_linux.cpp - Preferences editor, Linux implementation using GTK+
3 *
4 * SheepShaver (C) 1997-2008 Christian Bauer and Marc Hellwig
5 *
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 #define DEBUG 0
39 #include "debug.h"
40
41
42 // Global variables
43 static GtkWidget *win; // Preferences window
44 static bool start_clicked = false; // Return value of PrefsEditor() function
45 static int screen_width, screen_height; // Screen dimensions
46
47
48 // Prototypes
49 static void create_volumes_pane(GtkWidget *top);
50 static void create_graphics_pane(GtkWidget *top);
51 static void create_input_pane(GtkWidget *top);
52 static void create_serial_pane(GtkWidget *top);
53 static void create_memory_pane(GtkWidget *top);
54 static void create_jit_pane(GtkWidget *top);
55 static void read_settings(void);
56
57
58 /*
59 * Utility functions
60 */
61
62 #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 struct opt_desc {
69 int label_id;
70 GtkSignalFunc func;
71 };
72
73 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 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_container_border_width(GTK_CONTAINER(frame), 4);
124
125 box = gtk_vbox_new(FALSE, 4);
126 gtk_container_set_border_width(GTK_CONTAINER(box), 4);
127 gtk_container_add(GTK_CONTAINER(frame), box);
128
129 gtk_widget_show_all(frame);
130
131 label = gtk_label_new(GetString(title_id));
132 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), frame, label);
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 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 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 static const gchar *get_file_entry_path(GtkWidget *entry)
303 {
304 return gtk_entry_get_text(GTK_ENTRY(entry));
305 }
306
307 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 "Copyright (C) 1997-2008 Christian Bauer and Marc Hellwig\n"
376 "E-mail: cb@cebix.net\n"
377 "http://sheepshaver.cebix.net/\n\n"
378 "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 {(gchar *)GetString(STR_PREFS_ITEM_START_GTK), "<control>S", GTK_SIGNAL_FUNC(cb_start), 0, NULL},
414 {(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 // Get screen dimensions
424 screen_width = gdk_screen_width();
425 screen_height = gdk_screen_height();
426
427 // 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 #if GTK_CHECK_VERSION(1,3,15)
442 gtk_window_add_accel_group(GTK_WINDOW(win), accel_group);
443 #else
444 gtk_accel_group_attach(accel_group, GTK_OBJECT(win));
445 #endif
446 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_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);
452 gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), FALSE);
453 gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, 0);
454 gtk_widget_realize(notebook);
455
456 create_volumes_pane(notebook);
457 create_graphics_pane(notebook);
458 create_input_pane(notebook);
459 create_serial_pane(notebook);
460 create_memory_pane(notebook);
461 create_jit_pane(notebook);
462 gtk_widget_show(notebook);
463
464 static const opt_desc buttons[] = {
465 {STR_START_BUTTON, GTK_SIGNAL_FUNC(cb_start)},
466 {STR_QUIT_BUTTON, GTK_SIGNAL_FUNC(cb_quit)},
467 {0, NULL}
468 };
469 make_button_box(box, 4, buttons);
470
471 // Show window and enter main loop
472 gtk_widget_show(win);
473 gtk_main();
474 return start_clicked;
475 }
476
477
478 /*
479 * "Volumes" pane
480 */
481
482 static GtkWidget *volume_list, *w_extfs;
483 static int selected_volume;
484
485 // Volume in list selected
486 static void cl_selected(GtkWidget *list, int row, int column)
487 {
488 selected_volume = row;
489 }
490
491 // Volume selected for addition
492 static void add_volume_ok(GtkWidget *button, file_req_assoc *assoc)
493 {
494 gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
495 gtk_clist_append(GTK_CLIST(volume_list), &file);
496 gtk_widget_destroy(assoc->req);
497 delete assoc;
498 }
499
500 // Volume selected for creation
501 static void create_volume_ok(GtkWidget *button, file_req_assoc *assoc)
502 {
503 gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
504
505 const gchar *str = gtk_entry_get_text(GTK_ENTRY(assoc->entry));
506 int size = atoi(str);
507
508 char cmd[1024];
509 sprintf(cmd, "dd if=/dev/zero \"of=%s\" bs=1024k count=%d", file, size);
510 int ret = system(cmd);
511 if (ret == 0)
512 gtk_clist_append(GTK_CLIST(volume_list), &file);
513 gtk_widget_destroy(GTK_WIDGET(assoc->req));
514 delete assoc;
515 }
516
517 // "Add Volume" button clicked
518 static void cb_add_volume(...)
519 {
520 GtkWidget *req = gtk_file_selection_new(GetString(STR_ADD_VOLUME_TITLE));
521 gtk_signal_connect_object(GTK_OBJECT(req), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
522 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(req)->ok_button), "clicked", GTK_SIGNAL_FUNC(add_volume_ok), new file_req_assoc(req, NULL));
523 gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(req)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
524 gtk_widget_show(req);
525 }
526
527 // "Create Hardfile" button clicked
528 static void cb_create_volume(...)
529 {
530 GtkWidget *req = gtk_file_selection_new(GetString(STR_CREATE_VOLUME_TITLE));
531
532 GtkWidget *box = gtk_hbox_new(FALSE, 4);
533 gtk_widget_show(box);
534 GtkWidget *label = gtk_label_new(GetString(STR_HARDFILE_SIZE_CTRL));
535 gtk_widget_show(label);
536 GtkWidget *entry = gtk_entry_new();
537 gtk_widget_show(entry);
538 char str[32];
539 sprintf(str, "%d", 40);
540 gtk_entry_set_text(GTK_ENTRY(entry), str);
541 gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
542 gtk_box_pack_start(GTK_BOX(box), entry, FALSE, FALSE, 0);
543 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(req)->main_vbox), box, FALSE, FALSE, 0);
544
545 gtk_signal_connect_object(GTK_OBJECT(req), "delete_event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
546 gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(req)->ok_button), "clicked", GTK_SIGNAL_FUNC(create_volume_ok), new file_req_assoc(req, entry));
547 gtk_signal_connect_object(GTK_OBJECT(GTK_FILE_SELECTION(req)->cancel_button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(req));
548 gtk_widget_show(req);
549 }
550
551 // "Remove Volume" button clicked
552 static void cb_remove_volume(...)
553 {
554 gtk_clist_remove(GTK_CLIST(volume_list), selected_volume);
555 }
556
557 // "Boot From" selected
558 static void mn_boot_any(...) {PrefsReplaceInt32("bootdriver", 0);}
559 static void mn_boot_cdrom(...) {PrefsReplaceInt32("bootdriver", CDROMRefNum);}
560
561 // "No CD-ROM Driver" button toggled
562 static void tb_nocdrom(GtkWidget *widget)
563 {
564 PrefsReplaceBool("nocdrom", GTK_TOGGLE_BUTTON(widget)->active);
565 }
566
567 // Read settings from widgets and set preferences
568 static void read_volumes_settings(void)
569 {
570 while (PrefsFindString("disk"))
571 PrefsRemoveItem("disk");
572
573 for (int i=0; i<GTK_CLIST(volume_list)->rows; i++) {
574 char *str;
575 gtk_clist_get_text(GTK_CLIST(volume_list), i, 0, &str);
576 PrefsAddString("disk", str);
577 }
578
579 PrefsReplaceString("extfs", gtk_entry_get_text(GTK_ENTRY(w_extfs)));
580 }
581
582 // Create "Volumes" pane
583 static void create_volumes_pane(GtkWidget *top)
584 {
585 GtkWidget *box, *scroll, *menu;
586
587 box = make_pane(top, STR_VOLUMES_PANE_TITLE);
588
589 scroll = gtk_scrolled_window_new(NULL, NULL);
590 gtk_widget_show(scroll);
591 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
592 volume_list = gtk_clist_new(1);
593 gtk_widget_show(volume_list);
594 gtk_clist_set_selection_mode(GTK_CLIST(volume_list), GTK_SELECTION_SINGLE);
595 gtk_clist_set_shadow_type(GTK_CLIST(volume_list), GTK_SHADOW_NONE);
596 gtk_clist_set_reorderable(GTK_CLIST(volume_list), true);
597 gtk_signal_connect(GTK_OBJECT(volume_list), "select_row", GTK_SIGNAL_FUNC(cl_selected), NULL);
598 char *str;
599 int32 index = 0;
600 while ((str = (char *)PrefsFindString("disk", index++)) != NULL)
601 gtk_clist_append(GTK_CLIST(volume_list), &str);
602 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), volume_list);
603 gtk_box_pack_start(GTK_BOX(box), scroll, TRUE, TRUE, 0);
604 selected_volume = 0;
605
606 static const opt_desc buttons[] = {
607 {STR_ADD_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_add_volume)},
608 {STR_CREATE_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_create_volume)},
609 {STR_REMOVE_VOLUME_BUTTON, GTK_SIGNAL_FUNC(cb_remove_volume)},
610 {0, NULL},
611 };
612 make_button_box(box, 0, buttons);
613 make_separator(box);
614
615 w_extfs = make_entry(box, STR_EXTFS_CTRL, "extfs");
616
617 static const opt_desc options[] = {
618 {STR_BOOT_ANY_LAB, GTK_SIGNAL_FUNC(mn_boot_any)},
619 {STR_BOOT_CDROM_LAB, GTK_SIGNAL_FUNC(mn_boot_cdrom)},
620 {0, NULL}
621 };
622 int bootdriver = PrefsFindInt32("bootdriver"), active = 0;
623 switch (bootdriver) {
624 case 0: active = 0; break;
625 case CDROMRefNum: active = 1; break;
626 }
627 menu = make_option_menu(box, STR_BOOTDRIVER_CTRL, options, active);
628
629 make_checkbox(box, STR_NOCDROM_CTRL, "nocdrom", GTK_SIGNAL_FUNC(tb_nocdrom));
630 }
631
632
633 /*
634 * "JIT Compiler" pane
635 */
636
637 // Are we running a JIT capable CPU?
638 static bool is_jit_capable(void)
639 {
640 #if USE_JIT
641 return true;
642 #elif defined __APPLE__ && defined __MACH__
643 // XXX run-time detect so that we can use a PPC GUI prefs editor
644 static char cpu[10];
645 if (cpu[0] == 0) {
646 FILE *fp = popen("uname -p", "r");
647 if (fp == NULL)
648 return false;
649 fgets(cpu, sizeof(cpu) - 1, fp);
650 fclose(fp);
651 }
652 if (cpu[0] == 'i' && cpu[2] == '8' && cpu[3] == '6') // XXX assuming i?86
653 return true;
654 #endif
655 return false;
656 }
657
658 // Set sensitivity of widgets
659 static void set_jit_sensitive(void)
660 {
661 const bool jit_enabled = PrefsFindBool("jit");
662 }
663
664 // "Use JIT Compiler" button toggled
665 static void tb_jit(GtkWidget *widget)
666 {
667 PrefsReplaceBool("jit", GTK_TOGGLE_BUTTON(widget)->active);
668 set_jit_sensitive();
669 }
670
671 // Read settings from widgets and set preferences
672 static void read_jit_settings(void)
673 {
674 bool jit_enabled = is_jit_capable() && PrefsFindBool("jit");
675 }
676
677 // "Use built-in 68k DR emulator" button toggled
678 static void tb_jit_68k(GtkWidget *widget)
679 {
680 PrefsReplaceBool("jit68k", GTK_TOGGLE_BUTTON(widget)->active);
681 }
682
683 // Create "JIT Compiler" pane
684 static void create_jit_pane(GtkWidget *top)
685 {
686 GtkWidget *box, *table, *label, *menu;
687 char str[32];
688
689 box = make_pane(top, STR_JIT_PANE_TITLE);
690
691 if (is_jit_capable()) {
692 make_checkbox(box, STR_JIT_CTRL, "jit", GTK_SIGNAL_FUNC(tb_jit));
693 set_jit_sensitive();
694 }
695
696 make_checkbox(box, STR_JIT_68K_CTRL, "jit68k", GTK_SIGNAL_FUNC(tb_jit_68k));
697 }
698
699
700 /*
701 * "Graphics/Sound" pane
702 */
703
704 // Display types
705 enum {
706 DISPLAY_WINDOW,
707 DISPLAY_SCREEN
708 };
709
710 static GtkWidget *w_frameskip, *w_display_x, *w_display_y;
711 static GtkWidget *l_frameskip, *l_display_x, *l_display_y;
712 static int display_type;
713 static int dis_width, dis_height;
714 static bool is_fbdev_dga_mode = false;
715
716 static GtkWidget *w_dspdevice_file, *w_mixerdevice_file;
717
718 // Hide/show graphics widgets
719 static void hide_show_graphics_widgets(void)
720 {
721 switch (display_type) {
722 case DISPLAY_WINDOW:
723 gtk_widget_show(w_frameskip); gtk_widget_show(l_frameskip);
724 break;
725 case DISPLAY_SCREEN:
726 gtk_widget_hide(w_frameskip); gtk_widget_hide(l_frameskip);
727 break;
728 }
729 }
730
731 // "Window" video type selected
732 static void mn_window(...)
733 {
734 display_type = DISPLAY_WINDOW;
735 hide_show_graphics_widgets();
736 }
737
738 // "Fullscreen" video type selected
739 static void mn_fullscreen(...)
740 {
741 display_type = DISPLAY_SCREEN;
742 hide_show_graphics_widgets();
743 }
744
745 // "5 Hz".."60Hz" selected
746 static void mn_5hz(...) {PrefsReplaceInt32("frameskip", 12);}
747 static void mn_7hz(...) {PrefsReplaceInt32("frameskip", 8);}
748 static void mn_10hz(...) {PrefsReplaceInt32("frameskip", 6);}
749 static void mn_15hz(...) {PrefsReplaceInt32("frameskip", 4);}
750 static void mn_30hz(...) {PrefsReplaceInt32("frameskip", 2);}
751 static void mn_60hz(...) {PrefsReplaceInt32("frameskip", 1);}
752
753 // QuickDraw acceleration
754 static void tb_gfxaccel(GtkWidget *widget)
755 {
756 PrefsReplaceBool("gfxaccel", GTK_TOGGLE_BUTTON(widget)->active);
757 }
758
759 // Set sensitivity of widgets
760 static void set_graphics_sensitive(void)
761 {
762 const bool sound_enabled = !PrefsFindBool("nosound");
763 gtk_widget_set_sensitive(w_dspdevice_file, sound_enabled);
764 gtk_widget_set_sensitive(w_mixerdevice_file, sound_enabled);
765 }
766
767 // "Disable Sound Output" button toggled
768 static void tb_nosound(GtkWidget *widget)
769 {
770 PrefsReplaceBool("nosound", GTK_TOGGLE_BUTTON(widget)->active);
771 set_graphics_sensitive();
772 }
773
774 // Read and convert graphics preferences
775 static void parse_graphics_prefs(void)
776 {
777 display_type = DISPLAY_WINDOW;
778 dis_width = 640;
779 dis_height = 480;
780
781 const char *str = PrefsFindString("screen");
782 if (str) {
783 if (sscanf(str, "win/%d/%d", &dis_width, &dis_height) == 2)
784 display_type = DISPLAY_WINDOW;
785 else if (sscanf(str, "dga/%d/%d", &dis_width, &dis_height) == 2)
786 display_type = DISPLAY_SCREEN;
787 #ifdef ENABLE_FBDEV_DGA
788 else if (sscanf(str, "fbdev/%d/%d", &dis_width, &dis_height) == 2) {
789 is_fbdev_dga_mode = true;
790 display_type = DISPLAY_SCREEN;
791 }
792 #endif
793 }
794 else {
795 uint32 window_modes = PrefsFindInt32("windowmodes");
796 uint32 screen_modes = PrefsFindInt32("screenmodes");
797 if (screen_modes) {
798 display_type = DISPLAY_SCREEN;
799 static const struct {
800 int id;
801 int width;
802 int height;
803 }
804 modes[] = {
805 { 1, 640, 480 },
806 { 2, 800, 600 },
807 { 4, 1024, 768 },
808 { 64, 1152, 768 },
809 { 8, 1152, 900 },
810 { 16, 1280, 1024 },
811 { 32, 1600, 1200 },
812 { 0, }
813 };
814 for (int i = 0; modes[i].id != 0; i++) {
815 if (screen_modes & modes[i].id) {
816 if (modes[i].width <= screen_width && modes[i].height <= screen_height) {
817 dis_width = modes[i].width;
818 dis_height = modes[i].height;
819 }
820 }
821 }
822 }
823 else if (window_modes) {
824 display_type = DISPLAY_WINDOW;
825 if (window_modes & 1)
826 dis_width = 640, dis_height = 480;
827 if (window_modes & 2)
828 dis_width = 800, dis_height = 600;
829 }
830 }
831 if (dis_width == screen_width)
832 dis_width = 0;
833 if (dis_height == screen_height)
834 dis_height = 0;
835 }
836
837 // Read settings from widgets and set preferences
838 static void read_graphics_settings(void)
839 {
840 const char *str;
841
842 str = gtk_entry_get_text(GTK_ENTRY(w_display_x));
843 dis_width = atoi(str);
844
845 str = gtk_entry_get_text(GTK_ENTRY(w_display_y));
846 dis_height = atoi(str);
847
848 char pref[256];
849 bool use_screen_mode = true;
850 switch (display_type) {
851 case DISPLAY_WINDOW:
852 sprintf(pref, "win/%d/%d", dis_width, dis_height);
853 break;
854 case DISPLAY_SCREEN:
855 sprintf(pref, "dga/%d/%d", dis_width, dis_height);
856 break;
857 default:
858 use_screen_mode = false;
859 PrefsRemoveItem("screen");
860 return;
861 }
862 if (use_screen_mode) {
863 PrefsReplaceString("screen", pref);
864 // Old prefs are now migrated
865 PrefsRemoveItem("windowmodes");
866 PrefsRemoveItem("screenmodes");
867 }
868
869 PrefsReplaceString("dsp", get_file_entry_path(w_dspdevice_file));
870 PrefsReplaceString("mixer", get_file_entry_path(w_mixerdevice_file));
871 }
872
873 // Create "Graphics/Sound" pane
874 static void create_graphics_pane(GtkWidget *top)
875 {
876 GtkWidget *box, *table, *label, *opt, *menu, *combo;
877 char str[32];
878
879 parse_graphics_prefs();
880
881 box = make_pane(top, STR_GRAPHICS_SOUND_PANE_TITLE);
882 table = make_table(box, 2, 4);
883
884 label = gtk_label_new(GetString(STR_VIDEO_TYPE_CTRL));
885 gtk_widget_show(label);
886 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
887
888 opt = gtk_option_menu_new();
889 gtk_widget_show(opt);
890 menu = gtk_menu_new();
891 add_menu_item(menu, STR_WINDOW_CTRL, GTK_SIGNAL_FUNC(mn_window));
892 add_menu_item(menu, STR_FULLSCREEN_CTRL, GTK_SIGNAL_FUNC(mn_fullscreen));
893 switch (display_type) {
894 case DISPLAY_WINDOW:
895 gtk_menu_set_active(GTK_MENU(menu), 0);
896 break;
897 case DISPLAY_SCREEN:
898 gtk_menu_set_active(GTK_MENU(menu), 1);
899 break;
900 }
901 gtk_option_menu_set_menu(GTK_OPTION_MENU(opt), menu);
902 gtk_table_attach(GTK_TABLE(table), opt, 1, 2, 0, 1, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
903
904 l_frameskip = gtk_label_new(GetString(STR_FRAMESKIP_CTRL));
905 gtk_widget_show(l_frameskip);
906 gtk_table_attach(GTK_TABLE(table), l_frameskip, 0, 1, 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
907
908 w_frameskip = gtk_option_menu_new();
909 gtk_widget_show(w_frameskip);
910 menu = gtk_menu_new();
911 add_menu_item(menu, STR_REF_5HZ_LAB, GTK_SIGNAL_FUNC(mn_5hz));
912 add_menu_item(menu, STR_REF_7_5HZ_LAB, GTK_SIGNAL_FUNC(mn_7hz));
913 add_menu_item(menu, STR_REF_10HZ_LAB, GTK_SIGNAL_FUNC(mn_10hz));
914 add_menu_item(menu, STR_REF_15HZ_LAB, GTK_SIGNAL_FUNC(mn_15hz));
915 add_menu_item(menu, STR_REF_30HZ_LAB, GTK_SIGNAL_FUNC(mn_30hz));
916 add_menu_item(menu, STR_REF_60HZ_LAB, GTK_SIGNAL_FUNC(mn_60hz));
917 int frameskip = PrefsFindInt32("frameskip");
918 int item = -1;
919 switch (frameskip) {
920 case 12: item = 0; break;
921 case 8: item = 1; break;
922 case 6: item = 2; break;
923 case 4: item = 3; break;
924 case 2: item = 4; break;
925 case 1: item = 5; break;
926 case 0: item = 5; break;
927 }
928 if (item >= 0)
929 gtk_menu_set_active(GTK_MENU(menu), item);
930 gtk_option_menu_set_menu(GTK_OPTION_MENU(w_frameskip), menu);
931 gtk_table_attach(GTK_TABLE(table), w_frameskip, 1, 2, 1, 2, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
932
933 l_display_x = gtk_label_new(GetString(STR_DISPLAY_X_CTRL));
934 gtk_widget_show(l_display_x);
935 gtk_table_attach(GTK_TABLE(table), l_display_x, 0, 1, 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
936
937 combo = gtk_combo_new();
938 gtk_widget_show(combo);
939 GList *glist1 = NULL;
940 glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_512_LAB));
941 glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_640_LAB));
942 glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_800_LAB));
943 glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_1024_LAB));
944 glist1 = g_list_append(glist1, (void *)GetString(STR_SIZE_MAX_LAB));
945 gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist1);
946 if (dis_width)
947 sprintf(str, "%d", dis_width);
948 else
949 strcpy(str, GetString(STR_SIZE_MAX_LAB));
950 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
951 gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 2, 3, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
952 w_display_x = GTK_COMBO(combo)->entry;
953
954 l_display_y = gtk_label_new(GetString(STR_DISPLAY_Y_CTRL));
955 gtk_widget_show(l_display_y);
956 gtk_table_attach(GTK_TABLE(table), l_display_y, 0, 1, 3, 4, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
957
958 combo = gtk_combo_new();
959 gtk_widget_show(combo);
960 GList *glist2 = NULL;
961 glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_384_LAB));
962 glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_480_LAB));
963 glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_600_LAB));
964 glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_768_LAB));
965 glist2 = g_list_append(glist2, (void *)GetString(STR_SIZE_MAX_LAB));
966 gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist2);
967 if (dis_height)
968 sprintf(str, "%d", dis_height);
969 else
970 strcpy(str, GetString(STR_SIZE_MAX_LAB));
971 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
972 gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 3, 4, (GtkAttachOptions)GTK_FILL, (GtkAttachOptions)0, 4, 4);
973 w_display_y = GTK_COMBO(combo)->entry;
974
975 make_checkbox(box, STR_GFXACCEL_CTRL, PrefsFindBool("gfxaccel"), GTK_SIGNAL_FUNC(tb_gfxaccel));
976
977 make_separator(box);
978 make_checkbox(box, STR_NOSOUND_CTRL, "nosound", GTK_SIGNAL_FUNC(tb_nosound));
979 w_dspdevice_file = make_entry(box, STR_DSPDEVICE_FILE_CTRL, "dsp");
980 w_mixerdevice_file = make_entry(box, STR_MIXERDEVICE_FILE_CTRL, "mixer");
981
982 set_graphics_sensitive();
983
984 hide_show_graphics_widgets();
985 }
986
987
988 /*
989 * "Input" pane
990 */
991
992 static GtkWidget *w_keycode_file;
993 static GtkWidget *w_mouse_wheel_lines;
994
995 // Set sensitivity of widgets
996 static void set_input_sensitive(void)
997 {
998 const bool use_keycodes = PrefsFindBool("keycodes");
999 gtk_widget_set_sensitive(w_keycode_file, use_keycodes);
1000 gtk_widget_set_sensitive(GTK_WIDGET(g_object_get_data(G_OBJECT(w_keycode_file), "chooser_button")), use_keycodes);
1001 gtk_widget_set_sensitive(w_mouse_wheel_lines, PrefsFindInt32("mousewheelmode") == 1);
1002 }
1003
1004 // "Use Raw Keycodes" button toggled
1005 static void tb_keycodes(GtkWidget *widget)
1006 {
1007 PrefsReplaceBool("keycodes", GTK_TOGGLE_BUTTON(widget)->active);
1008 set_input_sensitive();
1009 }
1010
1011 // "Mouse Wheel Mode" selected
1012 static void mn_wheel_page(...) {PrefsReplaceInt32("mousewheelmode", 0); set_input_sensitive();}
1013 static void mn_wheel_cursor(...) {PrefsReplaceInt32("mousewheelmode", 1); set_input_sensitive();}
1014
1015 // Read settings from widgets and set preferences
1016 static void read_input_settings(void)
1017 {
1018 const char *str = get_file_entry_path(w_keycode_file);
1019 if (str && strlen(str))
1020 PrefsReplaceString("keycodefile", str);
1021 else
1022 PrefsRemoveItem("keycodefile");
1023
1024 PrefsReplaceInt32("mousewheellines", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w_mouse_wheel_lines)));
1025 }
1026
1027 // Create "Input" pane
1028 static void create_input_pane(GtkWidget *top)
1029 {
1030 GtkWidget *box, *hbox, *menu, *label, *button;
1031 GtkObject *adj;
1032
1033 box = make_pane(top, STR_INPUT_PANE_TITLE);
1034
1035 make_checkbox(box, STR_KEYCODES_CTRL, "keycodes", GTK_SIGNAL_FUNC(tb_keycodes));
1036
1037 hbox = gtk_hbox_new(FALSE, 4);
1038 gtk_widget_show(hbox);
1039 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1040
1041 label = gtk_label_new(GetString(STR_KEYCODES_CTRL));
1042 gtk_widget_show(label);
1043 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1044
1045 const char *str = PrefsFindString("keycodefile");
1046 if (str == NULL)
1047 str = "";
1048
1049 w_keycode_file = gtk_entry_new();
1050 gtk_entry_set_text(GTK_ENTRY(w_keycode_file), str);
1051 gtk_widget_show(w_keycode_file);
1052 gtk_box_pack_start(GTK_BOX(hbox), w_keycode_file, TRUE, TRUE, 0);
1053
1054 button = make_browse_button(w_keycode_file);
1055 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1056 g_object_set_data(G_OBJECT(w_keycode_file), "chooser_button", button);
1057
1058 make_separator(box);
1059
1060 static const opt_desc options[] = {
1061 {STR_MOUSEWHEELMODE_PAGE_LAB, GTK_SIGNAL_FUNC(mn_wheel_page)},
1062 {STR_MOUSEWHEELMODE_CURSOR_LAB, GTK_SIGNAL_FUNC(mn_wheel_cursor)},
1063 {0, NULL}
1064 };
1065 int wheelmode = PrefsFindInt32("mousewheelmode"), active = 0;
1066 switch (wheelmode) {
1067 case 0: active = 0; break;
1068 case 1: active = 1; break;
1069 }
1070 menu = make_option_menu(box, STR_MOUSEWHEELMODE_CTRL, options, active);
1071
1072 hbox = gtk_hbox_new(FALSE, 4);
1073 gtk_widget_show(hbox);
1074 gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
1075
1076 label = gtk_label_new(GetString(STR_MOUSEWHEELLINES_CTRL));
1077 gtk_widget_show(label);
1078 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1079
1080 adj = gtk_adjustment_new(PrefsFindInt32("mousewheellines"), 1, 1000, 1, 5, 0);
1081 w_mouse_wheel_lines = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0.0, 0);
1082 gtk_widget_show(w_mouse_wheel_lines);
1083 gtk_box_pack_start(GTK_BOX(hbox), w_mouse_wheel_lines, FALSE, FALSE, 0);
1084
1085 set_input_sensitive();
1086 }
1087
1088
1089 /*
1090 * "Serial/Network" pane
1091 */
1092
1093 static GtkWidget *w_seriala, *w_serialb, *w_ether;
1094
1095 // Read settings from widgets and set preferences
1096 static void read_serial_settings(void)
1097 {
1098 const char *str;
1099
1100 str = gtk_entry_get_text(GTK_ENTRY(w_seriala));
1101 PrefsReplaceString("seriala", str);
1102
1103 str = gtk_entry_get_text(GTK_ENTRY(w_serialb));
1104 PrefsReplaceString("serialb", str);
1105
1106 str = gtk_entry_get_text(GTK_ENTRY(w_ether));
1107 if (str && strlen(str))
1108 PrefsReplaceString("ether", str);
1109 else
1110 PrefsRemoveItem("ether");
1111 }
1112
1113 // Add names of serial devices
1114 static gint gl_str_cmp(gconstpointer a, gconstpointer b)
1115 {
1116 return strcmp((char *)a, (char *)b);
1117 }
1118
1119 static GList *add_serial_names(void)
1120 {
1121 GList *glist = NULL;
1122
1123 // Search /dev for ttyS* and lp*
1124 DIR *d = opendir("/dev");
1125 if (d) {
1126 struct dirent *de;
1127 while ((de = readdir(d)) != NULL) {
1128 #if defined(__linux__)
1129 if (strncmp(de->d_name, "ttyS", 4) == 0 || strncmp(de->d_name, "lp", 2) == 0) {
1130 #elif defined(__FreeBSD__)
1131 if (strncmp(de->d_name, "cuaa", 4) == 0 || strncmp(de->d_name, "lpt", 3) == 0) {
1132 #elif defined(__NetBSD__)
1133 if (strncmp(de->d_name, "tty0", 4) == 0 || strncmp(de->d_name, "lpt", 3) == 0) {
1134 #elif defined(sgi)
1135 if (strncmp(de->d_name, "ttyf", 4) == 0 || strncmp(de->d_name, "plp", 3) == 0) {
1136 #else
1137 if (false) {
1138 #endif
1139 char *str = new char[64];
1140 sprintf(str, "/dev/%s", de->d_name);
1141 glist = g_list_append(glist, str);
1142 }
1143 }
1144 closedir(d);
1145 }
1146 if (glist)
1147 g_list_sort(glist, gl_str_cmp);
1148 else
1149 glist = g_list_append(glist, (void *)"<none>");
1150 return glist;
1151 }
1152
1153 // Add names of ethernet interfaces
1154 static GList *add_ether_names(void)
1155 {
1156 GList *glist = NULL;
1157
1158 // Get list of all Ethernet interfaces
1159 int s = socket(PF_INET, SOCK_DGRAM, 0);
1160 if (s >= 0) {
1161 char inbuf[8192];
1162 struct ifconf ifc;
1163 ifc.ifc_len = sizeof(inbuf);
1164 ifc.ifc_buf = inbuf;
1165 if (ioctl(s, SIOCGIFCONF, &ifc) == 0) {
1166 struct ifreq req, *ifr = ifc.ifc_req;
1167 for (int i=0; i<ifc.ifc_len; i+=sizeof(ifreq), ifr++) {
1168 req = *ifr;
1169 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(sgi)
1170 if (ioctl(s, SIOCGIFADDR, &req) == 0 && (req.ifr_addr.sa_family == ARPHRD_ETHER || req.ifr_addr.sa_family == ARPHRD_ETHER+1)) {
1171 #elif defined(__linux__)
1172 if (ioctl(s, SIOCGIFHWADDR, &req) == 0 && req.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
1173 #else
1174 if (false) {
1175 #endif
1176 char *str = new char[64];
1177 strncpy(str, ifr->ifr_name, 63);
1178 glist = g_list_append(glist, str);
1179 }
1180 }
1181 }
1182 close(s);
1183 }
1184 #ifdef HAVE_SLIRP
1185 static char s_slirp[] = "slirp";
1186 glist = g_list_append(glist, s_slirp);
1187 #endif
1188 if (glist)
1189 g_list_sort(glist, gl_str_cmp);
1190 else
1191 glist = g_list_append(glist, (void *)"<none>");
1192 return glist;
1193 }
1194
1195 // Create "Serial/Network" pane
1196 static void create_serial_pane(GtkWidget *top)
1197 {
1198 GtkWidget *box, *table, *label, *combo;
1199 GList *glist = add_serial_names();
1200
1201 box = make_pane(top, STR_SERIAL_NETWORK_PANE_TITLE);
1202 table = make_table(box, 2, 3);
1203
1204 label = gtk_label_new(GetString(STR_SERPORTA_CTRL));
1205 gtk_widget_show(label);
1206 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
1207
1208 combo = gtk_combo_new();
1209 gtk_widget_show(combo);
1210 gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
1211 const char *str = PrefsFindString("seriala");
1212 if (str == NULL)
1213 str = "";
1214 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
1215 gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 0, 1, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
1216 w_seriala = GTK_COMBO(combo)->entry;
1217
1218 label = gtk_label_new(GetString(STR_SERPORTB_CTRL));
1219 gtk_widget_show(label);
1220 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
1221
1222 combo = gtk_combo_new();
1223 gtk_widget_show(combo);
1224 gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
1225 str = PrefsFindString("serialb");
1226 if (str == NULL)
1227 str = "";
1228 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
1229 gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 1, 2, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
1230 w_serialb = GTK_COMBO(combo)->entry;
1231
1232 label = gtk_label_new(GetString(STR_ETHERNET_IF_CTRL));
1233 gtk_widget_show(label);
1234 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, (GtkAttachOptions)0, (GtkAttachOptions)0, 4, 4);
1235
1236 glist = add_ether_names();
1237 combo = gtk_combo_new();
1238 gtk_widget_show(combo);
1239 gtk_combo_set_popdown_strings(GTK_COMBO(combo), glist);
1240 str = PrefsFindString("ether");
1241 if (str == NULL)
1242 str = "";
1243 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), str);
1244 gtk_table_attach(GTK_TABLE(table), combo, 1, 2, 2, 3, (GtkAttachOptions)(GTK_FILL | GTK_EXPAND), (GtkAttachOptions)0, 4, 4);
1245 w_ether = GTK_COMBO(combo)->entry;
1246 }
1247
1248
1249 /*
1250 * "Memory/Misc" pane
1251 */
1252
1253 static GtkWidget *w_ramsize;
1254 static GtkWidget *w_rom_file;
1255
1256 // Don't use CPU when idle?
1257 static void tb_idlewait(GtkWidget *widget)
1258 {
1259 PrefsReplaceBool("idlewait", GTK_TOGGLE_BUTTON(widget)->active);
1260 }
1261
1262 // "Ignore SEGV" button toggled
1263 static void tb_ignoresegv(GtkWidget *widget)
1264 {
1265 PrefsReplaceBool("ignoresegv", GTK_TOGGLE_BUTTON(widget)->active);
1266 }
1267
1268 // Read settings from widgets and set preferences
1269 static void read_memory_settings(void)
1270 {
1271 const char *str = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(w_ramsize)->entry));
1272 PrefsReplaceInt32("ramsize", atoi(str) << 20);
1273
1274 str = gtk_entry_get_text(GTK_ENTRY(w_rom_file));
1275 if (str && strlen(str))
1276 PrefsReplaceString("rom", str);
1277 else
1278 PrefsRemoveItem("rom");
1279 }
1280
1281 // Create "Memory/Misc" pane
1282 static void create_memory_pane(GtkWidget *top)
1283 {
1284 GtkWidget *box, *hbox, *table, *label, *menu;
1285
1286 box = make_pane(top, STR_MEMORY_MISC_PANE_TITLE);
1287 table = make_table(box, 2, 5);
1288
1289 static const combo_desc options[] = {
1290 STR_RAMSIZE_4MB_LAB,
1291 STR_RAMSIZE_8MB_LAB,
1292 STR_RAMSIZE_16MB_LAB,
1293 STR_RAMSIZE_32MB_LAB,
1294 STR_RAMSIZE_64MB_LAB,
1295 STR_RAMSIZE_128MB_LAB,
1296 STR_RAMSIZE_256MB_LAB,
1297 STR_RAMSIZE_512MB_LAB,
1298 STR_RAMSIZE_1024MB_LAB,
1299 0
1300 };
1301 char default_ramsize[16];
1302 sprintf(default_ramsize, "%d", PrefsFindInt32("ramsize") >> 20);
1303 w_ramsize = table_make_combobox(table, 0, STR_RAMSIZE_CTRL, default_ramsize, options);
1304
1305 w_rom_file = table_make_file_entry(table, 1, STR_ROM_FILE_CTRL, "rom");
1306
1307 make_checkbox(box, STR_IGNORESEGV_CTRL, "ignoresegv", GTK_SIGNAL_FUNC(tb_ignoresegv));
1308 make_checkbox(box, STR_IDLEWAIT_CTRL, "idlewait", GTK_SIGNAL_FUNC(tb_idlewait));
1309 }
1310
1311
1312 /*
1313 * Read settings from widgets and set preferences
1314 */
1315
1316 static void read_settings(void)
1317 {
1318 read_volumes_settings();
1319 read_graphics_settings();
1320 read_input_settings();
1321 read_serial_settings();
1322 read_memory_settings();
1323 read_jit_settings();
1324 }
1325
1326
1327 #ifdef STANDALONE_GUI
1328 #include <errno.h>
1329 #include <sys/wait.h>
1330 #include "rpc.h"
1331
1332 /*
1333 * Fake unused data and functions
1334 */
1335
1336 uint8 XPRAM[XPRAM_SIZE];
1337 void MountVolume(void *fh) { }
1338 void FileDiskLayout(loff_t size, uint8 *data, loff_t &start_byte, loff_t &real_size) { }
1339
1340 #if defined __APPLE__ && defined __MACH__
1341 void DarwinSysInit(void) { }
1342 void DarwinSysExit(void) { }
1343 void DarwinAddFloppyPrefs(void) { }
1344 void DarwinAddSerialPrefs(void) { }
1345 bool DarwinCDReadTOC(char *, uint8 *) { }
1346 #endif
1347
1348
1349 /*
1350 * Display alert
1351 */
1352
1353 static void dl_destroyed(void)
1354 {
1355 gtk_main_quit();
1356 }
1357
1358 static void display_alert(int title_id, int prefix_id, int button_id, const char *text)
1359 {
1360 char str[256];
1361 sprintf(str, GetString(prefix_id), text);
1362
1363 GtkWidget *dialog = gtk_dialog_new();
1364 gtk_window_set_title(GTK_WINDOW(dialog), GetString(title_id));
1365 gtk_container_border_width(GTK_CONTAINER(dialog), 5);
1366 gtk_widget_set_uposition(GTK_WIDGET(dialog), 100, 150);
1367 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", GTK_SIGNAL_FUNC(dl_destroyed), NULL);
1368
1369 GtkWidget *label = gtk_label_new(str);
1370 gtk_widget_show(label);
1371 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
1372
1373 GtkWidget *button = gtk_button_new_with_label(GetString(button_id));
1374 gtk_widget_show(button);
1375 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(dl_quit), GTK_OBJECT(dialog));
1376 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0);
1377 GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1378 gtk_widget_grab_default(button);
1379 gtk_widget_show(dialog);
1380
1381 gtk_main();
1382 }
1383
1384
1385 /*
1386 * Display error alert
1387 */
1388
1389 void ErrorAlert(const char *text)
1390 {
1391 display_alert(STR_ERROR_ALERT_TITLE, STR_GUI_ERROR_PREFIX, STR_QUIT_BUTTON, text);
1392 }
1393
1394
1395 /*
1396 * Display warning alert
1397 */
1398
1399 void WarningAlert(const char *text)
1400 {
1401 display_alert(STR_WARNING_ALERT_TITLE, STR_GUI_WARNING_PREFIX, STR_OK_BUTTON, text);
1402 }
1403
1404
1405 /*
1406 * RPC handlers
1407 */
1408
1409 static GMainLoop *g_gui_loop;
1410
1411 static int handle_ErrorAlert(rpc_connection_t *connection)
1412 {
1413 D(bug("handle_ErrorAlert\n"));
1414
1415 int error;
1416 char *str;
1417 if ((error = rpc_method_get_args(connection, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID)) < 0)
1418 return error;
1419
1420 ErrorAlert(str);
1421 free(str);
1422 return RPC_ERROR_NO_ERROR;
1423 }
1424
1425 static int handle_WarningAlert(rpc_connection_t *connection)
1426 {
1427 D(bug("handle_WarningAlert\n"));
1428
1429 int error;
1430 char *str;
1431 if ((error = rpc_method_get_args(connection, RPC_TYPE_STRING, &str, RPC_TYPE_INVALID)) < 0)
1432 return error;
1433
1434 WarningAlert(str);
1435 free(str);
1436 return RPC_ERROR_NO_ERROR;
1437 }
1438
1439 static int handle_Exit(rpc_connection_t *connection)
1440 {
1441 D(bug("handle_Exit\n"));
1442
1443 g_main_quit(g_gui_loop);
1444 return RPC_ERROR_NO_ERROR;
1445 }
1446
1447
1448 /*
1449 * SIGCHLD handler
1450 */
1451
1452 static char g_app_path[PATH_MAX];
1453 static rpc_connection_t *g_gui_connection = NULL;
1454
1455 static void sigchld_handler(int sig, siginfo_t *sip, void *)
1456 {
1457 D(bug("Child %d exitted with status = %x\n", sip->si_pid, sip->si_status));
1458
1459 // XXX perform a new wait because sip->si_status is sometimes not
1460 // the exit _value_ on MacOS X but rather the usual status field
1461 // from waitpid() -- we could arrange this code in some other way...
1462 int status;
1463 if (waitpid(sip->si_pid, &status, 0) < 0)
1464 status = sip->si_status;
1465 if (WIFEXITED(status))
1466 status = WEXITSTATUS(status);
1467 if (status & 0x80)
1468 status |= -1 ^0xff;
1469
1470 if (status < 0) { // negative -> execlp/-errno
1471 char str[256];
1472 sprintf(str, GetString(STR_NO_B2_EXE_FOUND), g_app_path, strerror(-status));
1473 ErrorAlert(str);
1474 status = 1;
1475 }
1476
1477 if (status != 0) {
1478 if (g_gui_connection)
1479 rpc_exit(g_gui_connection);
1480 exit(status);
1481 }
1482 }
1483
1484
1485 /*
1486 * Start standalone GUI
1487 */
1488
1489 int main(int argc, char *argv[])
1490 {
1491 // Init GTK
1492 gtk_set_locale();
1493 gtk_init(&argc, &argv);
1494
1495 // Read preferences
1496 PrefsInit(argc, argv);
1497
1498 // Show preferences editor
1499 bool start = PrefsEditor();
1500
1501 // Exit preferences
1502 PrefsExit();
1503
1504 // Transfer control to the executable
1505 if (start) {
1506 char gui_connection_path[64];
1507 sprintf(gui_connection_path, "/org/SheepShaver/GUI/%d", getpid());
1508
1509 // Catch exits from the child process
1510 struct sigaction sigchld_sa, old_sigchld_sa;
1511 sigemptyset(&sigchld_sa.sa_mask);
1512 sigchld_sa.sa_sigaction = sigchld_handler;
1513 sigchld_sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
1514 if (sigaction(SIGCHLD, &sigchld_sa, &old_sigchld_sa) < 0) {
1515 char str[256];
1516 sprintf(str, GetString(STR_SIG_INSTALL_ERR), SIGCHLD, strerror(errno));
1517 ErrorAlert(str);
1518 return 1;
1519 }
1520
1521 // Search and run the SheepShaver executable
1522 char *p;
1523 strcpy(g_app_path, argv[0]);
1524 if ((p = strstr(g_app_path, "SheepShaverGUI.app/Contents/MacOS")) != NULL) {
1525 strcpy(p, "SheepShaver.app/Contents/MacOS/SheepShaver");
1526 if (access(g_app_path, X_OK) < 0) {
1527 char str[256];
1528 sprintf(str, GetString(STR_NO_B2_EXE_FOUND), g_app_path, strerror(errno));
1529 WarningAlert(str);
1530 strcpy(g_app_path, "/Applications/SheepShaver.app/Contents/MacOS/SheepShaver");
1531 }
1532 } else {
1533 p = strrchr(g_app_path, '/');
1534 p = p ? p + 1 : g_app_path;
1535 strcpy(p, "SheepShaver");
1536 }
1537
1538 int pid = fork();
1539 if (pid == 0) {
1540 D(bug("Trying to execute %s\n", g_app_path));
1541 execlp(g_app_path, g_app_path, "--gui-connection", gui_connection_path, (char *)NULL);
1542 #ifdef _POSIX_PRIORITY_SCHEDULING
1543 // XXX get a chance to run the parent process so that to not confuse/upset GTK...
1544 sched_yield();
1545 #endif
1546 _exit(-errno);
1547 }
1548
1549 // Establish a connection to Basilisk II
1550 if ((g_gui_connection = rpc_init_server(gui_connection_path)) == NULL) {
1551 printf("ERROR: failed to initialize GUI-side RPC server connection\n");
1552 return 1;
1553 }
1554 static const rpc_method_descriptor_t vtable[] = {
1555 { RPC_METHOD_ERROR_ALERT, handle_ErrorAlert },
1556 { RPC_METHOD_WARNING_ALERT, handle_WarningAlert },
1557 { RPC_METHOD_EXIT, handle_Exit }
1558 };
1559 if (rpc_method_add_callbacks(g_gui_connection, vtable, sizeof(vtable) / sizeof(vtable[0])) < 0) {
1560 printf("ERROR: failed to setup GUI method callbacks\n");
1561 return 1;
1562 }
1563 int socket;
1564 if ((socket = rpc_listen_socket(g_gui_connection)) < 0) {
1565 printf("ERROR: failed to initialize RPC server thread\n");
1566 return 1;
1567 }
1568
1569 g_gui_loop = g_main_new(TRUE);
1570 while (g_main_is_running(g_gui_loop)) {
1571
1572 // Process a few events pending
1573 const int N_EVENTS_DISPATCH = 10;
1574 for (int i = 0; i < N_EVENTS_DISPATCH; i++) {
1575 if (!g_main_iteration(FALSE))
1576 break;
1577 }
1578
1579 // Check for RPC events (100 ms timeout)
1580 int ret = rpc_wait_dispatch(g_gui_connection, 100000);
1581 if (ret == 0)
1582 continue;
1583 if (ret < 0)
1584 break;
1585 rpc_dispatch(g_gui_connection);
1586 }
1587
1588 rpc_exit(g_gui_connection);
1589 return 0;
1590 }
1591
1592 return 0;
1593 }
1594 #endif