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

# User Rev Content
1 cebix 1.1 /*
2     * prefs_editor_linux.cpp - Preferences editor, Linux implementation using GTK+
3     *
4 gbeauche 1.21 * SheepShaver (C) 1997-2008 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_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 gbeauche 1.18
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 cebix 1.1 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.21 "Copyright (C) 1997-2008 Christian Bauer and Marc Hellwig\n"
376 gbeauche 1.16 "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_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 gbeauche 1.18 gtk_widget_realize(notebook);
455 cebix 1.1
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 gbeauche 1.18 gtk_widget_show(notebook);
463 cebix 1.1
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 gbeauche 1.12 gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
495 cebix 1.1 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 gbeauche 1.12 gchar *file = (gchar *)gtk_file_selection_get_filename(GTK_FILE_SELECTION(assoc->req));
504 cebix 1.1
505 gbeauche 1.12 const gchar *str = gtk_entry_get_text(GTK_ENTRY(assoc->entry));
506 cebix 1.1 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 gbeauche 1.3 * "JIT Compiler" pane
635     */
636    
637 gbeauche 1.18 // 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 gbeauche 1.3 // 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 gbeauche 1.18 bool jit_enabled = is_jit_capable() && PrefsFindBool("jit");
675 gbeauche 1.3 }
676    
677 gbeauche 1.10 // "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 gbeauche 1.3 // 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 gbeauche 1.18
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 gbeauche 1.10 make_checkbox(box, STR_JIT_68K_CTRL, "jit68k", GTK_SIGNAL_FUNC(tb_jit_68k));
697 gbeauche 1.3 }
698    
699    
700     /*
701 cebix 1.1 * "Graphics/Sound" pane
702     */
703    
704 gbeauche 1.13 // 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 gbeauche 1.14 static bool is_fbdev_dga_mode = false;
715 cebix 1.1
716 gbeauche 1.4 static GtkWidget *w_dspdevice_file, *w_mixerdevice_file;
717    
718 gbeauche 1.13 // 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 cebix 1.1 // "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 gbeauche 1.9 // QuickDraw acceleration
754     static void tb_gfxaccel(GtkWidget *widget)
755     {
756     PrefsReplaceBool("gfxaccel", GTK_TOGGLE_BUTTON(widget)->active);
757     }
758    
759 gbeauche 1.4 // 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 cebix 1.1 // "Disable Sound Output" button toggled
768     static void tb_nosound(GtkWidget *widget)
769     {
770     PrefsReplaceBool("nosound", GTK_TOGGLE_BUTTON(widget)->active);
771 gbeauche 1.4 set_graphics_sensitive();
772 cebix 1.1 }
773    
774 gbeauche 1.13 // 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 gbeauche 1.14 #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 gbeauche 1.13 }
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 cebix 1.1 // Read settings from widgets and set preferences
838     static void read_graphics_settings(void)
839     {
840 gbeauche 1.13 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 gbeauche 1.14 bool use_screen_mode = true;
850 gbeauche 1.13 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 gbeauche 1.14 use_screen_mode = false;
859 gbeauche 1.13 PrefsRemoveItem("screen");
860     return;
861     }
862 gbeauche 1.14 if (use_screen_mode) {
863     PrefsReplaceString("screen", pref);
864     // Old prefs are now migrated
865     PrefsRemoveItem("windowmodes");
866     PrefsRemoveItem("screenmodes");
867     }
868 gbeauche 1.13
869 gbeauche 1.4 PrefsReplaceString("dsp", get_file_entry_path(w_dspdevice_file));
870     PrefsReplaceString("mixer", get_file_entry_path(w_mixerdevice_file));
871 cebix 1.1 }
872    
873     // Create "Graphics/Sound" pane
874     static void create_graphics_pane(GtkWidget *top)
875     {
876 gbeauche 1.13 GtkWidget *box, *table, *label, *opt, *menu, *combo;
877     char str[32];
878    
879     parse_graphics_prefs();
880 cebix 1.1
881     box = make_pane(top, STR_GRAPHICS_SOUND_PANE_TITLE);
882 gbeauche 1.13 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 cebix 1.1
888 gbeauche 1.13 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 cebix 1.1 switch (frameskip) {
920 gbeauche 1.13 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 cebix 1.1 }
928 gbeauche 1.13 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 cebix 1.1
937 gbeauche 1.13 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 gbeauche 1.9
954 gbeauche 1.13 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 cebix 1.1
958 gbeauche 1.13 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 cebix 1.1
975 gbeauche 1.13 make_checkbox(box, STR_GFXACCEL_CTRL, PrefsFindBool("gfxaccel"), GTK_SIGNAL_FUNC(tb_gfxaccel));
976 cebix 1.1
977 gbeauche 1.4 make_separator(box);
978 cebix 1.1 make_checkbox(box, STR_NOSOUND_CTRL, "nosound", GTK_SIGNAL_FUNC(tb_nosound));
979 gbeauche 1.4 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 gbeauche 1.13
984     hide_show_graphics_widgets();
985 gbeauche 1.2 }
986    
987    
988     /*
989     * "Input" pane
990     */
991    
992     static GtkWidget *w_keycode_file;
993 gbeauche 1.5 static GtkWidget *w_mouse_wheel_lines;
994 gbeauche 1.2
995     // Set sensitivity of widgets
996     static void set_input_sensitive(void)
997     {
998 gbeauche 1.15 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 gbeauche 1.5 gtk_widget_set_sensitive(w_mouse_wheel_lines, PrefsFindInt32("mousewheelmode") == 1);
1002 gbeauche 1.2 }
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 gbeauche 1.5 // "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 gbeauche 1.2 // 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 gbeauche 1.5
1024     PrefsReplaceInt32("mousewheellines", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w_mouse_wheel_lines)));
1025 gbeauche 1.2 }
1026    
1027     // Create "Input" pane
1028     static void create_input_pane(GtkWidget *top)
1029     {
1030 gbeauche 1.15 GtkWidget *box, *hbox, *menu, *label, *button;
1031 gbeauche 1.2 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 gbeauche 1.15
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 gbeauche 1.5
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 gbeauche 1.2
1085     set_input_sensitive();
1086 cebix 1.1 }
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 gbeauche 1.7 #if defined(__linux__)
1129 cebix 1.1 if (strncmp(de->d_name, "ttyS", 4) == 0 || strncmp(de->d_name, "lp", 2) == 0) {
1130 gbeauche 1.7 #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 cebix 1.1 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 gbeauche 1.7 #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 cebix 1.1 if (ioctl(s, SIOCGIFHWADDR, &req) == 0 && req.ifr_hwaddr.sa_family == ARPHRD_ETHER) {
1173 gbeauche 1.7 #else
1174     if (false) {
1175     #endif
1176 cebix 1.1 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 gbeauche 1.15 #ifdef HAVE_SLIRP
1185     static char s_slirp[] = "slirp";
1186     glist = g_list_append(glist, s_slirp);
1187     #endif
1188 cebix 1.1 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 gbeauche 1.15 static GtkWidget *w_ramsize;
1254 cebix 1.1 static GtkWidget *w_rom_file;
1255    
1256 gbeauche 1.9 // 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 cebix 1.1 // "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 gbeauche 1.15 const char *str = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(w_ramsize)->entry));
1272     PrefsReplaceInt32("ramsize", atoi(str) << 20);
1273 cebix 1.1
1274 gbeauche 1.15 str = gtk_entry_get_text(GTK_ENTRY(w_rom_file));
1275 cebix 1.1 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 gbeauche 1.15 GtkWidget *box, *hbox, *table, *label, *menu;
1285 cebix 1.1
1286     box = make_pane(top, STR_MEMORY_MISC_PANE_TITLE);
1287 gbeauche 1.15 table = make_table(box, 2, 5);
1288 cebix 1.1
1289 gbeauche 1.15 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 gbeauche 1.20 STR_RAMSIZE_1024MB_LAB,
1299 gbeauche 1.15 0
1300     };
1301 gbeauche 1.20 char default_ramsize[16];
1302 gbeauche 1.15 sprintf(default_ramsize, "%d", PrefsFindInt32("ramsize") >> 20);
1303     w_ramsize = table_make_combobox(table, 0, STR_RAMSIZE_CTRL, default_ramsize, options);
1304 cebix 1.1
1305 gbeauche 1.15 w_rom_file = table_make_file_entry(table, 1, STR_ROM_FILE_CTRL, "rom");
1306 cebix 1.1
1307     make_checkbox(box, STR_IGNORESEGV_CTRL, "ignoresegv", GTK_SIGNAL_FUNC(tb_ignoresegv));
1308 gbeauche 1.9 make_checkbox(box, STR_IDLEWAIT_CTRL, "idlewait", GTK_SIGNAL_FUNC(tb_idlewait));
1309 cebix 1.1 }
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 gbeauche 1.17 read_input_settings();
1321 cebix 1.1 read_serial_settings();
1322     read_memory_settings();
1323 gbeauche 1.3 read_jit_settings();
1324 cebix 1.1 }
1325 gbeauche 1.17
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 gbeauche 1.19 void DarwinSysInit(void) { }
1342     void DarwinSysExit(void) { }
1343 gbeauche 1.17 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