1 #include "xdvi-config.h"
2 
3 #include <X11/Intrinsic.h>
4 #include <X11/StringDefs.h>
5 #include <X11/Shell.h>
6 #include <X11/StringDefs.h>
7 
8 
9 #if !MOTIF
10 # include <X11/Xaw/Form.h>
11 #endif
12 
13 #include "x_util.h"
14 #include "string-utils.h"
15 #include "util.h"
16 #include "statusline.h"
17 #include "message-window.h"
18 #include "events.h" /* for set_bar_value() */
19 
20 #define DEBUG_SCROLL_IF_NEEDED 0
21 
22 Boolean do_autoscroll = False;
23 
24 
25 /* Center window wa over window wb */
26 void
center_window(Widget wa,Widget wb)27 center_window(Widget wa, Widget wb)
28 {
29     Position x, y;
30     Dimension w1, h1, w2, h2;
31 
32     if (!XtIsRealized(wa) || !XtIsRealized(wb))
33 	return;
34 
35     XtVaGetValues(wa,
36 		  XtNwidth, &w1,
37 		  XtNheight, &h1,
38 		  NULL);
39     XtVaGetValues(wb,
40 		  XtNwidth, &w2,
41 		  XtNheight, &h2,
42 		  XtNx, &x,
43 		  XtNy, &y,
44 		  NULL);
45     XtVaSetValues(wa, XtNx, x + (w2 - w1) / 2,
46 		  XtNy, y + (h2 - h1) / 2,
47 		  NULL);
48 }
49 
50 /* Position window w at coordinates x, y */
51 void
position_window(Widget w,Position x,Position y)52 position_window(Widget w, Position x, Position y)
53 {
54     if (!XtIsRealized(w))
55 	return;
56 
57     TRACE_GUI((stderr, "positioning %ld at %d, %d", (unsigned long)w, x, y));
58     XtVaSetValues(w, XtNx, x, XtNy, y, NULL);
59 }
60 
61 /*
62   Used for the hyperref and forward search markers: scroll the page
63   to make the marker visible.
64 */
65 void
scroll_page_if_needed(int x_min,int x_max,int y_min,int y_max)66 scroll_page_if_needed(int x_min, int x_max, int y_min, int y_max)
67 {
68     Position drawing_x, drawing_y, drawing_h, clip_x, clip_y, clip_h, clip_w;
69     int test_scroll, need_v_scroll = 0, need_h_scroll = 0;
70 
71     if (!do_autoscroll)
72 	return;
73 
74     XtVaGetValues(globals.widgets.clip_widget,
75 		  XtNx, &clip_x, XtNy, &clip_y,
76 		  XtNheight, &clip_h, XtNwidth, &clip_w,
77 		  NULL);
78     XtVaGetValues(globals.widgets.draw_widget,
79 		  XtNx, &drawing_x, XtNy, &drawing_y,
80 		  XtNheight, &drawing_h,
81 		  NULL);
82 
83 #if DEBUG_SCROLL_IF_NEEDED
84     fprintf(stderr, "y: %d, drawing_y: %d, clip_h: %d\n", y, drawing_y, clip_h);
85 #endif
86     /* check if we need to scroll vertically; first, down for y_min */
87     test_scroll = y_min + drawing_y - clip_h;
88     if ((resource.expert_mode & XPRT_SHOW_STATUSLINE) != 0)
89 	test_scroll += get_statusline_height();
90 
91     TRACE_SRC((stderr, "test_scroll vertically: %d", test_scroll));
92 #if DEBUG_SCROLL_IF_NEEDED
93     fprintf(stderr, "%d + %d > %d?\n", drawing_y, y_min, clip_h);
94 #endif
95     if (test_scroll > 0) { /* need to scroll down? */
96 	need_v_scroll = test_scroll;
97 	TRACE_SRC((stderr, "need_v_scroll down: %d", need_v_scroll));
98     }
99     else if (abs(drawing_y) + 2 > y_max) { /* need to scroll up? */
100 	need_v_scroll = -((abs(drawing_y) - y_max) + 1);
101 	TRACE_SRC((stderr, "need_v_scroll up: %d (%d > %d; %d)", need_v_scroll, abs(drawing_y), y_max, clip_y));
102     }
103 
104     /* check if we need to scroll horizontally; x_min < 0 blocks this
105        (e.g. for hyperref, where we don't want it) */
106     if (x_min >= 0) {
107 	test_scroll = x_min + drawing_x - clip_w + 1;
108 	TRACE_SRC((stderr, "test_scroll horizontally: %d", test_scroll));
109 
110 	if (test_scroll > 0) {
111 	    /* need to scroll to right (i.e. make stuff on right-hand side visible)? */
112 	    need_h_scroll = test_scroll;
113 	    TRACE_SRC((stderr, "need_h_scroll right: %d", need_h_scroll));
114 	}
115 	else if (abs(drawing_x) > x_max) {
116 	    /* need to scroll to left (i.e. make stuff on left-hand side visible)? */
117 	    need_h_scroll =  -(abs(drawing_x) - x_max);
118 	    TRACE_SRC((stderr, "need_h_scroll left: %d", need_h_scroll));
119 	}
120     }
121 
122     /* FIXME: should we not scroll if keep_flag is active? */
123     if (need_v_scroll != 0 && globals.widgets.y_bar != NULL) {
124 #ifdef MOTIF
125 	/* need to add new value to current one */
126 	XtVaGetValues(globals.widgets.y_bar, XmNvalue, &test_scroll, NULL);
127 	(void)set_bar_value(globals.widgets.y_bar, test_scroll + need_v_scroll, (int)(globals.page.h - mane.height));
128 #else
129 	XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, cast_int_to_XtPointer(need_v_scroll));
130 #endif
131     }
132 
133     if (need_h_scroll != 0 && globals.widgets.x_bar != NULL) {
134 #ifdef MOTIF
135 	/* need to add new value to current one */
136 	XtVaGetValues(globals.widgets.x_bar, XmNvalue, &test_scroll, NULL);
137 	(void)set_bar_value(globals.widgets.x_bar, test_scroll + need_h_scroll, (int)(globals.page.w - mane.width));
138 #else
139 	XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, cast_int_to_XtPointer(need_h_scroll));
140 #endif
141     }
142 
143     do_autoscroll = False;
144 }
145 
adjust_width(Widget a,Widget b)146 void adjust_width(Widget a, Widget b)
147 {
148     Dimension w1, w2;
149 
150     XtVaGetValues(a, XtNwidth, &w1, NULL);
151     XtVaGetValues(b, XtNwidth, &w2, NULL);
152 
153     if (w1 < w2)
154 	XtVaSetValues(a, XtNwidth, w2, NULL);
155     else if (w2 > w1)
156 	XtVaSetValues(b, XtNwidth, w1, NULL);
157 }
158 
159 /* change a GC and return it, or create one if it doesn't exist yet */
160 GC
set_or_make_gc(GC gc,int function,Pixel fg,Pixel bg)161 set_or_make_gc(GC gc, int function, Pixel fg, Pixel bg)
162 {
163     XGCValues values;
164     values.function = function;
165     values.foreground = fg;
166     values.background = bg;
167 
168     /* Since print is in round dots we make drawings as "smooth" as possible. */
169     values.cap_style = CapRound;
170     values.join_style = JoinRound;
171 
172     if (gc != NULL)
173 	XChangeGC(DISP, gc, GCFunction | GCForeground | GCBackground
174 		  | GCCapStyle | GCJoinStyle, &values);
175     else
176 	gc = XCreateGC(DISP, XtWindow(globals.widgets.top_level), GCFunction | GCForeground | GCBackground
177 		       | GCCapStyle | GCJoinStyle, &values);
178 
179     return gc;
180 }
181 
182 /*
183  *	Atom handling.
184  */
185 
186 static char *atom_names[] = {
187     "XDVI_WINDOWS",
188     "DVI_NAME",
189     "SRC_GOTO",
190     "RELOAD",
191     "NEWDOC",
192     "NEWPAGE",
193     "RAISE",
194     "FIND_STRING",
195     "REREAD_PREFS"
196 };
197 static Atom atoms[XtNumber(atom_names)];
198 
199 /*
200  *	On 64-bit platforms, XGetWindowProperty and related functions convert
201  *	properties with format=32 to arrays of longs.  This function keeps that
202  *	convention.
203  *	The return value is the total number of bytes in the buffer.
204  */
205 
206 #if defined(WORD64) || defined(LONG64)
207 # define LONG_CONV_64(bytes, format)	((bytes) << ((format) >> 5))
208 #else
209 # define LONG_CONV_64(bytes, format)	(bytes)
210 #endif
211 
212 size_t
property_get_data(Window w,Atom a,char ** ret_buf,int (* x_get_property)(Display *,Window,Atom,long,long,Bool,Atom,Atom *,int *,unsigned long *,unsigned long *,unsigned char **))213 property_get_data(Window w, Atom a, char **ret_buf,
214 		  int (*x_get_property)(Display *, Window, Atom, long,
215 					long, Bool, Atom,
216 					Atom *, int *, unsigned long *,
217 					unsigned long *, unsigned char **))
218 {
219     /* all of these are in 8-bit units */
220     unsigned long byte_offset = 0;
221     Atom type_ret;
222     int format_ret;
223     unsigned long nitems_ret;
224     unsigned long bytes_after_ret = 0;
225     unsigned char *prop_ret = NULL;
226 
227     /*
228      * buffer for collecting returned data; this is static to
229      * avoid expensive malloc()s at every call (which is often!)
230      */
231     static unsigned char *buffer = NULL;
232     static size_t buffer_len = 0;
233 
234     while (x_get_property(DISP, w,
235 			  a, byte_offset / 4, (bytes_after_ret + 3) / 4, False,
236 			  a, &type_ret, &format_ret, &nitems_ret,
237 			  &bytes_after_ret, &prop_ret)
238 	   == Success) {
239 
240 	if (type_ret != a || format_ret == 0)
241 	    break;
242 
243 	nitems_ret *= (format_ret / 8);	/* convert to bytes */
244 
245 	if (LONG_CONV_64(byte_offset + nitems_ret, format_ret) >= buffer_len) {
246 	    buffer_len += 256
247 			  * ((LONG_CONV_64(byte_offset + nitems_ret, format_ret)
248 			      - buffer_len) / 256 + 1);
249 	    buffer = (buffer == NULL ? xmalloc(buffer_len)
250 				     : xrealloc(buffer, buffer_len));
251 	}
252 
253 	/* the +1 captures the extra '\0' that Xlib puts after the end.  */
254 	memcpy(buffer + LONG_CONV_64(byte_offset, format_ret), prop_ret,
255 	       LONG_CONV_64(nitems_ret, format_ret) + 1);
256 	byte_offset += nitems_ret;
257 
258 	XFree(prop_ret);
259 	prop_ret = NULL;
260 
261 	if (bytes_after_ret == 0)	/* got all data */
262 	    break;
263     }
264 
265     if (prop_ret != NULL)
266 	XFree(prop_ret);
267 
268     *ret_buf = (char *)buffer;
269     return LONG_CONV_64(byte_offset, format_ret);
270 }
271 
272 static size_t
property_get_window_list(long ** window_list)273 property_get_window_list(long **window_list)
274 {
275     size_t len = property_get_data(DefaultRootWindow(DISP),
276 				   atom_xdvi_windows(), (char **) window_list,
277 				   XGetWindowProperty);
278     if (len == 0) {
279 	TRACE_CLIENT((stderr, "No \"xdvi windows\" property found"));
280 	return 0;
281     }
282 
283     if (len % sizeof(long) != 0) {
284 	TRACE_CLIENT((stderr,
285 		"\"XDVI_WINDOWS\" property had incorrect size; deleting it."));
286 	XDeleteProperty(DISP, DefaultRootWindow(DISP), atom_xdvi_windows());
287 	return 0;
288     }
289 
290     return len / sizeof (long);
291 }
292 
293 /**
294  **	set_dvi_property sets the appropriate property for the main
295  **	window (used in source special handoff).
296  **/
297 
298 void
set_dvi_property(void)299 set_dvi_property(void)
300 {
301     XChangeProperty(DISP, XtWindow(globals.widgets.top_level), atom_dvi_file(), atom_dvi_file(),
302 		    8, PropModeReplace, (unsigned char *)dvi_property, dvi_property_length);
303 }
304 
305 
306 /*
307  * Delete all occurrences of window w from the window list property.
308  * Then, if `prepend' is true, prepend the window ID to the existing list.
309  */
310 void
update_window_property(Window w,Boolean prepend)311 update_window_property(Window w, Boolean prepend)
312 {
313     long *wlist;
314     size_t wlist_len;
315     long *wlist_end;
316     long *wp;
317 #if 0
318     int i;
319 #endif /* 0 */
320 
321     /* this allocates wlist */
322     if ((wlist_len = property_get_window_list(&wlist)) == 0)
323 	return;
324 
325     /* Loop over list of windows.  */
326     wlist_end = wlist + wlist_len;
327 
328 #if 0
329     for (i = 0, wp = wlist; wp < wlist_end; ++wp, ++i) {
330 	fprintf(stderr, "WIN %d: %08lx; len: %d\n", i, *wp, wlist_len);
331     }
332 #endif /* 0 */
333 
334     for (wp = wlist; wp < wlist_end; ++wp) {
335 	if (*wp == w) { /* match, remove our ID */
336 	    --wlist_len;
337 	    --wlist_end;
338 	    memmove(wp, wp + 1, (wlist_end - wp) * sizeof (long));
339 	    --wp; /* new item is now at wp; don't skip it in next iteration */
340 	}
341     }
342 
343     if (prepend) { /* add our ID again to front */
344 	/* Note: no need to realloc wlist, since the original length
345 	   was sufficient for all elements.
346 	*/
347 	memmove(wlist + 1, wlist, wlist_len * sizeof (long));
348 	++wlist_len;
349 	*wlist = w;
350     }
351 
352     if (wlist_len == 0)
353 	XDeleteProperty(DISP, DefaultRootWindow(DISP),
354 			atom_xdvi_windows());
355     else
356 	XChangeProperty(DISP, DefaultRootWindow(DISP),
357 			atom_xdvi_windows(), atom_xdvi_windows(), 32,
358 			PropModeReplace, (unsigned char *)wlist, wlist_len);
359 
360     XFlush(DISP);
361 }
362 
363 void
property_initialize(void)364 property_initialize(void)
365 {
366     size_t i;
367 
368 #if XlibSpecificationRelease >= 6
369     if (!XInternAtoms(DISP, atom_names, XtNumber(atom_names), False, atoms))
370 	XDVI_FATAL((stderr, "XtInternAtoms failed."));
371 #else
372     for (i = 0; i < XtNumber(atom_names); i++) {
373 	if ((atoms[i] = XInternAtom(DISP, atom_names[i], False)) == None)
374 	    XDVI_FATAL((stderr, "XtInternAtoms failed."));
375     }
376 #endif
377 
378     if (globals.debug & DBG_CLIENT) {
379 	for (i = 0; i < XtNumber(atom_names); i++)
380 	    TRACE_CLIENT((stderr, "Atom(%s) = %lu", atom_names[i], atoms[i]));
381     }
382 }
383 
384 
atom_xdvi_windows(void)385 Atom atom_xdvi_windows(void)
386 {
387     return atoms[0];
388 }
389 
atom_dvi_file(void)390 Atom atom_dvi_file(void)
391 {
392     return atoms[1];
393 }
394 
atom_src_goto(void)395 Atom atom_src_goto(void)
396 {
397     return atoms[2];
398 }
399 
atom_reload(void)400 Atom atom_reload(void)
401 {
402     return atoms[3];
403 }
404 
atom_newdoc(void)405 Atom atom_newdoc(void)
406 {
407     return atoms[4];
408 }
409 
atom_newpage(void)410 Atom atom_newpage(void)
411 {
412     return atoms[5];
413 }
414 
atom_raise(void)415 Atom atom_raise(void)
416 {
417     return atoms[6];
418 }
419 
atom_find_string(void)420 Atom atom_find_string(void)
421 {
422     return atoms[7];
423 }
424 
atom_reread_prefs(void)425 Atom atom_reread_prefs(void)
426 {
427     return atoms[8];
428 }
429 
430 /*
431  * Syntesize a mouse-1 down event for the Widget w passed as second argument.
432  * This can be, e.g. a dialog button, or some other window.
433  */
434 void
synthesize_event(XEvent * ev,Widget w)435 synthesize_event(XEvent *ev, Widget w)
436 {
437     memset(ev, 0, sizeof(XButtonPressedEvent));
438     ev->type = ButtonPress;
439     ev->xbutton.serial = 1;
440     ev->xbutton.send_event = True;
441     ev->xbutton.button = 1;
442     ev->xbutton.display = XtDisplayOfObject(w);
443     ev->xbutton.window = XtWindowOfObject(w);
444 }
445 
446 #ifdef MOTIF
xm_get_height(Widget w)447 int xm_get_height(Widget w)
448 {
449     int ret_h = 0;
450     static Dimension h0, h1, h2;
451     static Arg args[] = {
452 	{XmNheight, (XtArgVal) &h0},
453 	{XmNmarginHeight, (XtArgVal) &h1},
454 	{XmNshadowThickness, (XtArgVal) &h2},
455     };
456     ASSERT(w != NULL, "widget in xm_get_width mustn't be NULL!");
457 
458     XtGetValues(w, args, XtNumber(args));
459     ret_h = h0 + 2 * h1 + 2 * h2;
460     TRACE_GUI((stderr, "xm_get_height: %d", ret_h));
461     return ret_h;
462 }
463 
xm_get_width(Widget w)464 int xm_get_width(Widget w)
465 {
466     int ret_w = 0;
467     static Arg args = { XtNwidth, (XtArgVal)0 };
468     ASSERT(w != NULL, "widget in xm_get_width mustn't be NULL!");
469 
470     args.value = (XtArgVal)&ret_w;
471     XtGetValues(w, &args, 1);
472     TRACE_GUI((stderr, "xm_get_width: %d", ret_w));
473     return ret_w;
474 }
475 
476 /*
477  * Get pixel from color `colorname'. We try to keep this as
478  * high-level as possible, with simple black as fallback.
479  */
480 void
str_to_pixel(Widget w,const char * colorname,Pixel * ret)481 str_to_pixel(Widget w, const char *colorname, Pixel *ret)
482 {
483     XrmValue from, to;
484 
485     from.addr = (char *)colorname;
486     from.size = strlen(from.addr) + 1;
487     to.addr = (XtPointer)ret;
488     to.size = sizeof(Pixel);
489     if (!XtConvertAndStore(w, XtRString, &from, XtRPixel, &to)) {
490 	fprintf(stderr, "String to pixel conversion failed for %s!\n", colorname);
491 	from.addr = (char *)"black";
492 	from.size = strlen(from.addr) + 1;
493 	to.addr = (XtPointer)ret;
494 	to.size = sizeof(Pixel);
495 	XtConvertAndStore(w, XtRString, &from, XtRPixel, &to);
496     }
497 }
498 
499 /*
500  * Convert pixel value `pix' to color name passed as str,
501  * of length len, or `black' if conversion failed.
502  * -- FIXME: This is broken!!!
503  */
504 void
pixel_to_str(Widget w,Pixel pix,char * str,size_t len)505 pixel_to_str(Widget w, Pixel pix, char *str, size_t len)
506 {
507     XrmValue from, to;
508 
509     from.addr = (XtPointer)&pix;
510     from.size = sizeof(Pixel);
511     to.addr = str;
512     to.size = len;
513 
514     if (!XtConvertAndStore(w, XtRString, &from, XtRPixel, &to)) {
515 	fprintf(stderr, "Pixel to String conversion failed for %ld!\n", pix);
516 	sprintf(str, "white");
517     }
518 }
519 
520 /* Free color, and initialize it anew with pixel value `pix' */
521 void
pixel_to_color(Pixel pix,XColor * color,Display * display,Colormap colormap)522 pixel_to_color(Pixel pix, XColor *color, Display *display, Colormap colormap)
523 {
524     XColor test;
525     test.pixel = pix;
526     XQueryColor(display, colormap, &test);
527 
528     color->red = test.red;
529     color->green = test.green;
530     color->blue = test.blue;
531 
532     if (!XAllocColor(display, colormap, color)) {
533 	fprintf(stderr, "Fatal: Couldn't XAllocColor!");
534 	exit(1);
535     }
536 }
537 #endif /* MOTIF */
538 
539 /* helper routine for get_matching_parent() */
540 static Widget
matches_parent(Widget w,const char * name)541 matches_parent(Widget w, const char *name)
542 {
543     for (; w != NULL; w = XtParent(w)) {
544 	char *ptr;
545 	ptr = XtName(w);
546 	TRACE_GUI((stderr, "parent: %s", ptr == NULL ? "<NULL>" : ptr));
547 	if (ptr != NULL && strcmp(ptr, name) == 0) { /* found */
548 	    TRACE_GUI((stderr, "match!"));
549 	    break;
550 	}
551     }
552     TRACE_GUI((stderr, "returning: %p (0x%lx)", (void *)w, w ? XtWindow(w) : 0));
553     return w;
554 }
555 
556 /* Traverse widget hieararchy upwards until a widget matches
557    a name in the (NULL-terminated) list fmt, or Widget d (`default')
558    if none is found.
559 */
560 Widget
get_matching_parent(Widget w,Widget d,const char * fmt,...)561 get_matching_parent(Widget w, Widget d, const char *fmt, ...)
562 {
563     Widget parent = d;
564     const char *str = fmt;
565 
566     va_list argp;
567     va_start(argp, fmt);
568 
569     TRACE_GUI((stderr, "get_matching_parent of %p (0x%lx)", (void *)w, XtWindow(w)));
570     while (str != NULL) {
571 	Widget p;
572 	if ((p = matches_parent(w, str)) != NULL) {
573 	    parent = p;
574 	    break;
575 	}
576 	str = va_arg(argp, char *);
577     }
578 
579     va_end(argp);
580 
581     return parent;
582 }
583 
584 void
adjust_width_to_max(Widget w,...)585 adjust_width_to_max(Widget w, ...)
586 {
587     Dimension max = 0;
588     Widget w1 = w;
589 
590     va_list argp;
591     va_start(argp, w);
592 
593     /* get maximum width */
594     while (w1 != NULL) {
595 	Dimension curr;
596 	XtVaGetValues(w1, XtNwidth, &curr, NULL);
597 	if (curr > max)
598 	    max = curr;
599 	w1 = va_arg(argp, Widget);
600     }
601     va_end(argp);
602 
603     /* set maximum width */
604     va_start(argp, w);
605     w1 = w;
606     while (w1 != NULL) {
607 	XtVaSetValues(w1, XtNwidth, max, NULL);
608 	w1 = va_arg(argp, Widget);
609     }
610     va_end(argp);
611 }
612 
613 /* Return True if p is a parent of widget w, stopping (and returning FALSE)
614    if s is reached (which should be the toplevel window of the hierarchy).
615 */
616 Boolean
widget_is_parent(Widget w,Widget p,Widget s)617 widget_is_parent(Widget w, Widget p, Widget s)
618 {
619     Widget curr = XtParent(w);
620     while (curr != NULL && curr != s) {
621 	fprintf(stderr, "Comparing: %p - %p\n", (void *)curr, (void *)p);
622 	if (curr == p)
623 	    return True;
624 	curr = XtParent(curr);
625     }
626     return False;
627 }
628 
629 /*
630   Adjust height in a NULL-terminated list of widgets. For Motif,
631   this works better than the following adjust_vertically().
632 */
633 void
adjust_heights(Widget w,...)634 adjust_heights(Widget w, ...)
635 {
636     va_list ap;
637     Widget curr;
638     Dimension h, max;
639 
640     ASSERT(w != NULL, "Must have at least one element in va_list for adjust_heights!");
641 
642 #if MOTIF
643 #define HEIGHT XmNheight
644 #else
645 #define HEIGHT XtNheight
646 #endif
647 
648     /* initialize maximum */
649     XtVaGetValues(w, HEIGHT, &max, NULL);
650 
651     /* get maximum height */
652     va_start(ap, w);
653     while ((curr = va_arg(ap, Widget)) != NULL) {
654 	XtVaGetValues(curr, HEIGHT, &h, NULL);
655 	if (h > max)
656 	    max = h;
657     }
658     va_end(ap);
659 
660     /* set maximum height for all widgets */
661     XtVaSetValues(w, HEIGHT, max, NULL);
662 
663     va_start(ap, w);
664     while ((curr = va_arg(ap, Widget)) != NULL)
665 	XtVaSetValues(curr, HEIGHT, max, NULL);
666     va_end(ap);
667 
668 #undef HEIGHT
669 
670 }
671 
672 /*
673   Adjust height in a NULL-terminated list of widgets. For Motif,
674   this works better than the following adjust_vertically().
675 */
676 void
adjust_heights_min(Widget w,...)677 adjust_heights_min(Widget w, ...)
678 {
679     va_list ap;
680     Widget curr;
681     Dimension h, min;
682 
683     ASSERT(w != NULL, "Must have at least one element in va_list for adjust_heights!");
684 
685 #if MOTIF
686 #define HEIGHT XmNheight
687 #else
688 #define HEIGHT XtNheight
689 #endif
690 
691     /* initialize minimum */
692     XtVaGetValues(w, HEIGHT, &min, NULL);
693 
694     /* get minimum height */
695     va_start(ap, w);
696     while ((curr = va_arg(ap, Widget)) != NULL) {
697 	XtVaGetValues(curr, HEIGHT, &h, NULL);
698 	if (h < min)
699 	    min = h;
700     }
701     va_end(ap);
702 
703     /* set maximum height for all widgets */
704     XtVaSetValues(w, HEIGHT, min, NULL);
705 
706     va_start(ap, w);
707     while ((curr = va_arg(ap, Widget)) != NULL)
708 	XtVaSetValues(curr, HEIGHT, min, NULL);
709     va_end(ap);
710 
711 #undef HEIGHT
712 
713 }
714 
715 /* adjust two widgets vertically */
716 void
adjust_vertically(Widget w1,Widget w2,int default_dist)717 adjust_vertically(Widget w1, Widget w2, int default_dist)
718 {
719     Dimension h1, h2;
720 #if MOTIF
721     XtVaGetValues(w1, XmNheight, &h1, NULL);
722     XtVaGetValues(w2, XmNheight, &h2, NULL);
723     XtVaSetValues(w1, XmNtopOffset, default_dist + (h2 - h1) / 2, NULL);
724     /*     XtVaSetValues(w2, XmNtopOffset, default_dist + (h2 - h1) / 2, NULL); */
725 #else
726     XtVaGetValues(w1, XtNheight, &h1, NULL);
727     XtVaGetValues(w2, XtNheight, &h2, NULL);
728     XtVaSetValues(w1, XtNvertDistance, default_dist + (h2 - h1) / 2, NULL);
729     XtVaSetValues(w2, XtNvertDistance, default_dist + (h2 - h1) / 2, NULL);
730 #endif /* MOTIF */
731 }
732 
733 
734 /*
735  * This is a hack to block further processing of some events on widgets:
736  * Add as an event handler for all mouse/key events for a specific widget.
737  */
738 void
block_event_callback(Widget w,XtPointer client_data,XEvent * ev,Boolean * cont)739 block_event_callback(Widget w, XtPointer client_data,
740 		     XEvent *ev, Boolean *cont)
741 {
742     UNUSED(w);
743     UNUSED(client_data);
744     UNUSED(ev);
745 
746     /* Don't propagate this event further down... */
747     *cont = False;
748 
749     return;
750 }
751 
752 
753 /* Get a widget with `name' somewhere in the widget hierarchy below `parent'
754    (matching is done against `*name') and return it in `ret'.
755    If `report_error' is True, a warning message is popped up if the widget isn't found.
756 */
757 Boolean
get_widget_by_name(Widget * ret,Widget parent,const char * name,Boolean report_error)758 get_widget_by_name(Widget *ret, Widget parent, const char *name, Boolean report_error)
759 {
760     char buf[1024];
761     Widget test;
762 
763     /*      if (parent == 0 || !XtIsManaged(parent)) { */
764     /*  	fprintf(stderr, "Widget %p not managed!\n", parent); */
765     /*  	return False; */
766     /*      } */
767 
768     if (strlen(name) > 1023) {
769 	popup_message(globals.widgets.top_level,
770 		      MSG_ERR,
771 		      REPORT_XDVI_BUG_TEMPLATE,
772 		      "Widget name `%s' too long, couldn't get parent", name);
773 	return False;
774     }
775 
776     buf[0] = '*'; /* add wildcard to also match paths */
777     strcpy(buf + 1, name);
778 
779     if ((test = XtNameToWidget(parent, buf)) != NULL) {
780 	*ret = test;
781 	return True;
782     }
783     else {
784 	if (report_error)
785 	    popup_message(globals.widgets.top_level,
786 			  MSG_ERR,
787 			  REPORT_XDVI_BUG_TEMPLATE,
788 			  "XtNameToWidget failed for `%s', parent `%s'", name, XtName(parent));
789 	return False;
790     }
791 }
792 
793 void
unexpected_widget_in_callback(Widget w,const char * callback)794 unexpected_widget_in_callback(Widget w, const char *callback)
795 {
796     ASSERT(w != NULL, "Widget mustn't be NULL!");
797     popup_message(globals.widgets.top_level,
798 		  MSG_ERR,
799 		  REPORT_XDVI_BUG_TEMPLATE,
800 		  "Unexpected widget `%s' in callback `%s'",
801 		  XtName(w), callback);
802 }
803 
804 static XrmDatabase m_user_db = NULL;
805 
806 /*
807  * Merge the resource `name' with a value specified by `fmt' into
808  * the database `db' (if db == NULL, use m_user_db).
809  */
810 void
store_preference(XrmDatabase * db,const char * name,const char * fmt,...)811 store_preference(XrmDatabase *db, const char *name, const char *fmt, ...)
812 {
813     size_t offset = strlen("xdvi.");
814     size_t name_len = strlen(name);
815     char *name_buf = xmalloc(name_len + offset + 1);
816     char *buf = NULL;
817     XrmDatabase tmp_db = NULL;
818 
819     if (db == NULL)
820 	db = &m_user_db;
821 
822     XDVI_GET_STRING_ARGP(buf, fmt);
823 
824     memcpy(name_buf, "xdvi.", offset);
825     strcpy(name_buf + offset, name);
826 
827     TRACE_GUI((stderr, "storing resource: `%s: %s'", name_buf, buf));
828     XrmPutStringResource(&tmp_db, name_buf, buf);
829     XrmMergeDatabases(tmp_db, db);
830 
831     free(buf);
832     free(name_buf);
833 }
834 
835 void
merge_into_user_db(XrmDatabase db)836 merge_into_user_db(XrmDatabase db)
837 {
838     XrmMergeDatabases(db, &m_user_db);
839 }
840 
841 const char *const xdvirc_filename = ".xdvirc";
842 const char *const xdvirc_signature_line = "!!! ~/.xdvirc, used by xdvi(1) to save user preferences.\n";
843 const char *const xdvirc_header = ""
844 "!!!\n"
845 "!!! Do not edit this file, it will be overwritten by xdvi.\n"
846 "!!! This file contains resources that have been set via the\n"
847 "!!! menus/dialogs. The contents of this file will override\n"
848 "!!! the entries in your ~/.Xdefaults file (but not the command\n"
849 "!!! line arguments passed to xdvi). Remove this file\n"
850 "!!! if you want to get rid of all these customizations,\n"
851 "!!! or start xdvi with the `-q' option to ignore this file.\n"
852 "!!!\n";
853 
854 static char *
get_xdvirc_path(const char * basename)855 get_xdvirc_path(const char *basename)
856 {
857     const char *homedir;
858     size_t len;
859     char *str;
860 
861     if (basename == NULL)
862 	return NULL;
863 
864     homedir = getenv("HOME");
865     len = strlen(homedir) + strlen(basename) + 2; /* 1 more for '/' */
866     str = xmalloc(len);
867 
868     sprintf(str, "%s/%s", homedir, basename);
869     return str;
870 }
871 
872 static void
save_geometry(void)873 save_geometry(void)
874 {
875     int x_off, y_off;
876     Dimension w, h;
877     Window dummy;
878     /*     char *geom_str; */
879 
880     (void)XTranslateCoordinates(DISP, XtWindow(globals.widgets.top_level),
881 				RootWindowOfScreen(SCRN),
882 				0, 0,
883 				&x_off, &y_off,
884 				&dummy);
885     XtVaGetValues(globals.widgets.top_level,
886 		  XtNwidth, &w,
887 		  XtNheight, &h,
888 #ifdef MOTIF
889 		  /* 		  XmNgeometry, &geom_str, */
890 #endif
891 		  NULL);
892     TRACE_GUI((stderr, "geometry: %dx%d+%d+%d", w, h, x_off, y_off));
893 
894     store_preference(NULL, "windowSize", "%dx%d", w, h);
895 }
896 
897 /* Save user preferences to ~/.xdvirc. If `backup_only' is True,
898    it only writes to ~/.xdvirc.tmp and does not remove this temporary
899    file (this is used for synchronization between several xdvi instances).
900 */
901 Boolean
save_user_preferences(Boolean full_save)902 save_user_preferences(Boolean full_save)
903 {
904     char testbuf[1024];
905     char *xdvirc_name;
906     char *tmpname;
907     FILE *from_fp, *to_fp;
908     int fd;
909 
910     if (resource.no_init_file
911 	|| m_user_db == NULL) /* nothing to do */
912 	return True;
913 
914     if (resource.remember_windowsize)
915 	save_geometry();
916 
917     xdvirc_name = get_xdvirc_path(xdvirc_filename);
918 
919     if ((to_fp = fopen(xdvirc_name, "r")) != NULL) {
920 	TRACE_GUI((stderr, "~/.xdvirc exists, checking file contents ..."));
921 	if (fgets(testbuf, 1024, to_fp) != NULL &&
922 	    memcmp(testbuf, xdvirc_signature_line, strlen(xdvirc_signature_line)) != 0) {
923 	    popup_message(globals.widgets.top_level,
924 			  MSG_WARN,
925 			  "Xdvi uses the file ~/.xdvirc to save the preferences set via "
926 			  "the menu bar or the preferences dialog (in the Motif version only). "
927 			  "To avoid overwriting files created by the user, the first line of the "
928 			  "file is compared with a special signature line. If that signature line "
929 			  "is not found, the preferences won't be written. Your file doesn't seem "
930 			  "to contain that signature line. You should move the file to a safe location, "
931 			  "and then try to quit xdvi again.",
932 			  /* message */
933 			  "The file `%s' was apparently not written by xdvi(k). "
934 			  "Please move or delete this file first, then try to exit xdvi again. ",
935 			  xdvirc_name);
936 	    return False;
937 	}
938 	fclose(to_fp);
939     }
940 
941     /* don't use xdvi_temp_fd here, since XrmPutFileDatabase()
942        closes the FILE*, creating a temp race */
943     tmpname = xstrdup(xdvirc_name);
944     tmpname = xstrcat(tmpname, ".tmp");
945 
946     /* since XrmPutFileDatabase doesn't give a useful error message if it fails,
947        check that creating the file works beforehand. The file is created with 0600 permissions. */
948     if ((fd = try_open_mode(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
949 	XDVI_ERROR((stderr, "Could not save preferences!\nOpening %s for writing failed: %s", tmpname, strerror(errno)));
950 	return True;
951     }
952     close(fd);
953 
954     XrmPutFileDatabase(m_user_db, tmpname);
955 
956     if (full_save) {
957 	if ((from_fp = try_fopen(tmpname, "r")) == NULL) {
958 	    XDVI_ERROR((stderr, "Could not save preferences!\nOpening %s for reading failed: %s", tmpname, strerror(errno)));
959 	    return True;
960 	}
961 
962 	/* again, create the file with 600 permissions */
963 	if ((fd = try_open_mode(xdvirc_name, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
964 	    XDVI_ERROR((stderr, "Could not save preferences!\nOpening %s for writing failed: %s", xdvirc_name, strerror(errno)));
965 	    return True;
966 	}
967 
968 	if ((to_fp = fdopen(fd, "w")) == NULL) {
969 	    XDVI_ERROR((stderr, "Could not save preferences!\nfdopen for %s for writing failed: %s", xdvirc_name, strerror(errno)));
970 	    return True;
971 	}
972 
973 	if (fputs(xdvirc_signature_line, to_fp) == EOF
974 	    || fputs(xdvirc_header, to_fp) == EOF
975 	    || !copy_fp(from_fp, to_fp)) {
976 	    XDVI_ERROR((stderr, "Could not save preferences!\nError writing to %s: %s", xdvirc_name, strerror(errno)));
977 	}
978 
979 	fclose(from_fp);
980 	fclose(to_fp);
981     }
982 
983     free(xdvirc_name);
984 
985     if (full_save)
986 	unlink(tmpname);
987     free(tmpname);
988 
989     return True;
990 }
991 
992 /*
993  * Read the user preferences from xdvirc_filename and merge them into the
994  * current resource database *and* into m_user_db so that all of them are
995  * saved again when xdvi exits.
996  */
997 void
read_user_preferences(Widget toplevel,const char * filename)998 read_user_preferences(Widget toplevel, const char *filename)
999 {
1000     char *fullpath;
1001     XrmDatabase db;
1002 #if XtSpecificationRelease == 4
1003     XrmDatabase file_db;
1004 #endif
1005 
1006     fullpath = get_xdvirc_path(filename);
1007     TRACE_GUI((stderr, "Reading resources from `%s'", fullpath));
1008     db = XtDatabase(XtDisplay(toplevel));
1009 
1010 #if XtSpecificationRelease == 4
1011     file_db  = XrmGetFileDatabase(fullpath);
1012     XrmMergeDatabases(file_db, &db);
1013     XrmMergeDatabases(file_db, &m_user_db);
1014 #else /* Xt >= X11R5 */
1015     XrmCombineFileDatabase(fullpath, &db, True);
1016     XrmCombineFileDatabase(fullpath, &m_user_db, True);
1017 #endif
1018     free(fullpath);
1019 }
1020 
1021 
1022 /*
1023  *	Routines for running as source-special client.
1024  */
1025 
1026 static unsigned long xdvi_next_request = 0;
1027 static int xerrno;
1028 static int (*XdviOldErrorHandler)(Display *, XErrorEvent *);
1029 
1030 static int
XdviErrorHandler(Display * d,XErrorEvent * event)1031 XdviErrorHandler(Display *d, XErrorEvent *event)
1032 {
1033     if (event->serial != xdvi_next_request || event->error_code != BadWindow)
1034 	return XdviOldErrorHandler(d, event);
1035 
1036     xerrno = 1;
1037     return 0;
1038 }
1039 
1040 static int
XdviGetWindowProperty(Display * display,Window w,Atom property,long long_offset,long long_length,Bool delete,Atom req_type,Atom * actual_type_return,int * actual_format_return,unsigned long * nitems_return,unsigned long * bytes_after_return,unsigned char ** prop_return)1041 XdviGetWindowProperty(Display *display,
1042 		      Window w,
1043 		      Atom property,
1044 		      long long_offset,
1045 		      long long_length,
1046 		      Bool delete,
1047 		      Atom req_type,
1048 		      Atom *actual_type_return,
1049 		      int *actual_format_return,
1050 		      unsigned long *nitems_return,
1051 		      unsigned long *bytes_after_return,
1052 		      unsigned char **prop_return)
1053 {
1054     int retval;
1055 
1056     xdvi_next_request = NextRequest(display);
1057     xerrno = 0;
1058 
1059     retval = XGetWindowProperty(display, w, property, long_offset,
1060 				long_length, delete, req_type,
1061 				actual_type_return, actual_format_return,
1062 				nitems_return, bytes_after_return, prop_return);
1063 
1064     return (xerrno != 0 ? BadWindow : retval);
1065 }
1066 
1067 /* helper function to set a string property of type `prop' for window `win' */
1068 void
set_string_property(const char * str,Atom prop,Window win)1069 set_string_property(const char *str, Atom prop, Window win)
1070 {
1071     XChangeProperty(DISP, win, prop, prop, 8, PropModeReplace,
1072 		    (const unsigned char *)str, strlen(str));
1073     XFlush(DISP);	/* necessary to get the property set */
1074 }
1075 
1076 /*
1077  * Check for another running copy of xdvi.
1078  * If same_file is true, return the window ID of an instance that has
1079  * currently loaded the same file, or 0 if none exists.
1080  * If same_file is false, return the first valid xdvi window ID.
1081  */
1082 
1083 Window
get_xdvi_window_id(Boolean same_file,property_cbT callback)1084 get_xdvi_window_id(Boolean same_file, property_cbT callback)
1085 {
1086     long *window_list;
1087     size_t window_list_len;
1088     long *window_list_end;
1089     long *wp;
1090     long *p;
1091     Boolean need_rewrite = False;
1092     Window ret_window = 0;
1093 
1094     /*
1095      * Get window list.
1096      * Copy it over (we'll be calling property_get_data() again).
1097      */
1098     if ((window_list_len = property_get_window_list(&p)) == 0)
1099 	return 0;
1100 
1101     window_list = xmalloc(window_list_len * sizeof (long));
1102     memcpy(window_list, p, window_list_len * sizeof (long));
1103 
1104     XdviOldErrorHandler = XSetErrorHandler(XdviErrorHandler);
1105 
1106     /* Loop over list of windows.  */
1107 
1108     window_list_end = window_list + window_list_len;
1109     TRACE_CLIENT((stderr, "My property: `%s'", dvi_property));
1110 
1111     for (wp = window_list; wp < window_list_end; ++wp) {
1112 	char *buf_ret;
1113 	size_t len;
1114 
1115 	TRACE_CLIENT((stderr, "Checking window %08lx", *wp));
1116 
1117 	len = property_get_data((Window) *wp, atom_dvi_file(), &buf_ret,
1118 				XdviGetWindowProperty);
1119 
1120 	if (len == 0) {
1121 	    /* not getting back info for a window usually indicates
1122 	       that the application the window had belonged to had
1123 	       been killed with signal 9
1124 	    */
1125 	    TRACE_CLIENT((stderr,
1126 			"Window %08lx: doesn't exist any more, deleting", *wp));
1127 	    --window_list_len;
1128 	    --window_list_end;
1129 	    memmove(wp, wp + 1, (window_list_end - wp) * sizeof (long));
1130 	    --wp; /* new item is now at wp; don't skip it in next iteration */
1131 	    need_rewrite = True;
1132 	    continue;
1133 	}
1134 	else { /* window still alive */
1135 	    if (globals.debug & DBG_CLIENT) {
1136 		TRACE_CLIENT((stderr,
1137 			      "Window %08lx: property: `%s'", *wp, buf_ret));
1138 	    }
1139 
1140 	    /* invoke callback if given */
1141 	    if (callback != NULL) {
1142 		callback((Window) *wp);
1143 	    }
1144 
1145 	    if (!same_file && ret_window == 0) {
1146 		ret_window = *wp;
1147 		if (callback == 0) /* can return early */
1148 		    break;
1149 	    }
1150 	    else if (strcmp(buf_ret, dvi_property) == 0 && ret_window == 0) { /* match */
1151 		ret_window = *wp;
1152 		if (callback == 0) /* can return early */
1153 		    break;
1154 	    }
1155 	}
1156     }
1157 
1158     XSetErrorHandler(XdviOldErrorHandler);
1159 
1160     if (need_rewrite)
1161 	XChangeProperty(DISP, DefaultRootWindow(DISP),
1162 			atom_xdvi_windows(), atom_xdvi_windows(), 32,
1163 			PropModeReplace, (unsigned char *)window_list,
1164 			window_list_len);
1165 
1166     return ret_window;
1167 }
1168 
1169 Boolean
clip_region(int * x,int * y,int * w,int * h)1170 clip_region(int *x, int *y, int *w, int *h)
1171 {
1172 #if 0
1173     fprintf(stderr, "globals.win_expose.min_x: %d, globals.win_expose.max_x: %d, "
1174 	    "globals.win_expose.min_y: %d, globals.win_expose.max_y: %d\n",
1175 	    globals.win_expose.min_x, globals.win_expose.max_x,
1176 	    globals.win_expose.min_y, globals.win_expose.max_y);
1177 #endif
1178     /* check for <= so that we also return false if *w or *h == 0 */
1179     if (*x + *w <= globals.win_expose.min_x
1180 	|| *x >= globals.win_expose.max_x
1181 	|| *y + *h <= globals.win_expose.min_y
1182 	|| *y >= globals.win_expose.max_y
1183 	/* extra protection agains *w or *h == 0; don't know if this can actually happen ... */
1184 	|| globals.win_expose.max_y == globals.win_expose.min_y
1185 	|| globals.win_expose.max_x == globals.win_expose.min_x) {
1186 	return False;
1187     }
1188     if (*x < globals.win_expose.min_x) {
1189 	*w -= globals.win_expose.min_x - *x;
1190 	*x = globals.win_expose.min_x;
1191     }
1192     if (*x + *w > globals.win_expose.max_x) {
1193 	*w = globals.win_expose.max_x - *x;
1194     }
1195     if (*y < globals.win_expose.min_y) {
1196 	*h -= globals.win_expose.min_y - *y;
1197 	*y = globals.win_expose.min_y;
1198     }
1199     if (*y + *h > globals.win_expose.max_y) {
1200 	*h = globals.win_expose.max_y - *y;
1201     }
1202     return True;
1203 }
1204 
1205 Boolean
clip_region_to_rect(XRectangle * rect)1206 clip_region_to_rect(XRectangle *rect)
1207 {
1208     int x = rect->x;
1209     int y = rect->y;
1210     int w = rect->width;
1211     int h = rect->height;
1212     Boolean ret = clip_region(&x, &y, &w, &h);
1213     if (ret) {
1214 	rect->x = x;
1215 	rect->y = y;
1216 	rect->width = w;
1217 	rect->height = h;
1218     }
1219     return ret;
1220 }
1221 
window_is_mapped(Window w,Display * dpy)1222 Boolean window_is_mapped(Window w, Display *dpy)
1223 {
1224     XWindowAttributes xwa;
1225     return XGetWindowAttributes(dpy, w, &xwa) && xwa.map_state == IsViewable;
1226 }
1227 
cast_int_to_XtPointer(int i)1228 XtPointer cast_int_to_XtPointer(int i)
1229 {
1230     return (XtPointer)(ptrdiff_t)i;
1231 }
1232