1 /* NetHack 3.6	gnbind.c	$NHDT-Date: 1450453305 2015/12/18 15:41:45 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.33 $ */
2 /* Copyright (C) 1998 by Erik Andersen <andersee@debian.org> */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  * This file implements the interface between the window port specific
7  * code in the Gnome port and the rest of the nethack game engine.
8 */
9 
10 #include "gnbind.h"
11 #include "gnmain.h"
12 #include "gnmenu.h"
13 #include "gnaskstr.h"
14 #include "gnyesno.h"
15 
16 GNHWinData gnome_windowlist[MAXWINDOWS];
17 winid WIN_WORN = WIN_ERR;
18 
19 extern void tty_raw_print(const char *);
20 extern void tty_raw_print_bold(const char *);
21 
22 /* Interface definition, for windows.c */
23 struct window_procs Gnome_procs = {
24     "Gnome", WC_COLOR | WC_HILITE_PET | WC_INVERSE, 0L,
25     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},   /* color availability */
26     gnome_init_nhwindows,
27     gnome_player_selection, gnome_askname, gnome_get_nh_event,
28     gnome_exit_nhwindows, gnome_suspend_nhwindows, gnome_resume_nhwindows,
29     gnome_create_nhwindow, gnome_clear_nhwindow, gnome_display_nhwindow,
30     gnome_destroy_nhwindow, gnome_curs, gnome_putstr, genl_putmixed,
31     gnome_display_file, gnome_start_menu, gnome_add_menu, gnome_end_menu,
32     gnome_select_menu,
33     genl_message_menu, /* no need for X-specific handling */
34     gnome_update_inventory, gnome_mark_synch, gnome_wait_synch,
35 #ifdef CLIPPING
36     gnome_cliparound,
37 #endif
38 #ifdef POSITIONBAR
39     donull,
40 #endif
41     gnome_print_glyph, gnome_raw_print, gnome_raw_print_bold, gnome_nhgetch,
42     gnome_nh_poskey, gnome_nhbell, gnome_doprev_message, gnome_yn_function,
43     gnome_getlin, gnome_get_ext_cmd, gnome_number_pad, gnome_delay_output,
44 #ifdef CHANGE_COLOR /* only a Mac option currently */
45     donull, donull,
46 #endif
47     /* other defs that really should go away (they're tty specific) */
48     gnome_start_screen, gnome_end_screen, gnome_outrip,
49     genl_preference_update, genl_getmsghistory, genl_putmsghistory,
50     genl_status_init, genl_status_finish, genl_status_enablefield,
51     genl_status_update,
52     genl_can_suspend_yes,
53 };
54 
55 /*
56 init_nhwindows(int* argcp, char** argv)
57                 -- Initialize the windows used by NetHack.  This can also
58                    create the standard windows listed at the top, but does
59                    not display them.
60                 -- Any commandline arguments relevant to the windowport
61                    should be interpreted, and *argcp and *argv should
62                    be changed to remove those arguments.
63                 -- When the message window is created, the variable
64                    iflags.window_inited needs to be set to TRUE.  Otherwise
65                    all plines() will be done via raw_print().
66                 ** Why not have init_nhwindows() create all of the "standard"
67                 ** windows?  Or at least all but WIN_INFO?      -dean
68 */
69 void
gnome_init_nhwindows(int * argc,char ** argv)70 gnome_init_nhwindows(int *argc, char **argv)
71 {
72     /* Main window */
73     ghack_init_main_window(*argc, argv);
74     ghack_init_signals();
75 
76 #ifdef HACKDIR
77     // if (ghack_init_glyphs(HACKDIR "/t32-1024.xpm"))
78     if (ghack_init_glyphs(HACKDIR "/x11tiles"))
79         g_error("ERROR:  Could not initialize glyphs.\n");
80 #else
81 #error HACKDIR is not defined!
82 #endif
83 
84     // gnome/gtk is not reentrant
85     set_option_mod_status("ignintr", DISP_IN_GAME);
86     flags.ignintr = TRUE;
87 
88     iflags.window_inited = TRUE;
89 
90     /* gnome-specific window creation */
91     WIN_WORN = gnome_create_nhwindow(NHW_WORN);
92 }
93 
94 /* Do a window-port specific player type selection. If player_selection()
95    offers a Quit option, it is its responsibility to clean up and terminate
96    the process. You need to fill in pl_character[0].
97 */
98 void
gnome_player_selection()99 gnome_player_selection()
100 {
101     int n, i, sel;
102     const char **choices;
103     int *pickmap;
104 
105     /* prevent an unnecessary prompt */
106     rigid_role_checks();
107 
108     if (!flags.randomall && flags.initrole < 0) {
109         /* select a role */
110         for (n = 0; roles[n].name.m; n++)
111             continue;
112         choices = (const char **) alloc(sizeof(char *) * (n + 1));
113         pickmap = (int *) alloc(sizeof(int) * (n + 1));
114         for (;;) {
115             for (n = 0, i = 0; roles[i].name.m; i++) {
116                 if (ok_role(i, flags.initrace, flags.initgend,
117                             flags.initalign)) {
118                     if (flags.initgend >= 0 && flags.female
119                         && roles[i].name.f)
120                         choices[n] = roles[i].name.f;
121                     else
122                         choices[n] = roles[i].name.m;
123                     pickmap[n++] = i;
124                 }
125             }
126             if (n > 0)
127                 break;
128             else if (flags.initalign >= 0)
129                 flags.initalign = -1; /* reset */
130             else if (flags.initgend >= 0)
131                 flags.initgend = -1;
132             else if (flags.initrace >= 0)
133                 flags.initrace = -1;
134             else
135                 panic("no available ROLE+race+gender+alignment combinations");
136         }
137         choices[n] = (const char *) 0;
138         if (n > 1)
139             sel = ghack_player_sel_dialog(
140                 choices, _("Player selection"),
141                 _("Choose one of the following roles:"));
142         else
143             sel = 0;
144         if (sel >= 0)
145             sel = pickmap[sel];
146         else if (sel == ROLE_NONE) { /* Quit */
147             clearlocks();
148             gtk_exit(0);
149         }
150         free(choices);
151         free(pickmap);
152     } else if (flags.initrole < 0)
153         sel = ROLE_RANDOM;
154     else
155         sel = flags.initrole;
156 
157     if (sel == ROLE_RANDOM) { /* Random role */
158         sel = pick_role(flags.initrace, flags.initgend, flags.initalign,
159                         PICK_RANDOM);
160         if (sel < 0)
161             sel = randrole(FALSE);
162     }
163 
164     flags.initrole = sel;
165 
166     /* Select a race, if necessary */
167     /* force compatibility with role, try for compatibility with
168      * pre-selected gender/alignment */
169     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
170         if (flags.initrace == ROLE_RANDOM || flags.randomall) {
171             flags.initrace = pick_race(flags.initrole, flags.initgend,
172                                        flags.initalign, PICK_RANDOM);
173             if (flags.initrace < 0)
174                 flags.initrace = randrace(flags.initrole);
175         } else {
176             /* Count the number of valid races */
177             n = 0; /* number valid */
178             for (i = 0; races[i].noun; i++) {
179                 if (ok_race(flags.initrole, i, flags.initgend,
180                             flags.initalign))
181                     n++;
182             }
183             if (n == 0) {
184                 for (i = 0; races[i].noun; i++) {
185                     if (validrace(flags.initrole, i))
186                         n++;
187                 }
188             }
189 
190             choices = (const char **) alloc(sizeof(char *) * (n + 1));
191             pickmap = (int *) alloc(sizeof(int) * (n + 1));
192             for (n = 0, i = 0; races[i].noun; i++) {
193                 if (ok_race(flags.initrole, i, flags.initgend,
194                             flags.initalign)) {
195                     choices[n] = races[i].noun;
196                     pickmap[n++] = i;
197                 }
198             }
199             choices[n] = (const char *) 0;
200             /* Permit the user to pick, if there is more than one */
201             if (n > 1)
202                 sel = ghack_player_sel_dialog(
203                     choices, _("Race selection"),
204                     _("Choose one of the following races:"));
205             else
206                 sel = 0;
207             if (sel >= 0)
208                 sel = pickmap[sel];
209             else if (sel == ROLE_NONE) { /* Quit */
210                 clearlocks();
211                 gtk_exit(0);
212             }
213             flags.initrace = sel;
214             free(choices);
215             free(pickmap);
216         }
217         if (flags.initrace == ROLE_RANDOM) { /* Random role */
218             sel = pick_race(flags.initrole, flags.initgend, flags.initalign,
219                             PICK_RANDOM);
220             if (sel < 0)
221                 sel = randrace(flags.initrole);
222             flags.initrace = sel;
223         }
224     }
225 
226     /* Select a gender, if necessary */
227     /* force compatibility with role/race, try for compatibility with
228      * pre-selected alignment */
229     if (flags.initgend < 0
230         || !validgend(flags.initrole, flags.initrace, flags.initgend)) {
231         if (flags.initgend == ROLE_RANDOM || flags.randomall) {
232             flags.initgend = pick_gend(flags.initrole, flags.initrace,
233                                        flags.initalign, PICK_RANDOM);
234             if (flags.initgend < 0)
235                 flags.initgend = randgend(flags.initrole, flags.initrace);
236         } else {
237             /* Count the number of valid genders */
238             n = 0; /* number valid */
239             for (i = 0; i < ROLE_GENDERS; i++) {
240                 if (ok_gend(flags.initrole, flags.initrace, i,
241                             flags.initalign))
242                     n++;
243             }
244             if (n == 0) {
245                 for (i = 0; i < ROLE_GENDERS; i++) {
246                     if (validgend(flags.initrole, flags.initrace, i))
247                         n++;
248                 }
249             }
250 
251             choices = (const char **) alloc(sizeof(char *) * (n + 1));
252             pickmap = (int *) alloc(sizeof(int) * (n + 1));
253             for (n = 0, i = 0; i < ROLE_GENDERS; i++) {
254                 if (ok_gend(flags.initrole, flags.initrace, i,
255                             flags.initalign)) {
256                     choices[n] = genders[i].adj;
257                     pickmap[n++] = i;
258                 }
259             }
260             choices[n] = (const char *) 0;
261             /* Permit the user to pick, if there is more than one */
262             if (n > 1)
263                 sel = ghack_player_sel_dialog(
264                     choices, _("Gender selection"),
265                     _("Choose one of the following genders:"));
266             else
267                 sel = 0;
268             if (sel >= 0)
269                 sel = pickmap[sel];
270             else if (sel == ROLE_NONE) { /* Quit */
271                 clearlocks();
272                 gtk_exit(0);
273             }
274             flags.initgend = sel;
275             free(choices);
276             free(pickmap);
277         }
278         if (flags.initgend == ROLE_RANDOM) { /* Random gender */
279             sel = pick_gend(flags.initrole, flags.initrace, flags.initalign,
280                             PICK_RANDOM);
281             if (sel < 0)
282                 sel = randgend(flags.initrole, flags.initrace);
283             flags.initgend = sel;
284         }
285     }
286 
287     /* Select an alignment, if necessary */
288     /* force compatibility with role/race/gender */
289     if (flags.initalign < 0
290         || !validalign(flags.initrole, flags.initrace, flags.initalign)) {
291         if (flags.initalign == ROLE_RANDOM || flags.randomall) {
292             flags.initalign = pick_align(flags.initrole, flags.initrace,
293                                          flags.initgend, PICK_RANDOM);
294             if (flags.initalign < 0)
295                 flags.initalign = randalign(flags.initrole, flags.initrace);
296         } else {
297             /* Count the number of valid alignments */
298             n = 0; /* number valid */
299             for (i = 0; i < ROLE_ALIGNS; i++) {
300                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
301                              i))
302                     n++;
303             }
304             if (n == 0) {
305                 for (i = 0; i < ROLE_ALIGNS; i++)
306                     if (validalign(flags.initrole, flags.initrace, i))
307                         n++;
308             }
309 
310             choices = (const char **) alloc(sizeof(char *) * (n + 1));
311             pickmap = (int *) alloc(sizeof(int) * (n + 1));
312             for (n = 0, i = 0; i < ROLE_ALIGNS; i++) {
313                 if (ok_align(flags.initrole, flags.initrace, flags.initgend,
314                              i)) {
315                     choices[n] = aligns[i].adj;
316                     pickmap[n++] = i;
317                 }
318             }
319             choices[n] = (const char *) 0;
320             /* Permit the user to pick, if there is more than one */
321             if (n > 1)
322                 sel = ghack_player_sel_dialog(
323                     choices, _("Alignment selection"),
324                     _("Choose one of the following alignments:"));
325             else
326                 sel = 0;
327             if (sel >= 0)
328                 sel = pickmap[sel];
329             else if (sel == ROLE_NONE) { /* Quit */
330                 clearlocks();
331                 gtk_exit(0);
332             }
333             flags.initalign = sel;
334             free(choices);
335             free(pickmap);
336         }
337         if (flags.initalign == ROLE_RANDOM) {
338             sel = pick_align(flags.initrole, flags.initrace, flags.initgend,
339                              PICK_RANDOM);
340             if (sel < 0)
341                 sel = randalign(flags.initrole, flags.initrace);
342             flags.initalign = sel;
343         }
344     }
345 }
346 
347 /* Ask the user for a player name. */
348 void
gnome_askname()349 gnome_askname()
350 {
351     int ret;
352 
353     g_message("Asking name....");
354 
355     /* Ask for a name and stuff the response into plname, a nethack global */
356     ret = ghack_ask_string_dialog("What is your name?", "gandalf",
357                                   "GnomeHack", plname);
358 
359     /* Quit if they want to quit... */
360     if (ret == -1) {
361         clearlocks();
362         gtk_exit(0);
363     }
364 }
365 
366 /* Does window event processing (e.g. exposure events).
367    A noop for the tty and X window-ports.
368 */
369 void
gnome_get_nh_event()370 gnome_get_nh_event()
371 {
372     /* We handle our own events. */
373     return;
374 }
375 
376 /* Exits the window system.  This should dismiss all windows,
377    except the "window" used for raw_print().  str is printed if possible.
378 */
379 void
gnome_exit_nhwindows(const char * str)380 gnome_exit_nhwindows(const char *str)
381 {
382     /* gtk cannot do this without exiting the program, do nothing */
383 }
384 
385 /* Prepare the window to be suspended. */
386 void
gnome_suspend_nhwindows(const char * str)387 gnome_suspend_nhwindows(const char *str)
388 {
389     /* I don't think we need to do anything here... */
390     return;
391 }
392 
393 /* Restore the windows after being suspended. */
394 void
gnome_resume_nhwindows()395 gnome_resume_nhwindows()
396 {
397     /* Do Nothing.  Un-necessary since the GUI will refresh itself. */
398     return;
399 }
400 
401 /*  Create a window of type "type" which can be
402         NHW_MESSAGE     (top line)
403         NHW_STATUS      (bottom lines)
404         NHW_MAP         (main dungeon)
405         NHW_MENU        (inventory or other "corner" windows)
406         NHW_TEXT        (help/text, full screen paged window)
407 */
408 winid
gnome_create_nhwindow(int type)409 gnome_create_nhwindow(int type)
410 {
411     winid i = 0;
412 
413     /* Return the next available winid */
414 
415     for (i = 0; i < MAXWINDOWS; i++)
416         if (gnome_windowlist[i].win == NULL)
417             break;
418     if (i == MAXWINDOWS)
419         g_error("ERROR:  No windows available...\n");
420     gnome_create_nhwindow_by_id(type, i);
421     return i;
422 }
423 
424 void
gnome_create_nhwindow_by_id(int type,winid i)425 gnome_create_nhwindow_by_id(int type, winid i)
426 {
427     switch (type) {
428     case NHW_MAP: {
429         gnome_windowlist[i].win = ghack_init_map_window();
430         gnome_windowlist[i].type = NHW_MAP;
431         ghack_main_window_add_map_window(gnome_windowlist[i].win);
432         break;
433     }
434     case NHW_MESSAGE: {
435         gnome_windowlist[i].win = ghack_init_message_window();
436         gnome_windowlist[i].type = NHW_MESSAGE;
437         ghack_main_window_add_message_window(gnome_windowlist[i].win);
438         break;
439     }
440     case NHW_STATUS: {
441         gnome_windowlist[i].win = ghack_init_status_window();
442         gnome_windowlist[i].type = NHW_STATUS;
443         ghack_main_window_add_status_window(gnome_windowlist[i].win);
444         break;
445     }
446     case NHW_WORN: {
447         gnome_windowlist[i].win = ghack_init_worn_window();
448         gnome_windowlist[i].type = NHW_WORN;
449         ghack_main_window_add_worn_window(gnome_windowlist[i].win);
450         break;
451     }
452     case NHW_MENU: {
453         gnome_windowlist[i].type = NHW_MENU;
454         gnome_windowlist[i].win = ghack_init_menu_window();
455         break;
456     }
457     case NHW_TEXT: {
458         gnome_windowlist[i].win = ghack_init_text_window();
459         gnome_windowlist[i].type = NHW_TEXT;
460         break;
461     }
462     }
463 }
464 
465 /* This widget is being destroyed before its time--
466  * clear its entry from the windowlist.
467 */
468 void
gnome_delete_nhwindow_by_reference(GtkWidget * menuWin)469 gnome_delete_nhwindow_by_reference(GtkWidget *menuWin)
470 {
471     int i;
472 
473     for (i = 0; i < MAXWINDOWS; i++) {
474         if (gnome_windowlist[i].win == menuWin) {
475             gnome_windowlist[i].win = NULL;
476             gnome_windowlist[i].type = 0;
477             break;
478         }
479     }
480 }
481 
482 /* Clear the given window, when asked to. */
483 void
gnome_clear_nhwindow(winid wid)484 gnome_clear_nhwindow(winid wid)
485 {
486     if (gnome_windowlist[wid].win != NULL) {
487         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
488                         ghack_signals[GHSIG_CLEAR]);
489     }
490 }
491 
492 /* -- Display the window on the screen.  If there is data
493                    pending for output in that window, it should be sent.
494                    If blocking is TRUE, display_nhwindow() will not
495                    return until the data has been displayed on the screen,
496                    and acknowledged by the user where appropriate.
497                 -- All calls are blocking in the tty window-port.
498                 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
499                    --more--, if necessary, in the tty window-port.
500 */
501 void
gnome_display_nhwindow(winid wid,BOOLEAN_P block)502 gnome_display_nhwindow(winid wid, BOOLEAN_P block)
503 {
504     if (gnome_windowlist[wid].win != NULL) {
505         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
506                         ghack_signals[GHSIG_DISPLAY], block);
507         if (block && (gnome_windowlist[wid].type == NHW_MAP))
508             (void) gnome_nhgetch();
509     }
510 }
511 
512 /* Destroy will dismiss the window if the window has not
513  * already been dismissed.
514 */
515 void
gnome_destroy_nhwindow(winid wid)516 gnome_destroy_nhwindow(winid wid)
517 {
518     if ((wid == WIN_MAP) || (wid == WIN_MESSAGE) || (wid == WIN_STATUS)) {
519         /* no thanks, I'll do these myself */
520         return;
521     }
522     if (wid != -1 && gnome_windowlist[wid].win != NULL) {
523         gtk_widget_destroy(gnome_windowlist[wid].win);
524         gnome_windowlist[wid].win = NULL;
525         gnome_windowlist[wid].type = 0;
526     }
527 }
528 
529 /* Next output to window will start at (x,y), also moves
530  displayable cursor to (x,y).  For backward compatibility,
531  1 <= x < cols, 0 <= y < rows, where cols and rows are
532  the size of window.
533 */
534 void
gnome_curs(winid wid,int x,int y)535 gnome_curs(winid wid, int x, int y)
536 {
537     if (wid != -1 && gnome_windowlist[wid].win != NULL) {
538         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
539                         ghack_signals[GHSIG_CURS], x, y);
540     }
541 }
542 
543 /*
544 putstr(window, attr, str)
545                 -- Print str on the window with the given attribute.  Only
546                    printable ASCII characters (040-0126) must be supported.
547                    Multiple putstr()s are output on separate lines.
548 Attributes
549                    can be one of
550                         ATR_NONE (or 0)
551                         ATR_ULINE
552                         ATR_BOLD
553                         ATR_BLINK
554                         ATR_INVERSE
555                    If a window-port does not support all of these, it may map
556                    unsupported attributes to a supported one (e.g. map them
557                    all to ATR_INVERSE).  putstr() may compress spaces out of
558                    str, break str, or truncate str, if necessary for the
559                    display.  Where putstr() breaks a line, it has to clear
560                    to end-of-line.
561                 -- putstr should be implemented such that if two putstr()s
562                    are done consecutively the user will see the first and
563                    then the second.  In the tty port, pline() achieves this
564                    by calling more() or displaying both on the same line.
565 */
566 void
gnome_putstr(winid wid,int attr,const char * text)567 gnome_putstr(winid wid, int attr, const char *text)
568 {
569     if ((wid >= 0) && (wid < MAXWINDOWS)
570         && (gnome_windowlist[wid].win != NULL)) {
571         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
572                         ghack_signals[GHSIG_PUTSTR], (guint) attr, text);
573     }
574 }
575 
576 /* Display the file named str.  Complain about missing files
577                    iff complain is TRUE.
578 */
579 void
gnome_display_file(const char * filename,BOOLEAN_P must_exist)580 gnome_display_file(const char *filename, BOOLEAN_P must_exist)
581 {
582     /* Strange -- for some reason it makes us create a new text window
583      * instead of reusing any existing ones -- perhaps we can work out
584      * some way to reuse stuff -- but for now just make and destroy new
585      * ones each time */
586 
587     dlb *f;
588 
589     f = dlb_fopen(filename, "r");
590     if (!f) {
591         if (must_exist) {
592             GtkWidget *box;
593             char message[90];
594             sprintf(message, "Warning! Could not find file: %s\n", filename);
595 
596             box = gnome_message_box_new(_(message), GNOME_MESSAGE_BOX_ERROR,
597                                         GNOME_STOCK_BUTTON_OK, NULL);
598             gnome_dialog_set_default(GNOME_DIALOG(box), 0);
599             gnome_dialog_set_parent(GNOME_DIALOG(box),
600                                     GTK_WINDOW(ghack_get_main_window()));
601             gtk_window_set_modal(GTK_WINDOW(box), TRUE);
602             gtk_widget_show(box);
603         }
604     } else {
605         GtkWidget *txtwin, *gless, *frametxt;
606 #define LLEN 128
607         char line[LLEN], *textlines;
608         int num_lines, charcount;
609 
610         txtwin = gnome_dialog_new("Text Window", GNOME_STOCK_BUTTON_OK, NULL);
611         gtk_widget_set_usize(GTK_WIDGET(txtwin), 500, 400);
612         gtk_window_set_policy(GTK_WINDOW(txtwin), TRUE, TRUE, FALSE);
613         gtk_window_set_title(GTK_WINDOW(txtwin), "Text Window");
614         gnome_dialog_set_default(GNOME_DIALOG(txtwin), 0);
615         gtk_window_set_modal(GTK_WINDOW(txtwin), TRUE);
616         frametxt = gtk_frame_new("");
617         gtk_widget_show(frametxt);
618 
619         /*
620          * Count the number of lines and characters in the file.
621          */
622         num_lines = 0;
623         charcount = 1;
624         while (dlb_fgets(line, LLEN, f)) {
625             num_lines++;
626             charcount += strlen(line);
627         }
628         (void) dlb_fclose(f);
629 
630         /* Ignore empty files */
631         if (num_lines == 0)
632             return;
633 
634         /*
635          * Re-open the file and read the data into a buffer.
636          */
637         textlines = (char *) alloc((unsigned int) charcount);
638         textlines[0] = '\0';
639         f = dlb_fopen(filename, RDTMODE);
640 
641         while (dlb_fgets(line, LLEN, f)) {
642             (void) strcat(textlines, line);
643         }
644         (void) dlb_fclose(f);
645 
646         gless = gnome_less_new();
647         gnome_less_show_string(GNOME_LESS(gless), textlines);
648         gtk_container_add(GTK_CONTAINER(frametxt), gless);
649         gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(txtwin)->vbox), frametxt,
650                            TRUE, TRUE, 0);
651         gtk_widget_show_all(txtwin);
652         gtk_window_set_modal(GTK_WINDOW(txtwin), TRUE);
653         gnome_dialog_set_parent(GNOME_DIALOG(txtwin),
654                                 GTK_WINDOW(ghack_get_main_window()));
655         gnome_dialog_run_and_close(GNOME_DIALOG(txtwin));
656         free(textlines);
657     }
658 }
659 
660 /* Start using window as a menu.  You must call start_menu()
661    before add_menu().  After calling start_menu() you may not
662    putstr() to the window.  Only windows of type NHW_MENU may
663    be used for menus.
664 */
665 void
gnome_start_menu(winid wid)666 gnome_start_menu(winid wid)
667 {
668     if (wid != -1) {
669         if (gnome_windowlist[wid].win == NULL
670             && gnome_windowlist[wid].type != 0) {
671             gnome_create_nhwindow_by_id(gnome_windowlist[wid].type, wid);
672         }
673         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
674                         ghack_signals[GHSIG_START_MENU]);
675     }
676 }
677 
678 /*
679 add_menu(windid window, int glyph, const anything identifier,
680                                 char accelerator, char groupacc,
681                                 int attr, char *str, boolean preselected)
682                 -- Add a text line str to the given menu window.  If
683 identifier
684                    is 0, then the line cannot be selected (e.g. a title).
685                    Otherwise, identifier is the value returned if the line is
686                    selected.  Accelerator is a keyboard key that can be used
687                    to select the line.  If the accelerator of a selectable
688                    item is 0, the window system is free to select its own
689                    accelerator.  It is up to the window-port to make the
690                    accelerator visible to the user (e.g. put "a - " in front
691                    of str).  The value attr is the same as in putstr().
692                    Glyph is an optional glyph to accompany the line.  If
693                    window port cannot or does not want to display it, this
694                    is OK.  If there is no glyph applicable, then this
695                    value will be NO_GLYPH.
696                 -- All accelerators should be in the range [A-Za-z].
697                 -- It is expected that callers do not mix accelerator
698                    choices.  Either all selectable items have an accelerator
699                    or let the window system pick them.  Don't do both.
700                 -- Groupacc is a group accelerator.  It may be any character
701                    outside of the standard accelerator (see above) or a
702                    number.  If 0, the item is unaffected by any group
703                    accelerator.  If this accelerator conflicts with
704                    the menu command (or their user defined aliases), it loses.
705                    The menu commands and aliases take care not to interfere
706                    with the default object class symbols.
707                 -- If you want this choice to be preselected when the
708                    menu is displayed, set preselected to TRUE.
709 */
710 void
gnome_add_menu(winid wid,int glyph,const ANY_P * identifier,CHAR_P accelerator,CHAR_P group_accel,int attr,const char * str,BOOLEAN_P presel)711 gnome_add_menu(winid wid, int glyph, const ANY_P *identifier,
712                CHAR_P accelerator, CHAR_P group_accel, int attr,
713                const char *str, BOOLEAN_P presel)
714 {
715     GHackMenuItem item;
716     item.glyph = glyph;
717     item.identifier = identifier;
718     item.accelerator = accelerator;
719     item.group_accel = group_accel;
720     item.attr = attr;
721     item.str = str;
722     item.presel = presel;
723 
724     if (wid != -1 && gnome_windowlist[wid].win != NULL) {
725         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
726                         ghack_signals[GHSIG_ADD_MENU], &item);
727     }
728 }
729 
730 /*
731 end_menu(window, prompt)
732                 -- Stop adding entries to the menu and flushes the window
733                    to the screen (brings to front?).  Prompt is a prompt
734                    to give the user.  If prompt is NULL, no prompt will
735                    be printed.
736                 ** This probably shouldn't flush the window any more (if
737                 ** it ever did).  That should be select_menu's job.  -dean
738 */
739 void
gnome_end_menu(winid wid,const char * prompt)740 gnome_end_menu(winid wid, const char *prompt)
741 {
742     if (wid != -1 && gnome_windowlist[wid].win != NULL) {
743         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
744                         ghack_signals[GHSIG_END_MENU], prompt);
745     }
746 }
747 
748 /*
749 int select_menu(windid window, int how, menu_item **selected)
750                 -- Return the number of items selected; 0 if none were chosen,
751                    -1 when explicitly cancelled.  If items were selected, then
752                    selected is filled in with an allocated array of menu_item
753                    structures, one for each selected line.  The caller must
754                    free this array when done with it.  The "count" field
755                    of selected is a user supplied count.  If the user did
756                    not supply a count, then the count field is filled with
757                    -1 (meaning all).  A count of zero is equivalent to not
758                    being selected and should not be in the list.  If no items
759                    were selected, then selected is NULL'ed out.  How is the
760                    mode of the menu.  Three valid values are PICK_NONE,
761                    PICK_ONE, and PICK_N, meaning: nothing is selectable,
762                    only one thing is selectable, and any number valid items
763                    may selected.  If how is PICK_NONE, this function should
764                    never return anything but 0 or -1.
765                 -- You may call select_menu() on a window multiple times --
766                    the menu is saved until start_menu() or destroy_nhwindow()
767                    is called on the window.
768                 -- Note that NHW_MENU windows need not have select_menu()
769                    called for them. There is no way of knowing whether
770                    select_menu() will be called for the window at
771                    create_nhwindow() time.
772 */
773 int
gnome_select_menu(winid wid,int how,MENU_ITEM_P ** selected)774 gnome_select_menu(winid wid, int how, MENU_ITEM_P **selected)
775 {
776     int nReturned = -1;
777 
778     if (wid != -1 && gnome_windowlist[wid].win != NULL
779         && gnome_windowlist[wid].type == NHW_MENU) {
780         nReturned = ghack_menu_window_select_menu(gnome_windowlist[wid].win,
781                                                   selected, how);
782     }
783 
784     return nReturned;
785 }
786 
787 /*
788     -- Indicate to the window port that the inventory has been changed.
789     -- Merely calls display_inventory() for window-ports that leave the
790         window up, otherwise empty.
791 */
792 void
gnome_update_inventory()793 gnome_update_inventory()
794 {
795     ghack_main_window_update_inventory();
796 }
797 
798 /*
799 mark_synch()    -- Don't go beyond this point in I/O on any channel until
800                    all channels are caught up to here.  Can be an empty call
801                    for the moment
802 */
803 void
gnome_mark_synch()804 gnome_mark_synch()
805 {
806     /* Do nothing */
807 }
808 
809 /*
810 wait_synch()    -- Wait until all pending output is complete (*flush*() for
811                    streams goes here).
812                 -- May also deal with exposure events etc. so that the
813                    display is OK when return from wait_synch().
814 */
815 void
gnome_wait_synch()816 gnome_wait_synch()
817 {
818     /* Do nothing */
819 }
820 
821 /*
822 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
823                    screen if the playing area is larger than the screen.
824                 -- This function is only defined if CLIPPING is defined.
825 */
826 void
gnome_cliparound(int x,int y)827 gnome_cliparound(int x, int y)
828 {
829     /* FIXME!!!  winid should be a parameter!!!
830      * Call a function that Does The Right Thing(tm).
831     */
832     gnome_cliparound_proper(WIN_MAP, x, y);
833 }
834 
835 void
gnome_cliparound_proper(winid wid,int x,int y)836 gnome_cliparound_proper(winid wid, int x, int y)
837 {
838     if (wid != -1 && gnome_windowlist[wid].win != NULL) {
839         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
840                         ghack_signals[GHSIG_CLIPAROUND], (guint) x,
841                         (guint) y);
842     }
843 }
844 
845 /*
846 print_glyph(window, x, y, glyph, bkglyph)
847                 -- Print the glyph at (x,y) on the given window.  Glyphs are
848                    integers at the interface, mapped to whatever the window-
849                    port wants (symbol, font, color, attributes, ...there's
850                    a 1-1 map between glyphs and distinct things on the map).
851 */
852 void
gnome_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph,int bkglyph)853 gnome_print_glyph(winid wid, XCHAR_P x, XCHAR_P y, int glyph, int bkglyph)
854 {
855     if (wid != -1 && gnome_windowlist[wid].win != NULL) {
856         GdkImlibImage *im;
857 
858         im = ghack_image_from_glyph(glyph, FALSE);
859 
860         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[wid].win),
861                         ghack_signals[GHSIG_PRINT_GLYPH], (guint) x,
862                         (guint) y, im, NULL);
863     }
864 }
865 
866 /*
867 raw_print(str)  -- Print directly to a screen, or otherwise guarantee that
868                    the user sees str.  raw_print() appends a newline to str.
869                    It need not recognize ASCII control characters.  This is
870                    used during startup (before windowing system initialization
871                    -- maybe this means only error startup messages are raw),
872                    for error messages, and maybe other "msg" uses.  E.g.
873                    updating status for micros (i.e, "saving").
874 */
875 void
gnome_raw_print(const char * str)876 gnome_raw_print(const char *str)
877 {
878     tty_raw_print(str);
879 }
880 
881 /*
882 raw_print_bold(str)
883                 -- Like raw_print(), but prints in bold/standout (if
884 possible).
885 */
886 void
gnome_raw_print_bold(const char * str)887 gnome_raw_print_bold(const char *str)
888 {
889     tty_raw_print_bold(str);
890 }
891 
892 /*
893 int nhgetch()   -- Returns a single character input from the user.
894                 -- In the tty window-port, nhgetch() assumes that tgetch()
895                    will be the routine the OS provides to read a character.
896                    Returned character _must_ be non-zero.
897 */
898 int
gnome_nhgetch()899 gnome_nhgetch()
900 {
901     int key;
902     GList *theFirst;
903     gtk_signal_emit(GTK_OBJECT(gnome_windowlist[WIN_STATUS].win),
904                     ghack_signals[GHSIG_FADE_HIGHLIGHT]);
905 
906     g_askingQuestion = 1;
907     /* Process events until a key press event arrives. */
908     while (g_numKeys == 0) {
909         if (program_state.done_hup)
910             return '\033';
911         gtk_main_iteration();
912     }
913 
914     theFirst = g_list_first(g_keyBuffer);
915     g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst);
916     key = GPOINTER_TO_INT(theFirst->data);
917     g_list_free_1(theFirst);
918     g_numKeys--;
919     g_askingQuestion = 0;
920     return (key);
921 }
922 
923 /*
924 int nh_poskey(int *x, int *y, int *mod)
925                 -- Returns a single character input from the user or a
926                    a positioning event (perhaps from a mouse).  If the
927                    return value is non-zero, a character was typed, else,
928                    a position in the MAP window is returned in x, y and mod.
929                    mod may be one of
930 
931                         CLICK_1         -- mouse click type 1
932                         CLICK_2         -- mouse click type 2
933 
934                    The different click types can map to whatever the
935                    hardware supports.  If no mouse is supported, this
936                    routine always returns a non-zero character.
937 */
938 int
gnome_nh_poskey(int * x,int * y,int * mod)939 gnome_nh_poskey(int *x, int *y, int *mod)
940 {
941     gtk_signal_emit(GTK_OBJECT(gnome_windowlist[WIN_STATUS].win),
942                     ghack_signals[GHSIG_FADE_HIGHLIGHT]);
943 
944     g_askingQuestion = 0;
945     /* Process events until a key or map-click arrives. */
946     while (g_numKeys == 0 && g_numClicks == 0) {
947         if (program_state.done_hup)
948             return '\033';
949         gtk_main_iteration();
950     }
951 
952     if (g_numKeys > 0) {
953         int key;
954         GList *theFirst;
955 
956         theFirst = g_list_first(g_keyBuffer);
957         g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst);
958         key = GPOINTER_TO_INT(theFirst->data);
959         g_list_free_1(theFirst);
960         g_numKeys--;
961         return (key);
962     } else {
963         GHClick *click;
964         GList *theFirst;
965 
966         theFirst = g_list_first(g_clickBuffer);
967         g_clickBuffer = g_list_remove_link(g_clickBuffer, theFirst);
968         click = (GHClick *) theFirst->data;
969         *x = click->x;
970         *y = click->y;
971         *mod = click->mod;
972         g_free(click);
973         g_list_free_1(theFirst);
974         g_numClicks--;
975         return (0);
976     }
977 }
978 
979 /*
980 nhbell()        -- Beep at user.  [This will exist at least until sounds are
981                    redone, since sounds aren't attributable to windows
982 anyway.]
983 */
984 void
gnome_nhbell()985 gnome_nhbell()
986 {
987     /* FIXME!!! Play a cool GNOME sound instead */
988     gdk_beep();
989 }
990 
991 /*
992 doprev_message()
993                 -- Display previous messages.  Used by the ^P command.
994                 -- On the tty-port this scrolls WIN_MESSAGE back one line.
995 */
996 int
gnome_doprev_message()997 gnome_doprev_message()
998 {
999     /* Do Nothing.  They can read old messages using the scrollbar. */
1000     return 0;
1001 }
1002 
1003 /*
1004 char yn_function(const char *ques, const char *choices, char default)
1005                 -- Print a prompt made up of ques, choices and default.
1006                    Read a single character response that is contained in
1007                    choices or default.  If choices is NULL, all possible
1008                    inputs are accepted and returned.  This overrides
1009                    everything else.  The choices are expected to be in
1010                    lower case.  Entering ESC always maps to 'q', or 'n',
1011                    in that order, if present in choices, otherwise it maps
1012                    to default.  Entering any other quit character (SPACE,
1013                    RETURN, NEWLINE) maps to default.
1014                 -- If the choices string contains ESC, then anything after
1015                    it is an acceptable response, but the ESC and whatever
1016                    follows is not included in the prompt.
1017                 -- If the choices string contains a '#' then accept a count.
1018                    Place this value in the global "yn_number" and return '#'.
1019                 -- This uses the top line in the tty window-port, other
1020                    ports might use a popup.
1021 */
1022 char
gnome_yn_function(const char * question,const char * choices,CHAR_P def)1023 gnome_yn_function(const char *question, const char *choices, CHAR_P def)
1024 {
1025     int ch;
1026     int result = -1;
1027     char message[BUFSZ];
1028     char yn_esc_map = '\033';
1029     GtkWidget *mainWnd = ghack_get_main_window();
1030 
1031     if (choices) {
1032         char *cb, choicebuf[QBUFSZ];
1033         Strcpy(choicebuf, choices);
1034         if ((cb = index(choicebuf, '\033')) != 0) {
1035             /* anything beyond <esc> is hidden */
1036             *cb = '\0';
1037         }
1038         (void) strncpy(message, question, QBUFSZ - 1);
1039         message[QBUFSZ - 1] = '\0';
1040         sprintf(eos(message), " [%s]", choicebuf);
1041         if (def)
1042             sprintf(eos(message), " (%c)", def);
1043         Strcat(message, " ");
1044         /* escape maps to 'q' or 'n' or default, in that order */
1045         yn_esc_map =
1046             (index(choices, 'q') ? 'q' : (index(choices, 'n') ? 'n' : def));
1047     } else {
1048         Strcpy(message, question);
1049     }
1050 
1051     gnome_putstr(WIN_MESSAGE, ATR_BOLD, message);
1052     if (mainWnd != NULL && choices && !index(choices, ch)) {
1053         return (ghack_yes_no_dialog(question, choices, def));
1054     }
1055 
1056     /* Only here if main window is not present */
1057     while (result < 0) {
1058         ch = gnome_nhgetch();
1059         if (ch == '\033') {
1060             result = yn_esc_map;
1061         } else if (choices && !index(choices, ch)) {
1062             /* FYI: ch==-115 is for KP_ENTER */
1063             if (def
1064                 && (ch == ' ' || ch == '\r' || ch == '\n' || ch == -115)) {
1065                 result = def;
1066             } else {
1067                 gnome_nhbell();
1068                 /* and try again... */
1069             }
1070         } else {
1071             result = ch;
1072         }
1073     }
1074     return result;
1075 }
1076 
1077 /*
1078 getlin(const char *ques, char *input)
1079             -- Prints ques as a prompt and reads a single line of text,
1080                up to a newline.  The string entered is returned without the
1081                newline.  ESC is used to cancel, in which case the string
1082                "\033\000" is returned.
1083             -- getlin() must call flush_screen(1) before doing anything.
1084             -- This uses the top line in the tty window-port, other
1085                ports might use a popup.
1086 */
1087 void
gnome_getlin(const char * question,char * input)1088 gnome_getlin(const char *question, char *input)
1089 {
1090     int ret;
1091 
1092     ret = ghack_ask_string_dialog(question, "", "nethack", input);
1093 
1094     if (ret == -1)
1095         input[0] = 0;
1096 }
1097 
1098 /*
1099 int get_ext_cmd(void)
1100             -- Get an extended command in a window-port specific way.
1101                An index into extcmdlist[] is returned on a successful
1102                selection, -1 otherwise.
1103 */
1104 int
gnome_get_ext_cmd()1105 gnome_get_ext_cmd()
1106 {
1107     return ghack_menu_ext_cmd();
1108 }
1109 
1110 /*
1111 number_pad(state)
1112             -- Initialize the number pad to the given state.
1113 */
1114 void
gnome_number_pad(int state)1115 gnome_number_pad(int state)
1116 {
1117     /* Do Nothing */
1118 }
1119 
1120 /*
1121 delay_output()  -- Causes a visible delay of 50ms in the output.
1122                Conceptually, this is similar to wait_synch() followed
1123                by a nap(50ms), but allows asynchronous operation.
1124 */
1125 void
gnome_delay_output()1126 gnome_delay_output()
1127 {
1128     if (gnome_windowlist[WIN_MESSAGE].win != NULL) {
1129         gtk_signal_emit(GTK_OBJECT(gnome_windowlist[WIN_MESSAGE].win),
1130                         ghack_signals[GHSIG_DELAY], (guint) 50);
1131     }
1132 }
1133 
1134 /*
1135 start_screen()  -- Only used on Unix tty ports, but must be declared for
1136                completeness.  Sets up the tty to work in full-screen
1137                graphics mode.  Look at win/tty/termcap.c for an
1138                example.  If your window-port does not need this function
1139                just declare an empty function.
1140 */
1141 void
gnome_start_screen()1142 gnome_start_screen()
1143 {
1144     /* Do Nothing */
1145 }
1146 
1147 /*
1148 end_screen()    -- Only used on Unix tty ports, but must be declared for
1149                completeness.  The complement of start_screen().
1150 */
1151 void
gnome_end_screen()1152 gnome_end_screen()
1153 {
1154     /* Do Nothing */
1155 }
1156 
1157 /*
1158 outrip(winid, int, when)
1159             -- The tombstone code.  If you want the traditional code use
1160                genl_outrip for the value and check the #if in rip.c.
1161 */
1162 void
gnome_outrip(winid wid,int how,time_t when)1163 gnome_outrip(winid wid, int how, time_t when)
1164 {
1165     /* Follows roughly the same algorithm as genl_outrip() */
1166     char buf[BUFSZ];
1167     char ripString[BUFSZ] = "\0";
1168     long year;
1169 
1170     /* Put name on stone */
1171     Sprintf(buf, "%s\n", plname);
1172     Strcat(ripString, buf);
1173 
1174     /* Put $ on stone */
1175     Sprintf(buf, "%ld Au\n", done_money);
1176     Strcat(ripString, buf);
1177 
1178     /* Put together death description */
1179     formatkiller(buf, sizeof buf, how, FALSE);
1180 
1181     /* Put death type on stone */
1182     Strcat(ripString, buf);
1183     Strcat(ripString, "\n");
1184 
1185     /* Put year on stone */
1186     year = yyyymmdd(when) / 10000L;
1187     Sprintf(buf, "%4ld\n", year);
1188     Strcat(ripString, buf);
1189 
1190     ghack_text_window_rip_string(ripString);
1191 }
1192