more quieting of Qt6 build warnings
[NetHack.git] / win / X11 / wintext.c
blobe401b7018d9be66f8c04ff53cb70e359ec7e5cb6
1 /* NetHack 3.7 wintext.c $NHDT-Date: 1597967808 2020/08/20 23:56:48 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.22 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
5 /*
6 * File for dealing with text windows.
8 * + No global functions.
9 */
11 #ifndef SYSV
12 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
13 #endif
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #include <X11/Shell.h>
18 #include <X11/Xos.h>
19 #include <X11/Xaw/Form.h>
20 #include <X11/Xaw/AsciiText.h>
21 #include <X11/Xaw/Cardinals.h>
22 #include <X11/Xatom.h>
24 #ifdef PRESERVE_NO_SYSV
25 #ifdef SYSV
26 #undef SYSV
27 #endif
28 #undef PRESERVE_NO_SYSV
29 #endif
31 #define X11_BUILD
32 #include "hack.h"
33 #undef X11_BUILD
35 #include "winX.h"
36 #include "xwindow.h"
38 #ifdef GRAPHIC_TOMBSTONE
39 #include <X11/xpm.h>
40 #endif
42 #define TRANSIENT_TEXT /* text window is a transient window (no positioning) \
45 static const char text_translations[] = "#override\n\
46 <BtnDown>: dismiss_text()\n\
47 <Key>: key_dismiss_text()";
49 #ifdef GRAPHIC_TOMBSTONE
50 static const char rip_translations[] = "#override\n\
51 <BtnDown>: rip_dismiss_text()\n\
52 <Key>: rip_dismiss_text()";
54 static Widget create_ripout_widget(Widget);
55 #endif
57 /*ARGSUSED*/
58 void
59 delete_text(Widget w, XEvent *event, String *params, Cardinal *num_params)
61 struct xwindow *wp;
62 struct text_info_t *text_info;
64 nhUse(event);
65 nhUse(params);
66 nhUse(num_params);
68 wp = find_widget(w);
69 text_info = wp->text_information;
71 nh_XtPopdown(wp->popup);
73 if (text_info->blocked) {
74 exit_x_event = TRUE;
75 } else if (text_info->destroy_on_ack) {
76 destroy_text_window(wp);
81 * Callback used for all text windows. The window is popped down on any key
82 * or button down event. It is destroyed if the main nethack code is done
83 * with it.
85 /*ARGSUSED*/
86 void
87 dismiss_text(Widget w, XEvent *event, String *params, Cardinal *num_params)
89 struct xwindow *wp;
90 struct text_info_t *text_info;
92 nhUse(event);
93 nhUse(params);
94 nhUse(num_params);
96 wp = find_widget(w);
97 text_info = wp->text_information;
99 nh_XtPopdown(wp->popup);
101 if (text_info->blocked) {
102 exit_x_event = TRUE;
103 } else if (text_info->destroy_on_ack) {
104 destroy_text_window(wp);
108 /* Dismiss when a non-modifier key pressed. */
109 void
110 key_dismiss_text(Widget w, XEvent *event, String *params, Cardinal *num_params)
112 char ch = key_event_to_char((XKeyEvent *) event);
113 if (ch)
114 dismiss_text(w, event, params, num_params);
117 #ifdef GRAPHIC_TOMBSTONE
118 /* Dismiss from clicking on rip image. */
119 void
120 rip_dismiss_text(Widget w, XEvent *event, String *params, Cardinal *num_params)
122 dismiss_text(XtParent(w), event, params, num_params);
124 #endif
126 /* ARGSUSED */
127 void
128 add_to_text_window(struct xwindow *wp, int attr, /* currently unused */
129 const char *str)
131 struct text_info_t *text_info = wp->text_information;
132 int width;
134 nhUse(attr);
136 append_text_buffer(&text_info->text, str, FALSE);
138 /* Calculate text width and save longest line */
139 width = XTextWidth(text_info->fs, str, (int) strlen(str));
140 if (width > text_info->max_width)
141 text_info->max_width = width;
144 void
145 display_text_window(struct xwindow *wp, boolean blocking)
147 struct text_info_t *text_info;
148 Arg args[8];
149 Cardinal num_args;
150 Dimension width, height, font_height;
151 int nlines;
153 text_info = wp->text_information;
154 width = text_info->max_width + text_info->extra_width;
155 text_info->blocked = blocking;
156 text_info->destroy_on_ack = FALSE;
157 font_height = nhFontHeight(wp->w);
160 * Calculate the number of lines to use. First, find the number of
161 * lines that would fit on the screen. Next, remove four of these
162 * lines to give room for a possible window manager titlebar (some
163 * wm's put a titlebar on transient windows). Make sure we have
164 * _some_ lines. Finally, use the number of lines in the text if
165 * there are fewer than the max.
167 nlines = (XtScreen(wp->w)->height - text_info->extra_height) / font_height;
168 nlines -= 4;
170 if (nlines > text_info->text.num_lines)
171 nlines = text_info->text.num_lines;
172 if (nlines <= 0)
173 nlines = 1;
175 height = nlines * font_height + text_info->extra_height;
177 num_args = 0;
179 if (nlines < text_info->text.num_lines) {
180 /* add on width of scrollbar. Really should look this up,
181 * but can't until the window is realized. Chicken-and-egg problem.
183 width += 20;
186 #ifdef GRAPHIC_TOMBSTONE
187 if (text_info->is_rip) {
188 Widget rip = create_ripout_widget(XtParent(wp->w));
190 if (rip) {
191 XtSetArg(args[num_args], nhStr(XtNfromVert), rip);
192 num_args++;
193 } else
194 text_info->is_rip = FALSE;
196 #endif
198 if (width > (Dimension) XtScreen(wp->w)->width) { /* too wide for screen */
199 /* Back off some amount - we really need to back off the scrollbar */
200 /* width plus some extra. */
201 width = XtScreen(wp->w)->width - 20;
203 XtSetArg(args[num_args], XtNstring, text_info->text.text);
204 num_args++;
205 XtSetArg(args[num_args], XtNwidth, width);
206 num_args++;
207 XtSetArg(args[num_args], XtNheight, height);
208 num_args++;
209 XtSetValues(wp->w, args, num_args);
211 #ifdef TRANSIENT_TEXT
212 XtRealizeWidget(wp->popup);
213 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
214 &wm_delete_window, 1);
215 positionpopup(wp->popup, FALSE);
216 #endif
218 nh_XtPopup(wp->popup, (int) XtGrabNone, wp->w);
220 /* Kludge alert. Scrollbars are not sized correctly by the Text widget */
221 /* if added before the window is displayed, so do it afterward. */
222 num_args = 0;
223 if (nlines < text_info->text.num_lines) { /* add vert scrollbar */
224 XtSetArg(args[num_args], nhStr(XtNscrollVertical),
225 XawtextScrollAlways);
226 num_args++;
228 if (width >= (Dimension)(XtScreen(wp->w)->width - 20)) { /* too wide */
229 XtSetArg(args[num_args], nhStr(XtNscrollHorizontal),
230 XawtextScrollAlways);
231 num_args++;
233 if (num_args)
234 XtSetValues(wp->w, args, num_args);
236 /* We want the user to acknowledge. */
237 if (blocking) {
238 (void) x_event(EXIT_ON_EXIT);
239 nh_XtPopdown(wp->popup);
243 void
244 create_text_window(struct xwindow *wp)
246 struct text_info_t *text_info;
247 Arg args[8];
248 Cardinal num_args;
249 Position top_margin, bottom_margin, left_margin, right_margin;
250 Widget form;
252 wp->type = NHW_TEXT;
254 wp->text_information = text_info =
255 (struct text_info_t *) alloc(sizeof(struct text_info_t));
257 init_text_buffer(&text_info->text);
258 text_info->max_width = 0;
259 text_info->extra_width = 0;
260 text_info->extra_height = 0;
261 text_info->blocked = FALSE;
262 text_info->destroy_on_ack = TRUE; /* Ok to destroy before display */
263 #ifdef GRAPHIC_TOMBSTONE
264 text_info->is_rip = FALSE;
265 #endif
267 num_args = 0;
268 XtSetArg(args[num_args], XtNallowShellResize, True), num_args++;
269 XtSetArg(args[num_args], XtNtranslations,
270 XtParseTranslationTable(text_translations)), num_args++;
272 #ifdef TRANSIENT_TEXT
273 wp->popup = XtCreatePopupShell("text", transientShellWidgetClass,
274 toplevel, args, num_args);
275 #else
276 wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass, toplevel,
277 args, num_args);
278 #endif
279 XtOverrideTranslations(
280 wp->popup,
281 XtParseTranslationTable("<Message>WM_PROTOCOLS: delete_text()"));
283 num_args = 0;
284 XtSetArg(args[num_args], XtNallowShellResize, True), num_args++;
285 XtSetArg(args[num_args], XtNtranslations,
286 XtParseTranslationTable(text_translations)), num_args++;
287 form = XtCreateManagedWidget("form", formWidgetClass, wp->popup, args,
288 num_args);
290 num_args = 0;
291 XtSetArg(args[num_args], nhStr(XtNdisplayCaret), False);
292 num_args++;
293 XtSetArg(args[num_args], XtNresize, XawtextResizeBoth);
294 num_args++;
295 XtSetArg(args[num_args], XtNtranslations,
296 XtParseTranslationTable(text_translations));
297 num_args++;
299 wp->w = XtCreateManagedWidget(svk.killer.name[0] && WIN_MAP == WIN_ERR
300 ? "tombstone"
301 : "text_text", /* name */
302 asciiTextWidgetClass,
303 form, /* parent widget */
304 args, /* set some values */
305 num_args); /* number of values to set */
307 /* Get the font and margin information. */
308 num_args = 0;
309 XtSetArg(args[num_args], XtNfont, &text_info->fs);
310 num_args++;
311 XtSetArg(args[num_args], nhStr(XtNtopMargin), &top_margin);
312 num_args++;
313 XtSetArg(args[num_args], nhStr(XtNbottomMargin), &bottom_margin);
314 num_args++;
315 XtSetArg(args[num_args], nhStr(XtNleftMargin), &left_margin);
316 num_args++;
317 XtSetArg(args[num_args], nhStr(XtNrightMargin), &right_margin);
318 num_args++;
319 XtGetValues(wp->w, args, num_args);
321 text_info->extra_width = left_margin + right_margin;
322 text_info->extra_height = top_margin + bottom_margin;
325 void
326 destroy_text_window(struct xwindow *wp)
328 /* Don't need to pop down, this only called from dismiss_text(). */
330 struct text_info_t *text_info = wp->text_information;
333 * If the text window was blocked, then the user has already ACK'ed
334 * it and we are free to really destroy the window. Otherwise, don't
335 * destroy until the user dismisses the window via a key or button
336 * press.
338 if (text_info->blocked || text_info->destroy_on_ack) {
339 XtDestroyWidget(wp->popup);
340 free_text_buffer(&text_info->text);
341 free((genericptr_t) text_info), wp->text_information = 0;
342 wp->type = NHW_NONE; /* allow reuse */
343 } else {
344 text_info->destroy_on_ack = TRUE; /* destroy on next ACK */
348 void
349 clear_text_window(struct xwindow *wp)
351 clear_text_buffer(&wp->text_information->text);
354 /* text buffer routines ----------------------------------------------------
357 /* Append a line to the text buffer. */
358 void
359 append_text_buffer(struct text_buffer *tb, const char *str, boolean concat)
361 char *copy;
362 int length;
364 if (!tb->text)
365 panic("append_text_buffer: null text buffer");
367 if (str) {
368 length = strlen(str);
369 } else {
370 length = 0;
373 if (length + tb->text_last + 1 >= tb->text_size) {
374 /* we need to go to a bigger buffer! */
375 #ifdef VERBOSE
376 printf(
377 "append_text_buffer: text buffer growing from %d to %d bytes\n",
378 tb->text_size, 2 * tb->text_size);
379 #endif
380 copy = (char *) alloc((unsigned) tb->text_size * 2);
381 (void) memcpy(copy, tb->text, tb->text_last);
382 free(tb->text);
383 tb->text = copy;
384 tb->text_size *= 2;
387 if (tb->num_lines) { /* not first --- append a newline */
388 char appchar = '\n';
390 if (concat && !strchr("!.?'\")", tb->text[tb->text_last - 1])) {
391 appchar = ' ';
392 tb->num_lines--; /* offset increment at end of function */
395 *(tb->text + tb->text_last) = appchar;
396 tb->text_last++;
399 if (str) {
400 (void) memcpy((tb->text + tb->text_last), str, length + 1);
401 if (length) {
402 /* Remove all newlines. Otherwise we have a confused line count. */
403 copy = (tb->text + tb->text_last);
404 while ((copy = strchr(copy, '\n')) != (char *) 0)
405 *copy = ' ';
408 tb->text_last += length;
410 tb->text[tb->text_last] = '\0';
411 tb->num_lines++;
414 /* Initialize text buffer. */
415 void
416 init_text_buffer(struct text_buffer *tb)
418 tb->text = (char *) alloc(START_SIZE);
419 tb->text[0] = '\0';
420 tb->text_size = START_SIZE;
421 tb->text_last = 0;
422 tb->num_lines = 0;
425 /* Empty the text buffer */
426 void
427 clear_text_buffer(struct text_buffer *tb)
429 tb->text_last = 0;
430 tb->text[0] = '\0';
431 tb->num_lines = 0;
434 /* Free up allocated memory. */
435 void
436 free_text_buffer(struct text_buffer *tb)
438 free(tb->text);
439 tb->text = (char *) 0;
440 tb->text_size = 0;
441 tb->text_last = 0;
442 tb->num_lines = 0;
445 #ifdef GRAPHIC_TOMBSTONE
447 static void rip_exposed(Widget, XtPointer, XtPointer);
449 static XImage *rip_image = 0;
451 #define STONE_LINE_LEN 16 /* # chars that fit on one line */
452 #define NAME_LINE 0 /* line # for player name */
453 #define GOLD_LINE 1 /* line # for amount of gold */
454 #define DEATH_LINE 2 /* line # for death description */
455 #define YEAR_LINE 6 /* line # for year */
457 static char rip_line[YEAR_LINE + 1][STONE_LINE_LEN + 1];
459 void
460 calculate_rip_text(int how, time_t when)
462 /* Follows same algorithm as genl_outrip() */
464 char buf[BUFSZ];
465 char *dpx;
466 int line, year;
467 long cash;
469 /* Put name on stone */
470 Sprintf(rip_line[NAME_LINE], "%.16s", svp.plname); /* STONE_LINE_LEN */
472 /* Put $ on stone */
473 cash = max(gd.done_money, 0L);
474 /* arbitrary upper limit; practical upper limit is quite a bit less */
475 if (cash > 999999999L)
476 cash = 999999999L;
477 Sprintf(buf, "%ld Au", cash);
478 Sprintf(rip_line[GOLD_LINE], "%ld Au", cash);
480 /* Put together death description */
481 formatkiller(buf, sizeof buf, how, FALSE);
483 /* Put death type on stone */
484 for (line = DEATH_LINE, dpx = buf; line < YEAR_LINE; line++) {
485 int i, i0;
486 char tmpchar;
488 if ((i0 = strlen(dpx)) > STONE_LINE_LEN) {
489 for (i = STONE_LINE_LEN; ((i0 > STONE_LINE_LEN) && i); i--)
490 if (dpx[i] == ' ')
491 i0 = i;
492 if (!i)
493 i0 = STONE_LINE_LEN;
495 tmpchar = dpx[i0];
496 dpx[i0] = 0;
497 strcpy(rip_line[line], dpx);
498 if (tmpchar != ' ') {
499 dpx[i0] = tmpchar;
500 dpx = &dpx[i0];
501 } else
502 dpx = &dpx[i0 + 1];
505 /* Put year on stone */
506 year = (int) ((yyyymmdd(when) / 10000L) % 10000L);
507 Sprintf(rip_line[YEAR_LINE], "%4d", year);
511 * RIP image expose callback.
513 /*ARGSUSED*/
514 static void
515 rip_exposed(Widget w, XtPointer client_data UNUSED,
516 XtPointer widget_data) /* expose event from Window widget */
518 XExposeEvent *event = (XExposeEvent *) widget_data;
519 Display *dpy = XtDisplay(w);
520 Arg args[8];
521 XGCValues values;
522 XtGCMask mask;
523 GC ggc;
524 static Pixmap rip_pixmap = None;
525 int i, x, y;
527 if (!XtIsRealized(w) || event->count > 0)
528 return;
530 if (rip_pixmap == None && rip_image) {
531 rip_pixmap = XCreatePixmap(dpy, XtWindow(w), rip_image->width,
532 rip_image->height,
533 DefaultDepth(dpy, DefaultScreen(dpy)));
534 XPutImage(dpy, rip_pixmap, DefaultGC(dpy, DefaultScreen(dpy)),
535 rip_image, 0, 0, 0, 0, /* src, dest top left */
536 rip_image->width, rip_image->height);
537 XDestroyImage(rip_image); /* data bytes free'd also */
540 mask = GCFunction | GCForeground | GCGraphicsExposures | GCFont;
541 values.graphics_exposures = False;
542 XtSetArg(args[0], XtNforeground, &values.foreground);
543 XtGetValues(w, args, 1);
544 values.function = GXcopy;
545 values.font = WindowFont(w);
546 ggc = XtGetGC(w, mask, &values);
548 if (rip_pixmap != None) {
549 XCopyArea(dpy, rip_pixmap, XtWindow(w), ggc, event->x, event->y,
550 event->width, event->height, event->x, event->y);
553 x = appResources.tombtext_x;
554 y = appResources.tombtext_y;
555 for (i = 0; i <= YEAR_LINE; i++) {
556 int len = strlen(rip_line[i]);
557 XFontStruct *font = WindowFontStruct(w);
558 int width = XTextWidth(font, rip_line[i], len);
560 XDrawString(dpy, XtWindow(w), ggc, x - width / 2, y, rip_line[i], len);
561 x += appResources.tombtext_dx;
562 y += appResources.tombtext_dy;
565 XtReleaseGC(w, ggc);
569 * The ripout window creation routine.
571 static Widget
572 create_ripout_widget(Widget parent)
574 Widget imageport;
575 Arg args[16];
576 Cardinal num_args;
578 static int rip_width, rip_height;
580 if (!rip_image) {
581 XpmAttributes attributes;
582 int errorcode;
584 attributes.valuemask = XpmCloseness;
585 attributes.closeness = 65535; /* Try anything */
586 errorcode = XpmReadFileToImage(XtDisplay(parent),
587 appResources.tombstone,
588 &rip_image, 0, &attributes);
589 if (errorcode != XpmSuccess) {
590 char buf[BUFSZ];
592 Sprintf(buf, "Failed to load %s: %s", appResources.tombstone,
593 XpmGetErrorString(errorcode));
594 X11_raw_print(buf);
595 return (Widget) 0;
597 rip_width = rip_image->width;
598 rip_height = rip_image->height;
601 num_args = 0;
602 XtSetArg(args[num_args], XtNwidth, rip_width);
603 num_args++;
604 XtSetArg(args[num_args], XtNheight, rip_height);
605 num_args++;
606 XtSetArg(args[num_args], XtNtranslations,
607 XtParseTranslationTable(rip_translations));
608 num_args++;
610 imageport = XtCreateManagedWidget("rip", windowWidgetClass, parent, args,
611 num_args);
613 XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0);
615 return imageport;
618 #endif /* GRAPHIC_TOMBSTONE */
620 /*wintext.c*/