1 /* NetHack 3.6 winmesg.c $NHDT-Date: 1454811935 2016/02/07 02:25:35 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.10 $ */
2 /* Copyright (c) Dean Luick, 1992 */
3 /* NetHack may be freely redistributed. See license for details. */
6 * Message window routines.
9 * create_message_window()
10 * destroy_message_window()
11 * display_message_window()
16 #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/Shell.h>
22 #include <X11/Xaw/Cardinals.h>
23 #include <X11/Xaw/Viewport.h>
24 #include <X11/Xatom.h>
26 #ifdef PRESERVE_NO_SYSV
30 #undef PRESERVE_NO_SYSV
33 #include "xwindow.h" /* Window widget declarations */
38 static struct line_element *FDECL(get_previous, (struct line_element *));
39 static void FDECL(set_circle_buf, (struct mesg_info_t *, int));
41 static char *FDECL(split, (char *, XFontStruct *, DIMENSION_P));
43 static void FDECL(add_line, (struct mesg_info_t *, const char *));
44 static void FDECL(redraw_message_window, (struct xwindow *));
45 static void FDECL(mesg_check_size_change, (struct xwindow *));
46 static void FDECL(mesg_exposed, (Widget, XtPointer, XtPointer));
47 static void FDECL(get_gc, (Widget, struct mesg_info_t *));
48 static void FDECL(mesg_resized, (Widget, XtPointer, XtPointer));
50 static char mesg_translations[] = "#override\n\
51 <Key>Left: scroll(4)\n\
52 <Key>Right: scroll(6)\n\
54 <Key>Down: scroll(2)\n\
57 /* Move the message window's vertical scrollbar's slider to the bottom. */
59 set_message_slider(wp)
65 scrollbar = XtNameToWidget(XtParent(wp->w), "vertical");
69 XtCallCallbacks(scrollbar, XtNjumpProc, &top);
74 create_message_window(wp, create_popup, parent)
75 struct xwindow *wp; /* window pointer */
82 struct mesg_info_t *mesg_info;
84 XFontSetExtents *extent;
87 wp->type = NHW_MESSAGE;
89 wp->mesg_information = mesg_info =
90 (struct mesg_info_t *) alloc(sizeof (struct mesg_info_t));
93 mesg_info->num_lines = 0;
94 mesg_info->head = mesg_info->line_here = mesg_info->last_pause =
95 mesg_info->last_pause_head = (struct line_element *) 0;
96 mesg_info->dirty = False;
97 mesg_info->viewport_width = mesg_info->viewport_height = 0;
99 if (iflags.msg_history < (unsigned) appResources.message_lines)
100 iflags.msg_history = (unsigned) appResources.message_lines;
101 if (iflags.msg_history > MAX_HISTORY) /* a sanity check */
102 iflags.msg_history = MAX_HISTORY;
104 set_circle_buf(mesg_info, (int) iflags.msg_history);
106 /* Create a popup that becomes the parent. */
109 XtSetArg(args[num_args], XtNallowShellResize, True);
113 XtCreatePopupShell("message_popup", topLevelShellWidgetClass,
114 toplevel, args, num_args);
116 * If we're here, then this is an auxiliary message window. If we're
117 * cancelled via a delete window message, we should just pop down.
122 * Create the viewport. We only want the vertical scroll bar ever to be
123 * visible. If we allow the horizontal scrollbar to be visible it will
124 * always be visible, due to the stupid way the Athena viewport operates.
127 XtSetArg(args[num_args], XtNallowVert, True);
129 viewport = XtCreateManagedWidget(
130 "mesg_viewport", /* name */
131 viewportWidgetClass, /* widget class from Window.h */
132 parent, /* parent widget */
133 args, /* set some values */
134 num_args); /* number of values to set */
137 * Create a message window. We will change the width and height once
138 * we know what font we are using.
142 XtSetArg(args[num_args], XtNtranslations,
143 XtParseTranslationTable(mesg_translations));
146 wp->w = XtCreateManagedWidget(
147 "message", /* name */
148 windowWidgetClass, /* widget class from Window.h */
149 viewport, /* parent widget */
150 args, /* set some values */
151 num_args); /* number of values to set */
153 XtAddCallback(wp->w, XtNexposeCallback, mesg_exposed, (XtPointer) 0);
156 * Now adjust the height and width of the message window so that it
157 * is appResources.message_lines high and DEFAULT_MESSAGE_WIDTH wide.
160 /* Get the font information. */
162 XtSetArg(args[num_args], XtNfont, &mesg_info->fs);
165 XtSetArg(args[num_args], XtNfontSet, &mesg_info->fontset);
168 XtGetValues(wp->w, args, num_args);
170 extent = XExtentsOfFontSet(mesg_info->fontset);
173 /* Save character information for fast use later. */
175 mesg_info->char_width = mesg_info->fs->max_bounds.width;
176 mesg_info->char_height =
177 mesg_info->fs->max_bounds.ascent + mesg_info->fs->max_bounds.descent;
178 mesg_info->char_ascent = mesg_info->fs->max_bounds.ascent;
179 mesg_info->char_lbearing = -mesg_info->fs->min_bounds.lbearing;
181 mesg_info->char_width = extent->max_logical_extent.width;
182 mesg_info->char_height = extent->max_logical_extent.height;
183 mesg_info->char_ascent = -extent->max_logical_extent.y;
184 mesg_info->char_lbearing = extent->max_logical_extent.x;
187 get_gc(wp->w, mesg_info);
189 wp->pixel_height = ((int) iflags.msg_history) * mesg_info->char_height;
191 /* If a variable spaced font, only use 2/3 of the default size */
192 if (mesg_info->fs->min_bounds.width != mesg_info->fs->max_bounds.width) {
193 wp->pixel_width = ((2 * DEFAULT_MESSAGE_WIDTH) / 3)
194 * mesg_info->fs->max_bounds.width;
197 (DEFAULT_MESSAGE_WIDTH * mesg_info->fs->max_bounds.width);
199 /* Set the new width and height. */
201 XtSetArg(args[num_args], XtNwidth, wp->pixel_width);
203 XtSetArg(args[num_args], XtNheight, wp->pixel_height);
205 XtSetValues(wp->w, args, num_args);
207 /* make sure viewport height makes sense before realizing it */
209 mesg_info->viewport_height =
210 appResources.message_lines * mesg_info->char_height;
211 XtSetArg(args[num_args], XtNheight, mesg_info->viewport_height);
213 XtSetValues(viewport, args, num_args);
215 XtAddCallback(wp->w, XtNresizeCallback, mesg_resized, (XtPointer) 0);
218 * If we have created our own popup, then realize it so that the
219 * viewport is also realized.
222 XtRealizeWidget(wp->popup);
223 XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup),
224 &wm_delete_window, 1);
229 destroy_message_window(wp)
233 nh_XtPopdown(wp->popup);
234 if (!wp->keep_window)
235 XtDestroyWidget(wp->popup), wp->popup = (Widget) 0;
237 if (wp->mesg_information) {
238 set_circle_buf(wp->mesg_information, 0); /* free buffer list */
239 free((genericptr_t) wp->mesg_information), wp->mesg_information = 0;
242 XtRemoveCallback(wp->w, XtNexposeCallback, mesg_exposed,
248 /* Redraw message window if new lines have been added. */
250 display_message_window(wp)
253 set_message_slider(wp);
254 if (wp->mesg_information->dirty)
255 redraw_message_window(wp);
259 * Append a line of text to the message window. Split the line if the
260 * rendering of the text is too long for the window.
263 append_message(wp, str)
269 XRectangle ink_ext, lgc_ext;
270 char ss[1024],s1[1024],s2[1024]; /* may be enough */
276 XmbTextExtents(wp->mesg_information->fontset, ss, len,
278 if(lgc_ext.width < wp->pixel_width)
282 if( len >= strlen(ss)){
283 add_line(wp->mesg_information, ss);
286 split_japanese(ss, s1, s2, len);
287 add_line(wp->mesg_information, s1);
293 char *mark, *remainder, buf[BUFSZ];
298 Strcpy(buf, str); /* we might mark it up */
303 remainder = split(mark, wp->mesg_information->fs, wp->pixel_width);
304 add_line(wp->mesg_information, mark);
307 add_line(wp->mesg_information, buf);
312 /* private functions =======================================================
316 * Return the element in the circular linked list just before the given
319 static struct line_element *
321 struct line_element *mark;
323 struct line_element *curr;
326 return (struct line_element *) 0;
328 for (curr = mark; curr->next != mark; curr = curr->next)
334 * Set the information buffer size to count lines. We do this by creating
335 * a circular linked list of elements, each of which represents a line of
336 * text. New buffers are created as needed, old ones are freed if they
337 * are no longer used.
340 set_circle_buf(mesg_info, count)
341 struct mesg_info_t *mesg_info;
345 struct line_element *tail, *curr, *head;
348 panic("set_circle_buf: bad count [= %d]", count);
349 if (count == mesg_info->num_lines)
350 return; /* no change in size */
352 if (count < mesg_info->num_lines) {
354 * Toss num_lines - count line entries from our circular list.
356 * We lose lines from the front (top) of the list. We _know_
357 * the list is non_empty.
359 tail = get_previous(mesg_info->head);
360 for (i = mesg_info->num_lines - count; i > 0; i--) {
361 curr = mesg_info->head;
362 mesg_info->head = curr->next;
364 free((genericptr_t) curr->line);
365 free((genericptr_t) curr);
368 /* make sure we don't have a dangling pointer */
369 mesg_info->head = (struct line_element *) 0;
371 tail->next = mesg_info->head; /* link the tail to the head */
375 * Add count - num_lines blank lines to the head of the list.
377 * Create a separate list, keeping track of the tail.
379 for (head = tail = 0, i = 0; i < count - mesg_info->num_lines; i++) {
380 curr = (struct line_element *) alloc(sizeof(struct line_element));
382 curr->buf_length = 0;
383 curr->str_length = 0;
392 * Complete the circle by making the new tail point to the old head
393 * and the old tail point to the new head. If our line count was
394 * zero, then make the new list circular.
396 if (mesg_info->num_lines) {
397 curr = get_previous(mesg_info->head); /* get end of old list */
399 tail->next = mesg_info->head; /* new tail -> old head */
400 curr->next = head; /* old tail -> new head */
404 mesg_info->head = head;
407 mesg_info->num_lines = count;
408 /* Erase the line on a resize. */
409 mesg_info->last_pause = (struct line_element *) 0;
413 * Make sure the given string is shorter than the given pixel width. If
414 * not, back up from the end by words until we find a place to split.
418 split(s, fs, pixel_width)
420 XFontStruct *fs; /* Font for the window. */
421 Dimension pixel_width;
423 char save, *end, *remainder;
427 end = eos(s); /* point to null at end of string */
429 /* assume that if end == s, XXXXXX returns 0) */
430 while ((Dimension) XTextWidth(fs, s, (int) strlen(s)) > pixel_width) {
432 while (*end != ' ') {
434 panic("split: eos!");
446 * Add a line of text to the window. The first line in the curcular list
447 * becomes the last. So all we have to do is copy the new line over the
448 * old one. If the line buffer is too small, then allocate a new, larger
452 add_line(mesg_info, s)
453 struct mesg_info_t *mesg_info;
456 register struct line_element *curr = mesg_info->head;
457 register int new_line_length = strlen(s);
459 if (new_line_length + 1 > curr->buf_length) {
461 free(curr->line); /* free old line */
463 curr->buf_length = new_line_length + 1;
464 curr->line = (char *) alloc((unsigned) curr->buf_length);
467 Strcpy(curr->line, s); /* copy info */
468 curr->str_length = new_line_length; /* save string length */
470 mesg_info->head = mesg_info->head->next; /* move head to next line */
471 mesg_info->dirty = True; /* we have undrawn lines */
475 * Save a position in the text buffer so we can draw a line to seperate
476 * text from the last time this function was called.
478 * Save the head position, since it is the line "after" the last displayed
479 * line in the message window. The window redraw routine will draw a
480 * line above this saved pointer.
486 register struct mesg_info_t *mesg_info = wp->mesg_information;
490 * If we've erased the pause line and haven't added any new lines,
491 * don't try to erase the line again.
493 if (!mesg_info->last_pause
494 && mesg_info->last_pause_head == mesg_info->head)
497 if (mesg_info->last_pause == mesg_info->head) {
498 /* No new messages in last turn. Redraw window to erase line. */
499 mesg_info->last_pause = (struct line_element *) 0;
500 mesg_info->last_pause_head = mesg_info->head;
501 redraw_message_window(wp);
504 mesg_info->last_pause = mesg_info->head;
511 redraw_message_window(wp)
514 struct mesg_info_t *mesg_info = wp->mesg_information;
515 register struct line_element *curr;
516 register int row, y_base;
519 * Do this the cheap and easy way. Clear the window and just redraw
522 * This could be done more effecently with one call to XDrawText() instead
523 * of many calls to XDrawString(). Maybe later.
525 * Only need to clear if window has new text.
527 if (mesg_info->dirty) {
528 XClearWindow(XtDisplay(wp->w), XtWindow(wp->w));
529 mesg_info->line_here = mesg_info->last_pause;
532 /* For now, just update the whole shootn' match. */
533 for (y_base = row = 0, curr = mesg_info->head; row < mesg_info->num_lines;
534 row++, y_base += mesg_info->char_height, curr = curr->next) {
536 XDrawString(XtDisplay(wp->w), XtWindow(wp->w), mesg_info->gc,
537 mesg_info->char_lbearing, mesg_info->char_ascent + y_base,
538 curr->line, curr->str_length);
540 XmbDrawString(XtDisplay(wp->w), XtWindow(wp->w),
543 mesg_info->char_lbearing,
544 mesg_info->char_ascent + y_base,
549 * This draws a line at the _top_ of the line of text pointed to by
550 * mesg_info->last_pause.
552 if (appResources.message_line && curr == mesg_info->line_here) {
553 XDrawLine(XtDisplay(wp->w), XtWindow(wp->w), mesg_info->gc, 0,
554 y_base, wp->pixel_width, y_base);
558 mesg_info->dirty = False;
562 * Check the size of the viewport. If it has shrunk, then we want to
563 * move the vertical slider to the bottom.
566 mesg_check_size_change(wp)
569 struct mesg_info_t *mesg_info = wp->mesg_information;
571 Dimension new_width, new_height;
574 viewport = XtParent(wp->w);
576 XtSetArg(arg[0], XtNwidth, &new_width);
577 XtSetArg(arg[1], XtNheight, &new_height);
578 XtGetValues(viewport, arg, TWO);
580 /* Only move slider to bottom if new size is smaller. */
581 if (new_width < mesg_info->viewport_width
582 || new_height < mesg_info->viewport_height) {
583 set_message_slider(wp);
586 mesg_info->viewport_width = new_width;
587 mesg_info->viewport_height = new_height;
590 /* Event handler for message window expose events. */
593 mesg_exposed(w, client_data, widget_data)
595 XtPointer client_data; /* unused */
596 XtPointer widget_data; /* expose event from Window widget */
598 XExposeEvent *event = (XExposeEvent *) widget_data;
602 if (XtIsRealized(w) && event->count == 0) {
609 * Drain all pending expose events for the message window;
610 * we'll redraw the whole thing at once.
614 while (XCheckTypedWindowEvent(dpy, win, Expose, &evt))
618 if (wp->keep_window && !wp->mesg_information)
620 mesg_check_size_change(wp);
621 redraw_message_window(wp);
628 struct mesg_info_t *mesg_info;
631 XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont;
632 Pixel fgpixel, bgpixel;
635 XtSetArg(arg[0], XtNforeground, &fgpixel);
636 XtSetArg(arg[1], XtNbackground, &bgpixel);
637 XtGetValues(w, arg, TWO);
639 values.foreground = fgpixel;
640 values.background = bgpixel;
641 values.function = GXcopy;
642 values.font = WindowFont(w);
643 mesg_info->gc = XtGetGC(w, mask, &values);
647 * Handle resizes on a message window. Correct saved pixel height and width.
648 * Adjust circle buffer to accomidate the new size.
650 * Problem: If the resize decreases the width of the window such that
651 * some lines are now longer than the window, they will be cut off by
652 * X itself. All new lines will be split to the new size, but the ends
653 * of the old ones will not be seen again unless the window is lengthened.
654 * I don't deal with this problem because it isn't worth the trouble.
658 mesg_resized(w, client_data, call_data)
660 XtPointer call_data, client_data;
664 Dimension pixel_width, pixel_height;
669 old_lines = wp->mesg_information->num_lines;
677 XtSetArg(args[num_args], XtNwidth, &pixel_width);
679 XtSetArg(args[num_args], XtNheight, &pixel_height);
681 XtGetValues(w, args, num_args);
684 wp->pixel_width = pixel_width;
685 wp->pixel_height = pixel_height;
687 set_circle_buf(wp->mesg_information,
688 (int) pixel_height / wp->mesg_information->char_height);
691 printf("Message resize. Pixel: width = %d, height = %d; Lines: old = "
693 pixel_width, pixel_height, old_lines,
694 wp->mesg_information->num_lines);