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