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