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