events.c (43930B)
1 /*========================================================================*\ 2 3 Copyright (c) 1990-2004 Paul Vojta 4 5 Permission is hereby granted, free of charge, to any person obtaining a copy 6 of this software and associated documentation files (the "Software"), to 7 deal in the Software without restriction, including without limitation the 8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 sell copies of the Software, and to permit persons to whom the Software is 10 furnished to do so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in 13 all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF OR CONTRIBUTOR TO 19 THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 IN THE SOFTWARE. 23 24 NOTE: 25 xdvi is based on prior work, as noted in the modification history 26 in xdvi.c. 27 28 \*========================================================================*/ 29 30 #include <ctype.h> /* tolower() */ 31 #include <err.h> 32 #include <limits.h> /* abs() */ 33 #include <math.h> /* pow() */ 34 #include <stdlib.h> /* free(), atoi(), atof(), exit(), abs() */ 35 36 #include <X11/IntrinsicI.h> /* _XtComputeLateBindings */ 37 #include <X11/XKBlib.h> 38 #define XBell(dpy, percent) XkbBell(dpy, mane.win, percent, (Atom) None) 39 40 #include "dvi-init.h" /* init_page(), full_reset_colors(), reset_colors() */ 41 #include "dvi-draw.h" /* prescan(), draw_page(), draw_links() */ 42 #include "events.h" 43 #include "http.h" /* http_get() */ 44 #include "popups.h" /* WARN, Act_print() */ 45 #include "special.h" /* page_colors, scanned_page, ... */ 46 #include "util.h" /* xmalloc() */ 47 #include "xdvi.h" 48 49 50 #define MAGBORD 1 /* border size for magnifier */ 51 52 /* 53 * Command line flags. 54 */ 55 56 extern struct _resource resource; 57 58 #define clip_w mane.width 59 #define clip_h mane.height 60 static Position main_x, main_y; 61 static Position mag_x, mag_y, new_mag_x, new_mag_y; 62 static int mag_conv_x, mag_conv_y; 63 static Boolean dragcurs = False; 64 65 typedef void (*home_proc)(Boolean); 66 typedef void (*mouse_proc)(XEvent *); 67 68 static void can_exposures(struct WindowRec *); 69 void null_mouse(XEvent *); 70 71 mouse_proc mouse_motion = null_mouse; 72 mouse_proc mouse_release = null_mouse; 73 74 75 /* 76 * The cursor shape in the magnifying glass is determined by which 77 * window received the button press event. Under XAW, that is a parent 78 * of mane.win, so we have to define the cursor of that window instead 79 * of mane.win in that case. 80 */ 81 82 #define CURSORWIN XtWindow(form_widget) 83 84 #define ACTION_DECL(name) \ 85 void name(Widget, XEvent *, String *, Cardinal *) 86 87 #define ACTION(name) \ 88 void name(Widget w, XEvent *event, \ 89 String *params, Cardinal *num_params) 90 91 void 92 null_mouse(XEvent *event) 93 { 94 } 95 96 static ACTION_DECL(Act_digit); 97 static ACTION_DECL(Act_minus); 98 static ACTION_DECL(Act_quit); 99 static ACTION_DECL(Act_goto_page); 100 static ACTION_DECL(Act_forward_page); 101 static ACTION_DECL(Act_back_page); 102 static ACTION_DECL(Act_declare_page_number); 103 static ACTION_DECL(Act_home); 104 static ACTION_DECL(Act_center); 105 static ACTION_DECL(Act_left); 106 static ACTION_DECL(Act_right); 107 static ACTION_DECL(Act_up); 108 static ACTION_DECL(Act_down); 109 static ACTION_DECL(Act_up_or_previous); 110 static ACTION_DECL(Act_down_or_next); 111 static ACTION_DECL(Act_show_display_attributes); 112 static ACTION_DECL(Act_set_shrink_factor); 113 static ACTION_DECL(Act_shrink_to_dpi); 114 static ACTION_DECL(Act_set_density); 115 static ACTION_DECL(Act_set_color); 116 static ACTION_DECL(Act_discard_number); 117 118 static ACTION_DECL(Act_magnifier); 119 static ACTION_DECL(Act_drag); 120 static ACTION_DECL(Act_wheel); 121 static ACTION_DECL(Act_hwheel); 122 static ACTION_DECL(Act_wheel_actions); 123 static ACTION_DECL(Act_motion); 124 static ACTION_DECL(Act_release); 125 static ACTION_DECL(Act_addr_go); 126 static ACTION_DECL(Act_show_links); 127 128 129 XtActionsRec Actions[] = { 130 {"digit", Act_digit}, 131 {"minus", Act_minus}, 132 {"quit", Act_quit}, 133 {"goto-page", Act_goto_page}, 134 {"forward-page", Act_forward_page}, 135 {"back-page", Act_back_page}, 136 {"declare-page-number", Act_declare_page_number}, 137 {"home", Act_home}, 138 {"center", Act_center}, 139 {"left", Act_left}, 140 {"right", Act_right}, 141 {"up", Act_up}, 142 {"down", Act_down}, 143 {"up-or-previous", Act_up_or_previous}, 144 {"down-or-next", Act_down_or_next}, 145 {"show-display-attributes", Act_show_display_attributes}, 146 {"set-shrink-factor", Act_set_shrink_factor}, 147 {"shrink-to-dpi", Act_shrink_to_dpi}, 148 {"set-density", Act_set_density}, 149 {"print", Act_print}, 150 {"set-color", Act_set_color}, 151 {"discard-number", Act_discard_number}, 152 {"magnifier", Act_magnifier}, 153 {"drag", Act_drag}, 154 {"wheel", Act_wheel}, 155 {"hwheel", Act_hwheel}, 156 {"wheel-actions", Act_wheel_actions}, 157 {"motion", Act_motion}, 158 {"release", Act_release}, 159 {"addr-go", Act_addr_go}, 160 {"show-links", Act_show_links}, 161 }; 162 163 Cardinal num_actions = XtNumber(Actions); 164 165 166 Bool 167 compile_action(str, app) 168 const char *str; 169 struct xdvi_action **app; 170 { 171 const char *p, *p1, *p2; 172 XtActionsRec *actp; 173 struct xdvi_action *ap; 174 175 for (;;) { 176 while (*str == ' ' || *str == '\t') ++str; 177 178 if (*str == '\0' || *str == '\n') 179 break; 180 181 p = str; 182 while (((*p | ('a' ^ 'A')) >= 'a' && (*p | ('a' ^ 'A')) <= 'z') 183 || (*p >= '0' && *p <= '9') || *p == '-' || *p == '_') 184 ++p; 185 186 for (actp = Actions;; ++actp) { 187 if (actp >= Actions + XtNumber(Actions)) { 188 warnx("cannot compile action '%.*s'", (int) (p - str), str); 189 *app = NULL; 190 return False; 191 } 192 if (memcmp(str, actp->string, p - str) == 0 193 && actp->string[p - str] == '\0') 194 break; 195 } 196 197 while (*p == ' ' || *p == '\t') ++p; 198 if (*p != '(') { 199 while (*p != '\0' && *p != '\n') ++p; 200 warnx("syntax error in action '%.*s'", (int) (p - str), str); 201 *app = NULL; 202 return False; 203 } 204 ++p; 205 while (*p == ' ' || *p == '\t') ++p; 206 for (p1 = p;; ++p1) { 207 if (*p1 == '\0' || *p1 == '\n') { 208 warnx("syntax error in action '%.*s'", (int) (p1 - str), str); 209 *app = NULL; 210 return False; 211 } 212 if (*p1 == ')') break; 213 } 214 215 ap = xmalloc(sizeof *ap); 216 ap->proc = actp->proc; 217 for (p2 = p1;; --p2) 218 if (p2 <= p) { /* if no args */ 219 ap->num_params = 0; 220 ap->param = NULL; 221 break; 222 } 223 else if (p2[-1] != ' ' && p2[-1] != '\t') { 224 char *arg; 225 226 arg = xmalloc(p2 - p + 1); 227 bcopy(p, arg, p2 - p); 228 arg[p2 - p] = '\0'; 229 ap->num_params = 1; 230 ap->param = arg; 231 break; 232 } 233 234 *app = ap; 235 app = &ap->next; 236 237 str = p1 + 1; 238 } 239 240 *app = NULL; 241 return True; 242 } 243 244 245 246 static Widget x_bar, y_bar; /* horizontal and vertical scroll bars */ 247 248 static Arg resize_args[] = { 249 {XtNwidth, (XtArgVal) 0}, 250 {XtNheight, (XtArgVal) 0}, 251 }; 252 253 #define XdviResizeWidget(widget, w, h) \ 254 (width_arg.value = (XtArgVal) (w), \ 255 resize_args[1].value = (XtArgVal) (h), \ 256 XtSetValues(widget, resize_args, XtNumber(resize_args)) ) 257 258 #define width_arg (resize_args[0]) 259 #define height_arg (resize_args[1]) 260 261 262 263 /* 264 * Mechanism to keep track of the magnifier window. The problems are, 265 * (a) if the button is released while the window is being drawn, this 266 * could cause an X error if we continue drawing in it after it is 267 * destroyed, and 268 * (b) creating and destroying the window too quickly confuses the window 269 * manager, which is avoided by waiting for an expose event before 270 * destroying it. 271 */ 272 static short alt_stat; /* 1 = wait for expose, */ 273 /* -1 = destroy upon expose */ 274 275 /* 276 * Data for buffered events. 277 */ 278 279 static void 280 mask_shifts(mask, pshift1, pshift2) 281 Pixel mask; 282 int *pshift1, *pshift2; 283 { 284 int k, l; 285 286 for (k = 0; (mask & 1) == 0; ++k) 287 mask >>= 1; 288 for (l = 0; (mask & 1) == 1; ++l) 289 mask >>= 1; 290 *pshift1 = sizeof(short) * 8 - l; 291 *pshift2 = k; 292 } 293 294 295 /* 296 * Warn about overstrike characters. 297 */ 298 299 static void 300 warn_overstrike() 301 { 302 static Boolean warned_already = False; 303 304 if (!warned_already) { 305 warned_already = True; 306 WARN(XmDIALOG_WARNING, "Overstrike characters may be incorrect"); 307 } 308 } 309 310 /* 311 * Allocate a color and add it to our list of pixels to be returned 312 * upon opening a new document. 313 */ 314 315 #define SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2)) 316 317 static int shift1_r, shift1_g, shift1_b; 318 static int shift2_r, shift2_g, shift2_b; 319 static Boolean shifts_good = False; 320 321 static Pixel 322 alloc_color(const struct rgb *colorp, Pixel fallback_pixel) 323 { 324 Visual *visual = DefaultVisualOfScreen(SCRN); 325 326 if (!shifts_good) { 327 mask_shifts(visual->red_mask, &shift1_r, &shift2_r); 328 mask_shifts(visual->green_mask, &shift1_g, &shift2_g); 329 mask_shifts(visual->blue_mask, &shift1_b, &shift2_b); 330 shifts_good = True; 331 } 332 return SHIFTIFY(colorp->r, shift1_r, shift2_r) | 333 SHIFTIFY(colorp->g, shift1_g, shift2_g) | 334 SHIFTIFY(colorp->b, shift1_b, shift2_b); 335 } 336 337 #undef SHIFTIFY 338 339 340 /* 341 * Switch colors of GCs, etc. Called when we're about to use a GC. 342 */ 343 344 #define SetGC(gc, fcn, fg, bg) \ 345 { \ 346 values.function = fcn; \ 347 values.foreground = fg; \ 348 values.background = bg; \ 349 if (gc != NULL) \ 350 XChangeGC(DISP, gc, \ 351 GCFunction | GCForeground | GCBackground, &values); \ 352 else \ 353 gc = XCreateGC(DISP, XtWindow(top_level), \ 354 GCFunction | GCForeground | GCBackground, &values); \ 355 } 356 357 void 358 do_color_change() 359 { 360 static int shrink_allocated_for = 0; 361 static GC foreGC2_bak = NULL; 362 XGCValues values; 363 Pixel set_bits; 364 Pixel clr_bits; 365 Visual *visual; 366 367 /* 368 * use_grey 369 */ 370 if (!fg_current->pixel_good) { 371 fg_current->pixel = alloc_color(&fg_current->color, 372 fore_color_data.pixel); 373 fg_current->pixel_good = True; 374 } 375 376 set_bits = fg_current->pixel & ~bg_current->pixel; 377 clr_bits = bg_current->pixel & ~fg_current->pixel; 378 379 visual = DefaultVisualOfScreen(SCRN); 380 381 if (set_bits & visual->red_mask) 382 set_bits |= visual->red_mask; 383 if (clr_bits & visual->red_mask) 384 clr_bits |= visual->red_mask; 385 if (set_bits & visual->green_mask) 386 set_bits |= visual->green_mask; 387 if (clr_bits & visual->green_mask) 388 clr_bits |= visual->green_mask; 389 if (set_bits & visual->blue_mask) 390 set_bits |= visual->blue_mask; 391 if (clr_bits & visual->blue_mask) 392 clr_bits |= visual->blue_mask; 393 394 /* 395 * Make the GCs 396 */ 397 398 SetGC(ruleGC, GXcopy, fg_current->pixel, bg_current->pixel); 399 foreGC2 = NULL; 400 401 if (resource.copy || (set_bits && clr_bits && !resource.thorough)) { 402 if (!resource.copy) 403 warn_overstrike(); 404 SetGC(foreGC, GXcopy, fg_current->pixel, bg_current->pixel); 405 } 406 else { 407 if (set_bits) { 408 SetGC(foreGC, GXor, fg_current->pixel & set_bits, 0); 409 if (clr_bits) { 410 SetGC(foreGC2_bak, GXandInverted, 411 ~fg_current->pixel & clr_bits, 0); 412 foreGC2 = foreGC2_bak; 413 } 414 } 415 else 416 SetGC(foreGC, GXandInverted, 417 ~fg_current->pixel & clr_bits, 0); 418 } 419 420 if (debug & DBG_DVI) 421 printf("do_color_change: fg = %lx, bg = %lx, with%s foreGC2\n", 422 fg_current->pixel, bg_current->pixel, 423 foreGC2 == NULL ? "out" : ""); 424 425 if (mane.shrinkfactor <= 1) { 426 fg_active = fg_current; 427 return; 428 } 429 430 /* 431 * mane.shrinkfactor > 1 432 */ 433 int i; 434 unsigned int sf_squared; 435 436 sf_squared = mane.shrinkfactor * mane.shrinkfactor; 437 438 if (shrink_allocated_for < mane.shrinkfactor) { 439 if (pixeltbl != NULL) { 440 free((char *) pixeltbl); 441 if (pixeltbl_t != NULL) { 442 free((char *) pixeltbl_t); 443 pixeltbl_t = NULL; 444 } 445 } 446 pixeltbl = xmalloc((sf_squared + 1) * sizeof(Pixel)); 447 shrink_allocated_for = mane.shrinkfactor; 448 } 449 if (foreGC2 != NULL && pixeltbl_t == NULL) 450 /* Can't use sf_squared (or mane.shrinkfactor) here */ 451 pixeltbl_t = xmalloc((shrink_allocated_for 452 * shrink_allocated_for + 1) * sizeof(Pixel)); 453 454 /* 455 * Compute pixel values directly. 456 */ 457 for (i = 0; i <= sf_squared; ++i) { 458 double frac = resource._gamma > 0 459 ? pow((double) i / sf_squared, 1 / resource._gamma) 460 : 1 - pow((double) (sf_squared - i) / sf_squared, 461 -resource._gamma); 462 unsigned int red, green, blue; 463 Pixel pixel; 464 465 red = frac * ((double) fg_current->color.r 466 - bg_current->color.r) 467 + bg_current->color.r; 468 green = frac * ((double) fg_current->color.g 469 - bg_current->color.g) 470 + bg_current->color.g; 471 blue = frac * ((double) fg_current->color.b 472 - bg_current->color.b) 473 + bg_current->color.b; 474 475 #define SHIFTIFY(x, shift1, shift2) ((((Pixel)(x)) >> (shift1)) << (shift2)) 476 pixel = SHIFTIFY(red, shift1_r, shift2_r) | 477 SHIFTIFY(green, shift1_g, shift2_g) | 478 SHIFTIFY(blue, shift1_b, shift2_b); 479 #undef SHIFTIFY 480 481 if (foreGC2 != NULL) { /* if thorough */ 482 pixeltbl[i] = pixel & ~bg_current->pixel; 483 pixeltbl_t[i] = ~pixel & bg_current->pixel; 484 } 485 else if (resource.copy || (set_bits && clr_bits)) 486 pixeltbl[i] = pixel; 487 else 488 pixeltbl[i] = set_bits ? pixel & set_bits 489 : ~pixel & clr_bits; 490 } 491 492 fg_active = fg_current; 493 } 494 495 #undef SetGC 496 497 498 /* 499 * Event-handling routines 500 */ 501 502 static void 503 expose(windowrec, x, y, w, h) 504 struct WindowRec *windowrec; 505 int x, y; 506 unsigned int w, h; 507 { 508 if (windowrec->min_x > x) windowrec->min_x = x; 509 if (windowrec->max_x < x + w) 510 windowrec->max_x = x + w; 511 if (windowrec->min_y > y) windowrec->min_y = y; 512 if (windowrec->max_y < y + h) 513 windowrec->max_y = y + h; 514 ev_flags |= EV_EXPOSE; 515 } 516 517 static void 518 clearexpose(windowrec, x, y, w, h) 519 struct WindowRec *windowrec; 520 int x, y; 521 unsigned int w, h; 522 { 523 XClearArea(DISP, windowrec->win, x, y, w, h, False); 524 expose(windowrec, x, y, w, h); 525 } 526 527 static void 528 scrollwindow(windowrec, x0, y0) 529 struct WindowRec *windowrec; 530 int x0, y0; 531 { 532 int x, y; 533 int x2 = 0, y2 = 0; 534 int ww, hh; 535 536 x = x0 - windowrec->base_x; 537 y = y0 - windowrec->base_y; 538 ww = windowrec->width - x; 539 hh = windowrec->height - y; 540 windowrec->base_x = x0; 541 windowrec->base_y = y0; 542 if (currwin.win == windowrec->win) { 543 currwin.base_x = x0; 544 currwin.base_y = y0; 545 } 546 windowrec->min_x -= x; 547 if (windowrec->min_x < 0) windowrec->min_x = 0; 548 windowrec->max_x -= x; 549 if (windowrec->max_x > windowrec->width) 550 windowrec->max_x = windowrec->width; 551 windowrec->min_y -= y; 552 if (windowrec->min_y < 0) windowrec->min_y = 0; 553 windowrec->max_y -= y; 554 if (windowrec->max_y > windowrec->height) 555 windowrec->max_y = windowrec->height; 556 if (x < 0) { 557 x2 = -x; 558 x = 0; 559 ww = windowrec->width - x2; 560 } 561 if (y < 0) { 562 y2 = -y; 563 y = 0; 564 hh = windowrec->height - y2; 565 } 566 if (ww <= 0 || hh <= 0) { 567 XClearWindow(DISP, windowrec->win); 568 windowrec->min_x = windowrec->min_y = 0; 569 windowrec->max_x = windowrec->width; 570 windowrec->max_y = windowrec->height; 571 } 572 else { 573 XCopyArea(DISP, windowrec->win, windowrec->win, copyGC, 574 x, y, (unsigned int) ww, (unsigned int) hh, x2, y2); 575 if (x > 0) 576 clearexpose(windowrec, ww, 0, 577 (unsigned int) x, windowrec->height); 578 if (x2 > 0) 579 clearexpose(windowrec, 0, 0, 580 (unsigned int) x2, windowrec->height); 581 if (y > 0) 582 clearexpose(windowrec, 0, hh, 583 windowrec->width, (unsigned int) y); 584 if (y2 > 0) 585 clearexpose(windowrec, 0, 0, 586 windowrec->width, (unsigned int) y2); 587 } 588 } 589 590 591 /* 592 * routines for X11 toolkit 593 */ 594 595 Dimension window_w, window_h; 596 597 static Arg arg_wh[] = { 598 {XtNwidth, (XtArgVal) &window_w}, 599 {XtNheight, (XtArgVal) &window_h}, 600 }; 601 602 static Position window_x, window_y; 603 static Arg arg_xy[] = { 604 {XtNx, (XtArgVal) &window_x}, 605 {XtNy, (XtArgVal) &window_y}, 606 }; 607 608 #define get_xy() XtGetValues(draw_widget, arg_xy, XtNumber(arg_xy)) 609 610 #define mane_base_x 0 611 #define mane_base_y 0 612 613 614 615 616 static void 617 home(Boolean scrl) 618 { 619 if (!scrl) XUnmapWindow(DISP, mane.win); 620 get_xy(); 621 if (x_bar != NULL) { 622 XtCallCallbacks(x_bar, XtNscrollProc, 623 (XtPointer) (ptrdiff_t) window_x); 624 } 625 if (y_bar != NULL) { 626 XtCallCallbacks(y_bar, XtNscrollProc, 627 (XtPointer) (ptrdiff_t) window_y); 628 } 629 if (!scrl) { 630 XMapWindow(DISP, mane.win); 631 /* Wait for the server to catch up---this eliminates flicker. */ 632 XSync(DISP, False); 633 } 634 } 635 636 /* 637 * Same as home(), except move to the bottom of the page. 638 */ 639 640 static void 641 home_bottom(Boolean scrl) 642 { 643 XUnmapWindow(DISP, mane.win); 644 get_xy(); 645 if (x_bar != NULL) { 646 XtCallCallbacks(x_bar, XtNscrollProc, 647 (XtPointer) (ptrdiff_t) (window_x)); 648 } 649 if (y_bar != NULL) 650 XtCallCallbacks(y_bar, XtNscrollProc, 651 (XtPointer) (ptrdiff_t) (window_y + (page_h - clip_h))); 652 XMapWindow(DISP, mane.win); 653 /* Wait for the server to catch up---this eliminates flicker. */ 654 XSync(DISP, False); 655 } 656 657 658 static void 659 handle_destroy_bar(w, client_data, call_data) 660 Widget w; 661 XtPointer client_data; 662 XtPointer call_data; 663 { 664 * (Widget *) client_data = NULL; 665 } 666 667 668 static Boolean resized = False; 669 670 static void 671 get_geom() 672 { 673 static Dimension new_clip_w, new_clip_h; 674 static Arg arg_wh_clip[] = { 675 {XtNwidth, (XtArgVal) &new_clip_w}, 676 {XtNheight, (XtArgVal) &new_clip_h}, 677 }; 678 int old_clip_w; 679 680 XtGetValues(vport_widget, arg_wh, XtNumber(arg_wh)); 681 XtGetValues(clip_widget, arg_wh_clip, XtNumber(arg_wh_clip)); 682 /* Note: widgets may be destroyed but not forgotten */ 683 if (x_bar == NULL) { 684 x_bar = XtNameToWidget(vport_widget, "horizontal"); 685 if (x_bar != NULL) 686 XtAddCallback(x_bar, XtNdestroyCallback, handle_destroy_bar, 687 (XtPointer) &x_bar); 688 } 689 if (y_bar == NULL) { 690 y_bar = XtNameToWidget(vport_widget, "vertical"); 691 if (y_bar != NULL) 692 XtAddCallback(y_bar, XtNdestroyCallback, handle_destroy_bar, 693 (XtPointer) &y_bar); 694 } 695 old_clip_w = clip_w; 696 /* we need to do this because */ 697 /* sizeof(Dimension) != sizeof(int) */ 698 clip_w = new_clip_w; 699 clip_h = new_clip_h; 700 if (old_clip_w == 0) ev_flags |= EV_NEWPAGE; 701 resized = False; 702 } 703 704 /* 705 * callback routines 706 */ 707 708 void 709 handle_resize(widget, junk, event, cont) 710 Widget widget; 711 XtPointer junk; 712 XEvent *event; 713 Boolean *cont; /* unused */ 714 { 715 resized = True; 716 } 717 718 719 static void 720 reconfig() 721 { 722 XdviResizeWidget(draw_widget, page_w, page_h); 723 get_geom(); 724 } 725 726 727 728 /* 729 * goto_page is the only place where current_page should be set, 730 * other than when reading a new dvi file. 731 */ 732 733 static home_proc home_action = home; 734 735 static void 736 goto_page(n, proc) 737 int n; 738 home_proc proc; 739 { 740 current_page = n; 741 home_action = proc; 742 } 743 744 745 746 747 748 /* ||| 749 * Currently the event handler does not coordinate XCopyArea requests 750 * with GraphicsExpose events. This can lead to problems if the window 751 * is partially obscured and one, for example, drags a scrollbar. 752 */ 753 754 /* 755 * Actions for the translation mechanism. 756 */ 757 758 static Boolean have_arg = False; 759 static int number = 0; 760 static int sign = 1; 761 762 763 #define GET_ARG4(arg, param, param2, default) \ 764 if (*num_params > 0) \ 765 {param;} \ 766 else { \ 767 if (have_arg) { \ 768 arg = (param2); \ 769 have_arg = False; \ 770 number = 0; \ 771 sign = 1; \ 772 } \ 773 else \ 774 {default} \ 775 } 776 777 #define GET_ARG(arg, default) \ 778 GET_ARG4(arg, arg = atoi(*params), sign * number, \ 779 arg = (default);) 780 781 #define GET_ARG6(arg, param, c, param_c, param2, default)\ 782 if (*num_params > 0) { \ 783 if (**params == (c)) \ 784 {param_c;} \ 785 else \ 786 {param;} \ 787 } \ 788 else { \ 789 if (have_arg) { \ 790 arg = (param2); \ 791 have_arg = False; \ 792 number = 0; \ 793 sign = 1; \ 794 } \ 795 else \ 796 {default;} \ 797 } 798 799 #define TOGGLE(arg) \ 800 if (*num_params > 0) { \ 801 if (**params != 't' && (atoi(*params) != 0) == arg) \ 802 return; \ 803 } \ 804 else { \ 805 if (have_arg) { \ 806 int tmparg = number; \ 807 \ 808 have_arg = False; \ 809 number = 0; \ 810 sign = 1; \ 811 \ 812 if ((tmparg != 0) == arg) \ 813 return; \ 814 } \ 815 } 816 817 818 819 static 820 ACTION(Act_digit) 821 { 822 unsigned int digit; 823 824 if (*num_params != 1 || (digit = **params - '0') > 9) { 825 XBell(DISP, 0); 826 return; 827 } 828 have_arg = True; 829 number = number * 10 + digit; 830 } 831 832 static 833 ACTION(Act_minus) 834 { 835 have_arg = True; 836 sign = -sign; 837 } 838 839 static 840 ACTION(Act_quit) 841 { 842 exit(1); 843 } 844 845 static 846 ACTION(Act_goto_page) 847 { 848 int arg; 849 850 GET_ARG6(arg, arg = atoi(*params) - pageno_correct, 851 'e', arg = total_pages - 1, 852 sign * number - pageno_correct, arg = total_pages - 1); 853 854 if (arg < 0 || arg >= total_pages) { 855 XBell(DISP, 0); 856 return; 857 } 858 859 if (current_page != arg) 860 goto_page(arg, home); 861 862 ev_flags |= EV_NEWPAGE; 863 XFlush(DISP); 864 return; /* Don't use longjmp here: it might be called from 865 * within the toolkit, and we don't want to longjmp out 866 * of Xt routines. */ 867 } 868 869 static 870 ACTION(Act_forward_page) 871 { 872 int arg; 873 874 GET_ARG(arg, 1); 875 arg += current_page; 876 877 if (arg >= 0 && arg < total_pages) { 878 if (current_page != arg) 879 goto_page(arg, home); 880 /* Control-L (and changing the page) clears this box */ 881 ev_flags |= EV_NEWPAGE; 882 XFlush(DISP); 883 return; /* Don't use longjmp here: it might be called from 884 * within the toolkit, and we don't want to longjmp out 885 * of Xt routines. */ 886 } 887 XBell(DISP, 0); 888 } 889 890 static 891 ACTION(Act_back_page) 892 { 893 int arg; 894 895 GET_ARG(arg, 1); 896 arg = current_page - arg; 897 898 if (arg >= 0 && arg < total_pages) { 899 if (current_page != arg) 900 goto_page(arg, home); 901 ev_flags |= EV_NEWPAGE; 902 XFlush(DISP); 903 return; /* Don't use longjmp here: it might be called from 904 * within the toolkit, and we don't want to longjmp out 905 * of Xt routines. */ 906 } 907 XBell(DISP, 0); 908 } 909 910 static 911 ACTION(Act_declare_page_number) 912 { 913 int arg; 914 915 GET_ARG(arg, 0); 916 pageno_correct = arg - current_page; 917 } 918 919 static 920 ACTION(Act_home) 921 { 922 home(True); 923 } 924 925 static 926 ACTION(Act_center) 927 { 928 int x, y; 929 930 x = event->xkey.x - clip_w / 2; 931 y = event->xkey.y - clip_h / 2; 932 /* The clip widget gives a more exact value. */ 933 if (x_bar != NULL) 934 XtCallCallbacks(x_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) x); 935 if (y_bar != NULL) 936 XtCallCallbacks(y_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) y); 937 XWarpPointer(XtDisplay(w), None, None, 0, 0, 0, 0, -x, -y); 938 } 939 940 static 941 ACTION(Act_left) 942 { 943 if (x_bar != NULL) 944 XtCallCallbacks(x_bar, XtNscrollProc, 945 (XtPointer) (*num_params == 0 ? (-2 * (ptrdiff_t) clip_w / 3) 946 : (ptrdiff_t) (-atof(*params) * clip_w))); 947 else 948 XBell(XtDisplay(w), 0); 949 } 950 951 static 952 ACTION(Act_right) 953 { 954 if (x_bar != NULL) 955 XtCallCallbacks(x_bar, XtNscrollProc, 956 (XtPointer) (*num_params == 0 ? (2 * (ptrdiff_t) clip_w / 3) 957 : (ptrdiff_t) (atof(*params) * clip_w))); 958 else 959 XBell(XtDisplay(w), 0); 960 } 961 962 static 963 ACTION(Act_up) 964 { 965 if (y_bar != NULL) 966 XtCallCallbacks(y_bar, XtNscrollProc, 967 (XtPointer) (*num_params == 0 ? (-2 * (ptrdiff_t) clip_h / 3) 968 : (ptrdiff_t) (-atof(*params) * clip_h))); 969 else 970 XBell(XtDisplay(w), 0); 971 } 972 973 static 974 ACTION(Act_down) 975 { 976 if (y_bar != NULL) 977 XtCallCallbacks(y_bar, XtNscrollProc, 978 (XtPointer) (*num_params == 0 ? (2 * (ptrdiff_t) clip_h / 3) 979 : (ptrdiff_t) (atof(*params) * clip_h))); 980 else 981 XBell(XtDisplay(w), 0); 982 } 983 984 static 985 ACTION(Act_down_or_next) 986 { 987 if (y_bar != NULL) { 988 get_xy(); 989 if (window_y > (int) clip_h - (int) page_h) { 990 XtCallCallbacks(y_bar, XtNscrollProc, 991 (XtPointer) (*num_params == 0 992 ? (2 * (ptrdiff_t) clip_h / 3) 993 : (ptrdiff_t) (atof(*params) * clip_h))); 994 return; 995 } 996 } 997 998 if (current_page < total_pages - 1) { 999 goto_page(current_page + 1, home); 1000 ev_flags |= EV_NEWPAGE; 1001 XFlush(XtDisplay(w)); 1002 return; /* Don't use longjmp here: it might be called from 1003 * within the toolkit, and we don't want to longjmp out 1004 * of Xt routines. */ 1005 } 1006 else 1007 XBell(XtDisplay(w), 0); 1008 } 1009 1010 static 1011 ACTION(Act_up_or_previous) 1012 { 1013 if (y_bar != NULL) { 1014 get_xy(); 1015 if (window_y < 0) { 1016 XtCallCallbacks(y_bar, XtNscrollProc, 1017 (XtPointer) (*num_params == 0 1018 ? (-2 * (ptrdiff_t) clip_h / 3) 1019 : (ptrdiff_t) (-atof(*params) * clip_h))); 1020 return; 1021 } 1022 } 1023 1024 if (current_page > 0) { 1025 goto_page(current_page - 1, home_bottom); 1026 ev_flags |= EV_NEWPAGE; 1027 XFlush(XtDisplay(w)); 1028 return; /* Don't use longjmp here: it might be called from 1029 * within the toolkit, and we don't want to longjmp out 1030 * of Xt routines. */ 1031 } 1032 else 1033 XBell(XtDisplay(w), 0); 1034 } 1035 1036 static 1037 ACTION(Act_show_display_attributes) 1038 { 1039 Display *disp = XtDisplay(w); 1040 1041 printf("Unit = %d, bitord = %d, byteord = %d\n", 1042 BitmapUnit(disp), BitmapBitOrder(disp), ImageByteOrder(disp)); 1043 } 1044 1045 static int 1046 shrink_to_fit() 1047 { 1048 int value1; 1049 int value2; 1050 1051 value1 = ROUNDUP(unshrunk_page_w, window_w - 2); 1052 1053 value2 = ROUNDUP(unshrunk_page_h, window_h - 2); 1054 1055 return value1 > value2 ? value1 : value2; 1056 } 1057 1058 static 1059 ACTION(Act_set_shrink_factor) 1060 { 1061 int arg; 1062 1063 GET_ARG6(arg, arg = atoi(*params), 'a', arg = shrink_to_fit(), 1064 number, arg = shrink_to_fit()); 1065 1066 if (arg <= 0 || arg > 10) { 1067 XBell(XtDisplay(w), 0); 1068 return; 1069 } 1070 1071 if (arg == mane.shrinkfactor) 1072 return; 1073 mane.shrinkfactor = arg; 1074 if (arg != 1 && arg != bak_shrink) { 1075 bak_shrink = arg; 1076 fg_active = NULL; 1077 reset_fonts(); 1078 } 1079 1080 if (dvi_file == NULL) { 1081 unshrunk_page_w = unshrunk_page_h = 0; 1082 return; 1083 } 1084 1085 init_page(); 1086 reconfig(); 1087 home(False); 1088 ev_flags |= EV_NEWPAGE; 1089 XFlush(XtDisplay(w)); 1090 } 1091 1092 static 1093 ACTION(Act_shrink_to_dpi) 1094 { 1095 int arg; 1096 1097 GET_ARG(arg, 0); 1098 1099 if (arg > 0) 1100 arg = (double) pixels_per_inch / arg + 0.5; 1101 1102 if (arg <= 0) { 1103 XBell(XtDisplay(w), 0); 1104 return; 1105 } 1106 1107 if (arg == mane.shrinkfactor) 1108 return; 1109 mane.shrinkfactor = arg; 1110 if (arg != 1 && arg != bak_shrink) { 1111 bak_shrink = arg; 1112 fg_active = NULL; 1113 reset_fonts(); 1114 } 1115 1116 if (dvi_file == NULL) { 1117 unshrunk_page_w = unshrunk_page_h = 0; 1118 return; 1119 } 1120 1121 init_page(); 1122 reconfig(); 1123 home(False); 1124 ev_flags |= EV_NEWPAGE; 1125 XFlush(XtDisplay(w)); 1126 } 1127 1128 static 1129 ACTION(Act_set_density) 1130 { 1131 int arg; 1132 1133 GET_ARG4(arg, arg = atoi(*params), sign * number, 1134 {XBell(XtDisplay(w), 0); return;}); 1135 1136 float newgamma = arg != 0 ? arg / 100.0 : 1.0; 1137 1138 /* 1139 * use_grey 1140 */ 1141 if (newgamma == resource._gamma) 1142 return; 1143 resource._gamma = newgamma; 1144 fg_active = NULL; 1145 reset_colors(); 1146 1147 ev_flags |= EV_NEWPAGE; 1148 XFlush(XtDisplay(w)); 1149 } 1150 1151 1152 static 1153 ACTION(Act_set_color) 1154 { 1155 TOGGLE(resource._use_color) 1156 1157 if (resource._use_color) { 1158 resource._use_color = False; 1159 full_reset_colors(); 1160 scanned_page_color = total_pages; 1161 } 1162 else { 1163 resource._use_color = True; 1164 scanned_page = scanned_page_color = scanned_page_reset; 1165 } 1166 ev_flags |= EV_NEWPAGE; 1167 } 1168 1169 1170 static 1171 ACTION(Act_discard_number) 1172 { 1173 have_arg = False; 1174 number = 0; 1175 sign = 1; 1176 } 1177 1178 1179 /* Actions to support the magnifying glass. */ 1180 1181 static void mag_motion(XEvent *); 1182 static void mag_release(XEvent *); 1183 1184 static void 1185 compute_mag_pos(xp, yp) 1186 int *xp, *yp; 1187 { 1188 int t; 1189 1190 t = mag_x + main_x - alt.width/2; 1191 if (t > WidthOfScreen(SCRN) - (int) alt.width - 2*MAGBORD) 1192 t = WidthOfScreen(SCRN) - (int) alt.width - 2*MAGBORD; 1193 if (t < 0) t = 0; 1194 *xp = t; 1195 t = mag_y + main_y - alt.height/2; 1196 if (t > HeightOfScreen(SCRN) - (int) alt.height - 2*MAGBORD) 1197 t = HeightOfScreen(SCRN) - (int) alt.height - 2*MAGBORD; 1198 if (t < 0) t = 0; 1199 *yp = t; 1200 } 1201 1202 static 1203 ACTION(Act_magnifier) 1204 { 1205 const char *p; 1206 int x, y; 1207 XSetWindowAttributes attr; 1208 Window throwaway; 1209 Display *disp; 1210 1211 disp = XtDisplay(w); 1212 1213 if (event->type != ButtonPress || mouse_release != null_mouse 1214 || alt.win != (Window) 0 || mane.shrinkfactor == 1 1215 || *num_params != 1) { 1216 XBell(disp, 0); 1217 return; 1218 } 1219 1220 p = *params; 1221 if (*p == '*') { 1222 int n = atoi(p + 1) - 1; 1223 1224 if (n < 0 || n >= 5 || mg_size[n].w <= 0) { 1225 XBell(disp, 0); 1226 return; 1227 } 1228 alt.width = mg_size[n].w; 1229 alt.height = mg_size[n].h; 1230 } 1231 else { 1232 alt.width = alt.height = atoi(p); 1233 p = index(p, 'x'); 1234 if (p != NULL) { 1235 alt.height = atoi(p + 1); 1236 if (alt.height == 0) alt.width = 0; 1237 } 1238 if (alt.width == 0) { 1239 XBell(disp, 0); 1240 return; 1241 } 1242 } 1243 (void) XTranslateCoordinates(XtDisplay(w), event->xbutton.window, mane.win, 1244 0, 0, &mag_conv_x, &mag_conv_y, &throwaway); 1245 1246 mag_x = event->xbutton.x + mag_conv_x; 1247 mag_y = event->xbutton.y + mag_conv_y; 1248 main_x = event->xbutton.x_root - mag_x; 1249 main_y = event->xbutton.y_root - mag_y; 1250 compute_mag_pos(&x, &y); 1251 alt.base_x = (mag_x + mane_base_x) * mane.shrinkfactor - alt.width/2; 1252 alt.base_y = (mag_y + mane_base_y) * mane.shrinkfactor - alt.height/2; 1253 attr.save_under = True; 1254 attr.background_pixel = bg_current->pixel; 1255 attr.override_redirect = True; 1256 alt.win = XCreateWindow(disp, RootWindowOfScreen(SCRN), 1257 x, y, alt.width, alt.height, MAGBORD, 1258 CopyFromParent, InputOutput, CopyFromParent, 1259 CWSaveUnder | CWBackPixel | 1260 CWOverrideRedirect, &attr); 1261 XSelectInput(disp, alt.win, ExposureMask); 1262 XMapWindow(disp, alt.win); 1263 alt_stat = 1; /* waiting for exposure */ 1264 mouse_motion = mag_motion; 1265 mouse_release = mag_release; 1266 } 1267 1268 static void 1269 mag_motion(XEvent *event) 1270 { 1271 new_mag_x = event->xmotion.x + mag_conv_x; 1272 main_x = event->xmotion.x_root - new_mag_x; 1273 new_mag_y = event->xmotion.y + mag_conv_y; 1274 main_y = event->xmotion.y_root - new_mag_y; 1275 1276 if (new_mag_x != mag_x || new_mag_y != mag_y) 1277 ev_flags |= EV_MAG_MOVE; 1278 else 1279 ev_flags &= ~EV_MAG_MOVE; 1280 } 1281 1282 static void 1283 mag_release(XEvent *event) 1284 { 1285 if (alt.win != (Window) 0) { 1286 if (alt_stat) 1287 alt_stat = -1; /* destroy upon expose */ 1288 else { 1289 XDestroyWindow(DISP, alt.win); 1290 if (drawing_mag) ev_flags |= EV_MAG_GONE; 1291 alt.win = (Window) 0; 1292 mouse_motion = mouse_release = null_mouse; 1293 ev_flags &= ~EV_MAG_MOVE; 1294 can_exposures(&alt); 1295 } 1296 } 1297 } 1298 1299 static void 1300 movemag(x, y) 1301 int x, y; 1302 { 1303 int xx, yy; 1304 1305 mag_x = x; 1306 mag_y = y; 1307 if (mag_x == new_mag_x && mag_y == new_mag_y) ev_flags &= ~EV_MAG_MOVE; 1308 compute_mag_pos(&xx, &yy); 1309 XMoveWindow(DISP, alt.win, xx, yy); 1310 scrollwindow(&alt, 1311 (x + mane_base_x) * mane.shrinkfactor - (int) alt.width/2, 1312 (y + mane_base_y) * mane.shrinkfactor - (int) alt.height/2); 1313 } 1314 1315 1316 /* Actions to support dragging the image. */ 1317 1318 static int drag_last_x, drag_last_y; /* last position of cursor */ 1319 static int drag_flags; /* 1 = vert, 2 = horiz */ 1320 1321 static void drag_motion(XEvent *); 1322 static void drag_release(XEvent *); 1323 1324 static 1325 ACTION(Act_drag) 1326 { 1327 if (mouse_release != null_mouse && mouse_release != drag_release) 1328 return; 1329 1330 1331 if (*num_params != 1) return; 1332 switch (**params) { 1333 case '|': drag_flags = 1; break; 1334 case '-': drag_flags = 2; break; 1335 case '+': drag_flags = 3; break; 1336 default: return; 1337 } 1338 1339 1340 if (mouse_release == null_mouse) { 1341 mouse_motion = drag_motion; 1342 mouse_release = drag_release; 1343 drag_last_x = event->xbutton.x_root; 1344 drag_last_y = event->xbutton.y_root; 1345 } 1346 else 1347 drag_motion(event); 1348 1349 XFlush(XtDisplay(w)); 1350 dragcurs = True; 1351 } 1352 1353 1354 static void 1355 drag_motion(XEvent *event) 1356 { 1357 1358 if (drag_flags & 2) { /* if horizontal motion */ 1359 if (x_bar != NULL) 1360 XtCallCallbacks(x_bar, XtNscrollProc, 1361 (XtPointer) (ptrdiff_t) 1362 (drag_last_x - event->xbutton.x_root)); 1363 drag_last_x = event->xbutton.x_root; 1364 } 1365 1366 if (drag_flags & 1) { /* if vertical motion */ 1367 if (y_bar != NULL) 1368 XtCallCallbacks(y_bar, XtNscrollProc, 1369 (XtPointer) (ptrdiff_t) 1370 (drag_last_y - event->xbutton.y_root)); 1371 drag_last_y = event->xbutton.y_root; 1372 } 1373 } 1374 1375 1376 static void 1377 drag_release(XEvent *event) 1378 { 1379 drag_motion(event); 1380 mouse_motion = mouse_release = null_mouse; 1381 1382 XDefineCursor(DISP, CURSORWIN, 1383 ev_flags & EV_CURSOR ? redraw_cursor : ready_cursor); 1384 XFlush(DISP); 1385 dragcurs = False; 1386 } 1387 1388 1389 1390 /* Wheel support. */ 1391 1392 static int wheel_button = -1; 1393 1394 static 1395 ACTION(Act_wheel) 1396 { 1397 int dist; 1398 1399 if (*num_params == 0) { 1400 XBell(XtDisplay(w), 0); 1401 return; 1402 } 1403 dist = (index(*params, '.') == NULL) ? atoi(*params) 1404 #define WHEEL_UNIT 80 1405 : (int) (atof(*params) * WHEEL_UNIT); 1406 if (y_bar != NULL) 1407 XtCallCallbacks(y_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) dist); 1408 1409 if (event != NULL) 1410 wheel_button = event->xbutton.button; 1411 } 1412 1413 static int wheel_h_button = -1; 1414 1415 static 1416 ACTION(Act_hwheel) 1417 { 1418 int dist; 1419 1420 if (*num_params == 0) { 1421 XBell(XtDisplay(w), 0); 1422 return; 1423 } 1424 dist = (index(*params, '.') == NULL) ? atoi(*params) 1425 : (int) (atof(*params) * WHEEL_UNIT); 1426 if (x_bar != NULL) 1427 XtCallCallbacks(x_bar, XtNscrollProc, (XtPointer) (ptrdiff_t) dist); 1428 1429 if (event != NULL) 1430 wheel_h_button = event->xbutton.button; 1431 } 1432 1433 static 1434 ACTION(Act_wheel_actions) 1435 { 1436 struct wheel_acts *wactp; 1437 struct xdvi_action *actp; 1438 1439 for (wactp = wheel_actions; wactp != NULL; wactp = wactp->next) 1440 if (event->xbutton.button == wactp->button || wactp->button == 0) { 1441 Modifiers mask = 0; 1442 Modifiers value = 0; 1443 1444 if (wactp->late_bindings == NULL 1445 || _XtComputeLateBindings(XtDisplay(w), wactp->late_bindings, 1446 &value, &mask)) { 1447 mask |= wactp->mask; 1448 value |= wactp->value; 1449 if (((value ^ event->xbutton.state) & mask) == 0) { 1450 for (actp = wactp->action; actp != NULL; 1451 actp = actp->next) 1452 (actp->proc)(w, event, 1453 &actp->param, &actp->num_params); 1454 return; 1455 } 1456 } 1457 } 1458 } 1459 1460 1461 /* Internal mouse actions. */ 1462 1463 1464 static 1465 ACTION(Act_motion) 1466 { 1467 if (event->xbutton.button != wheel_button 1468 && event->xbutton.button != wheel_h_button) 1469 mouse_motion(event); 1470 } 1471 1472 1473 static 1474 ACTION(Act_release) 1475 { 1476 if (event->xbutton.button == wheel_button) { 1477 wheel_button = -1; 1478 return; 1479 } 1480 1481 if (event->xbutton.button == wheel_h_button) { 1482 wheel_h_button = -1; 1483 return; 1484 } 1485 1486 mouse_release(event); 1487 } 1488 1489 static Boolean 1490 new_dvi(const char *url) 1491 { 1492 FILE *tmp; 1493 1494 if ((tmp = http_get(url)) == NULL) 1495 return False; 1496 1497 if (dvi_magic_ok(tmp) == False) { 1498 warning_popup_long("%s: Not a DVI file", "OK", NULL, url); 1499 return False; 1500 } 1501 1502 if (fseek(tmp, 0L, SEEK_SET) < 0) 1503 err(1, "fseek"); 1504 1505 fclose(dvi_file); 1506 dvi_file = tmp; 1507 ev_flags |= EV_NEWDOC; 1508 goto_page(0, home); 1509 1510 return True; 1511 } 1512 1513 static void 1514 cb_addr_go(Widget w, XtPointer client_data, XtPointer call_data) 1515 { 1516 const char *s; 1517 1518 if (w == NULL) 1519 s = addr_default; 1520 else 1521 XtVaGetValues(addr_widget, XtNstring, &s, NULL); 1522 1523 new_dvi(s); 1524 } 1525 1526 static 1527 ACTION(Act_addr_go) 1528 { 1529 cb_addr_go(w, NULL, NULL); 1530 } 1531 1532 1533 static Boolean link_mode = False; 1534 struct htex_data htex_data; 1535 1536 static 1537 ACTION(Act_show_links) 1538 { 1539 bzero(&htex_data, sizeof(struct htex_data)); 1540 hypertex_search(&htex_data); 1541 1542 link_mode = False; 1543 1544 #define numitems(_a) (sizeof(_a) / sizeof((_a)[0])) 1545 for (int i = 0; i < numitems(htex_data.links); i++) 1546 if (htex_data.links[i] != NULL) { 1547 link_mode = True; 1548 free(htex_data.links[i]); 1549 } 1550 1551 } 1552 1553 #undef ACTION 1554 #undef GET_ARG4 1555 #undef GET_ARG 1556 #undef TOGGLE 1557 1558 void 1559 handle_expose(widget, closure, ev, cont) 1560 Widget widget; 1561 XtPointer closure; 1562 XEvent *ev; 1563 #define event (&(ev->xexpose)) 1564 Boolean *cont; /* unused */ 1565 { 1566 struct WindowRec *windowrec = (struct WindowRec *) closure; 1567 1568 if (windowrec == &alt) { 1569 if (alt_stat < 0) { /* destroy upon exposure */ 1570 alt_stat = 0; 1571 mag_release(ev); 1572 return; 1573 } 1574 else 1575 alt_stat = 0; 1576 } 1577 1578 expose(windowrec, event->x, event->y, 1579 (unsigned int) event->width, (unsigned int) event->height); 1580 } 1581 1582 #undef event 1583 1584 void 1585 handle_messages(widget, closure, event, cont) 1586 Widget widget; 1587 XtPointer closure; 1588 XEvent *event; 1589 Boolean *cont; /* unused */ 1590 { 1591 if (event->type == ClientMessage 1592 && event->xclient.message_type == XA_WM_PROTOCOLS 1593 && event->xclient.data.l[0] == XA_WM_DELETE_WINDOW) { 1594 if (closure != 0) 1595 ((XtCallbackProc) closure) (widget, NULL, NULL); 1596 else 1597 Act_quit(widget, event, NULL, 0); 1598 } 1599 } 1600 1601 static void 1602 handle_keypress(Widget w, XtPointer closure, XEvent *event, Boolean *cont) 1603 { 1604 Modifiers modifiers; 1605 KeySym keysym; 1606 int index; 1607 const char *url; 1608 1609 /* 1610 * Clear the highlights. This second call to the drawing routines 1611 * clears existing highlights. 1612 */ 1613 bzero(&htex_data, sizeof(struct htex_data)); 1614 hypertex_search(&htex_data); 1615 link_mode = False; 1616 1617 XtTranslateKeycode(DISP, event->xkey.keycode, 0, &modifiers, &keysym); 1618 1619 index = tolower(keysym) - 'a'; 1620 if (index < 0 || index > 26) 1621 return; 1622 1623 url = htex_data.links[index]; 1624 if (url == NULL) 1625 return; 1626 1627 if (new_dvi(url) == False) 1628 return; 1629 1630 XtVaSetValues(addr_widget, XtNstring, url, NULL); 1631 XtVaSetValues(addr_widget, XtNinsertPosition, strlen(url), NULL); 1632 } 1633 1634 1635 /* 1636 * Process-related routines. Call set_chld() to indicate that a given 1637 * child process should be watched for when it terminates. Call 1638 * clear_chld() to remove the process from the list. When the child 1639 * terminates, the record is removed from the list and the indicated 1640 * process is called. 1641 */ 1642 1643 static struct xchild *child_recs = NULL; /* head of child process list */ 1644 1645 void 1646 set_chld(cp) 1647 struct xchild *cp; 1648 { 1649 cp->next = child_recs; 1650 child_recs = cp; 1651 } 1652 1653 void 1654 clear_chld(cp) 1655 struct xchild *cp; 1656 { 1657 struct xchild **cpp; 1658 1659 for (cpp = &child_recs;;) { 1660 struct xchild *cp2; 1661 1662 cp2 = *cpp; 1663 if (cp2 == cp) break; 1664 cpp = &cp2->next; 1665 } 1666 *cpp = cp->next; 1667 } 1668 1669 1670 /* 1671 * File-related routines. Call set_io() to indicate that a given fd 1672 * should be watched for ability to input or output data. Call clear_io() 1673 * to remove it from the list. When poll()/select() indicates that the fd 1674 * is available for the indicated type of i/o, the corresponding routine 1675 * is called. Call clear_io() to remove an fd from the list. 1676 * Both set_io() and clear_io() can be called from within read_proc or 1677 * write_proc (although turning an io descriptor on or off is better 1678 * accomplished by setting the events flag in the xio structure, and 1679 * in the corresponding pollfd structure if the pfd pointer is not NULL 1680 * (it is always non-NULL when read_proc and write_proc are called)). 1681 * We allocate space for one additional record in the pollfd array, to 1682 * accommodate the fd for the X connection; this is done by initializing 1683 * num_fds to 1 instead of zero. 1684 */ 1685 1686 static struct xio *iorecs = NULL; /* head of xio list */ 1687 1688 static struct pollfd *fds = NULL; 1689 static int num_fds = 1; /* current number of fds */ 1690 static int max_fds = 0; /* max allocated number of fds */ 1691 static Boolean io_dirty= True; /* need to recompute fds[] array */ 1692 1693 void 1694 set_io(ip) 1695 struct xio *ip; 1696 { 1697 ip->next = iorecs; 1698 iorecs = ip; 1699 1700 ++num_fds; 1701 if (!io_dirty && num_fds <= max_fds) { 1702 fds[num_fds - 1].fd = ip->fd; 1703 fds[num_fds - 1].events = ip->xio_events; 1704 ip->pfd = &fds[num_fds - 1]; 1705 } 1706 else { 1707 ip->pfd = NULL; 1708 io_dirty = True; 1709 } 1710 } 1711 1712 void 1713 clear_io(ip) 1714 struct xio *ip; 1715 { 1716 struct xio **ipp; 1717 1718 for (ipp = &iorecs;;) { 1719 struct xio *ip2; 1720 1721 ip2 = *ipp; 1722 if (ip2 == ip) break; 1723 ipp = &ip2->next; 1724 } 1725 *ipp = ip->next; 1726 1727 --num_fds; 1728 io_dirty = True; 1729 } 1730 1731 1732 /* 1733 * Ultimately, the goal is to have this be the only place in xdvi where 1734 * blocking occurs. 1735 * 1736 * The argument to this function should be a mask of event types (EV_*) 1737 * indicating which event types should cause read_events to return instead 1738 * of waiting for more events. This function will always process all 1739 * pending events and signals before returning. 1740 * The return value is the value of ev_flags. 1741 */ 1742 1743 unsigned int 1744 read_events(unsigned int ret_mask) 1745 { 1746 XEvent event; 1747 1748 for(;;) { 1749 1750 if (!XtPending() && (ev_flags & ret_mask)) 1751 return ev_flags; 1752 1753 XtNextEvent(&event); 1754 1755 if (resized) 1756 get_geom(); 1757 if (event.xany.window == alt.win && event.type == Expose) { 1758 handle_expose((Widget) NULL, (XtPointer) &alt, &event, 1759 (Boolean *) NULL); 1760 continue; 1761 } 1762 if (link_mode == True && event.type == KeyPress) { 1763 handle_keypress(NULL, NULL, &event, NULL); 1764 continue; 1765 } 1766 1767 XtDispatchEvent(&event); 1768 } 1769 } 1770 1771 1772 /* 1773 * Higher-level routines for managing events. 1774 */ 1775 1776 static void 1777 can_exposures(windowrec) 1778 struct WindowRec *windowrec; 1779 { 1780 windowrec->min_x = windowrec->min_y = MAXDIM; 1781 windowrec->max_x = windowrec->max_y = 0; 1782 } 1783 1784 static void 1785 redraw(windowrec) 1786 struct WindowRec *windowrec; 1787 { 1788 1789 currwin = *windowrec; 1790 min_x = currwin.min_x + currwin.base_x; 1791 min_y = currwin.min_y + currwin.base_y; 1792 max_x = currwin.max_x + currwin.base_x; 1793 max_y = currwin.max_y + currwin.base_y; 1794 can_exposures(windowrec); 1795 1796 if (debug & DBG_EVENT) 1797 printf("Redraw %d x %d at (%d, %d) (base=%d,%d)\n", max_x - min_x, 1798 max_y - min_y, min_x, min_y, currwin.base_x, currwin.base_y); 1799 1800 if (!(ev_flags & EV_CURSOR)) { 1801 if (!dragcurs) { 1802 XDefineCursor(DISP, CURSORWIN, redraw_cursor); 1803 XFlush(DISP); 1804 } 1805 ev_flags |= EV_CURSOR; 1806 } 1807 1808 draw_page(); 1809 } 1810 1811 static void 1812 redraw_page() 1813 { 1814 const struct rgb *rgbp; 1815 1816 if (debug & DBG_EVENT) 1817 puts("Redraw page: "); 1818 1819 if (scanned_page < current_page) { 1820 prescan(); 1821 if (ev_flags & EV_GE_NEWPAGE) /* if we need to re-prescan */ 1822 return; 1823 } 1824 1825 if (page_info[current_page].ww != unshrunk_page_w 1826 || page_info[current_page].wh != unshrunk_page_h) { 1827 init_page(); 1828 reconfig(); 1829 } 1830 1831 /* We can't call home() without proper unshrunk_page_*, which requires 1832 * prescan(), which can't be done from within read_events() */ 1833 1834 if (home_action != NULL) { 1835 home_action(False); 1836 home_action = NULL; 1837 1838 /* This discards the expose event generated by home() */ 1839 if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) 1840 return; 1841 can_exposures(&mane); 1842 } 1843 1844 rgbp = &bg_initial; 1845 if (page_colors != NULL) 1846 rgbp = &page_colors[current_page].bg; 1847 1848 /* Set background color */ 1849 if (bg_current == NULL 1850 || rgbp->r != bg_current->color.r 1851 || rgbp->g != bg_current->color.g 1852 || rgbp->b != bg_current->color.b) { 1853 struct bgrec **bgpp; 1854 1855 for (bgpp = &bg_head;;) { 1856 bg_current = *bgpp; 1857 if (bg_current == NULL) { /* if bg is not in list */ 1858 bg_current = *bgpp = xmalloc(sizeof *bg_current); 1859 bg_current->next = NULL; 1860 bg_current->color = *rgbp; 1861 bg_current->fg_head = NULL; 1862 bg_current->pixel_good = False; 1863 break; 1864 } 1865 if (bg_current->color.r == rgbp->r 1866 && bg_current->color.g == rgbp->g 1867 && bg_current->color.b == rgbp->b) 1868 break; 1869 bgpp = &bg_current->next; 1870 } 1871 fg_current = NULL; /* force change of foreground color */ 1872 if (debug & DBG_DVI) 1873 printf("Changing background color to %5d %5d %5d\n", 1874 bg_current->color.r, bg_current->color.g, 1875 bg_current->color.b); 1876 1877 if (!bg_current->pixel_good) { 1878 bg_current->pixel = alloc_color(&bg_current->color, 1879 back_color_data.pixel); 1880 bg_current->pixel_good = True; 1881 } 1882 XSetWindowBackground(DISP, mane.win, bg_current->pixel); 1883 } 1884 1885 XWindowAttributes attrs; 1886 XGetWindowAttributes(DISP, mane.win, &attrs); 1887 int backing_store = attrs.backing_store; 1888 1889 XClearWindow(DISP, mane.win); 1890 if (backing_store != NotUseful) { 1891 mane.min_x = mane.min_y = 0; 1892 mane.max_x = page_w; 1893 mane.max_y = page_h; 1894 } 1895 else { 1896 get_xy(); 1897 mane.min_x = -window_x; 1898 mane.max_x = -window_x + clip_w; 1899 mane.min_y = -window_y; 1900 mane.max_y = -window_y + clip_h; 1901 } 1902 redraw(&mane); 1903 } 1904 1905 void 1906 do_pages() 1907 { 1908 (void) read_events(EV_GT_IDLE); 1909 1910 if (ev_flags & (EV_NEWPAGE | EV_NEWDOC | EV_PS_TOGGLE)) { 1911 ev_flags &= ~(EV_NEWPAGE | EV_EXPOSE | EV_PS_TOGGLE); 1912 if (ev_flags & EV_NEWDOC) { 1913 ev_flags &= ~EV_NEWDOC; 1914 if (reload_dvi_file() == False) 1915 warning_popup("Bad DVI document", "OK", NULL); 1916 } 1917 can_exposures(&mane); 1918 can_exposures(&alt); 1919 1920 if (dvi_file != NULL) 1921 redraw_page(); 1922 } 1923 else if (ev_flags & EV_MAG_MOVE) { 1924 if (alt.win == (Window) 0) 1925 ev_flags &= ~EV_MAG_MOVE; 1926 else if (abs(new_mag_x - mag_x) > 2 * abs(new_mag_y - mag_y)) 1927 movemag(new_mag_x, mag_y); 1928 else if (abs(new_mag_y - mag_y) > 2 * abs(new_mag_x - mag_x)) 1929 movemag(mag_x, new_mag_y); 1930 else 1931 movemag(new_mag_x, new_mag_y); 1932 } 1933 else if (ev_flags & EV_EXPOSE) { 1934 if (alt.min_x < MAXDIM) { 1935 if (mane.min_x >= MAXDIM) 1936 ev_flags &= ~EV_EXPOSE; 1937 redraw(&alt); 1938 } 1939 else { 1940 ev_flags &= ~EV_EXPOSE; 1941 if (mane.min_x < MAXDIM) 1942 redraw(&mane); 1943 } 1944 } 1945 else if (ev_flags & EV_CURSOR) { 1946 /* 1947 * This code eliminates unnecessary calls to XDefineCursor, 1948 * since this is a slow operation on some hardware 1949 * (e.g., S3 chips). 1950 */ 1951 XSync(DISP, False); 1952 if (!XtPending()) { 1953 ev_flags &= ~EV_CURSOR; 1954 if (!dragcurs) 1955 XDefineCursor(DISP, CURSORWIN, ready_cursor); 1956 } 1957 } 1958 1959 XFlush(DISP); 1960 }