Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/platform/x11/x11_main.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 // Copyright (C) 2004-2025 Artifex Software, Inc. | |
| 2 // | |
| 3 // This file is part of MuPDF. | |
| 4 // | |
| 5 // MuPDF is free software: you can redistribute it and/or modify it under the | |
| 6 // terms of the GNU Affero General Public License as published by the Free | |
| 7 // Software Foundation, either version 3 of the License, or (at your option) | |
| 8 // any later version. | |
| 9 // | |
| 10 // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
| 12 // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
| 13 // details. | |
| 14 // | |
| 15 // You should have received a copy of the GNU Affero General Public License | |
| 16 // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> | |
| 17 // | |
| 18 // Alternative licensing terms are available from the licensor. | |
| 19 // For commercial licensing, see <https://www.artifex.com/> or contact | |
| 20 // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, | |
| 21 // CA 94129, USA, for further information. | |
| 22 | |
| 23 #include "pdfapp.h" | |
| 24 | |
| 25 #include <X11/Xlib.h> | |
| 26 #include <X11/Xutil.h> | |
| 27 #include <X11/Xatom.h> | |
| 28 #include <X11/cursorfont.h> | |
| 29 #include <X11/keysym.h> | |
| 30 #include <X11/XF86keysym.h> | |
| 31 | |
| 32 #include <string.h> | |
| 33 #include <stdlib.h> | |
| 34 #include <stdio.h> | |
| 35 | |
| 36 #include <sys/select.h> | |
| 37 #include <sys/time.h> | |
| 38 #include <sys/types.h> | |
| 39 #include <sys/wait.h> | |
| 40 #include <unistd.h> | |
| 41 #include <signal.h> | |
| 42 #include <limits.h> | |
| 43 | |
| 44 #define mupdf_icon_bitmap_16_width 16 | |
| 45 #define mupdf_icon_bitmap_16_height 16 | |
| 46 static unsigned char mupdf_icon_bitmap_16_bits[] = { | |
| 47 0x00, 0x00, 0x00, 0x1e, 0x00, 0x2b, 0x80, 0x55, 0x8c, 0x62, 0x8c, 0x51, | |
| 48 0x9c, 0x61, 0x1c, 0x35, 0x3c, 0x1f, 0x3c, 0x0f, 0xfc, 0x0f, 0xec, 0x0d, | |
| 49 0xec, 0x0d, 0xcc, 0x0c, 0xcc, 0x0c, 0x00, 0x00 }; | |
| 50 | |
| 51 #define mupdf_icon_bitmap_16_mask_width 16 | |
| 52 #define mupdf_icon_bitmap_16_mask_height 16 | |
| 53 static unsigned char mupdf_icon_bitmap_16_mask_bits[] = { | |
| 54 0x00, 0x1e, 0x00, 0x3f, 0x80, 0x7f, 0xce, 0xff, 0xde, 0xff, 0xde, 0xff, | |
| 55 0xfe, 0xff, 0xfe, 0x7f, 0xfe, 0x3f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, | |
| 56 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xce, 0x1c }; | |
| 57 | |
| 58 #ifndef timeradd | |
| 59 #define timeradd(a, b, result) \ | |
| 60 do { \ | |
| 61 (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ | |
| 62 (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ | |
| 63 if ((result)->tv_usec >= 1000000) \ | |
| 64 { \ | |
| 65 ++(result)->tv_sec; \ | |
| 66 (result)->tv_usec -= 1000000; \ | |
| 67 } \ | |
| 68 } while (0) | |
| 69 #endif | |
| 70 | |
| 71 #ifndef timersub | |
| 72 #define timersub(a, b, result) \ | |
| 73 do { \ | |
| 74 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ | |
| 75 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ | |
| 76 if ((result)->tv_usec < 0) { \ | |
| 77 --(result)->tv_sec; \ | |
| 78 (result)->tv_usec += 1000000; \ | |
| 79 } \ | |
| 80 } while (0) | |
| 81 #endif | |
| 82 | |
| 83 extern int ximage_init(Display *display, int screen, Visual *visual); | |
| 84 extern int ximage_get_depth(void); | |
| 85 extern Visual *ximage_get_visual(void); | |
| 86 extern Colormap ximage_get_colormap(void); | |
| 87 extern void ximage_blit(Drawable d, GC gc, int dstx, int dsty, | |
| 88 unsigned char *srcdata, | |
| 89 int srcx, int srcy, int srcw, int srch, int srcstride); | |
| 90 | |
| 91 static void windrawstringxor(pdfapp_t *app, int x, int y, char *s); | |
| 92 static void cleanup(pdfapp_t *app); | |
| 93 | |
| 94 static Display *xdpy; | |
| 95 static Atom XA_CLIPBOARD; | |
| 96 static Atom XA_TARGETS; | |
| 97 static Atom XA_TIMESTAMP; | |
| 98 static Atom XA_UTF8_STRING; | |
| 99 static Atom WM_DELETE_WINDOW; | |
| 100 static Atom NET_WM_NAME; | |
| 101 static Atom NET_WM_STATE; | |
| 102 static Atom NET_WM_STATE_FULLSCREEN; | |
| 103 static Atom WM_RELOAD_PAGE; | |
| 104 static int x11fd; | |
| 105 static int xscr; | |
| 106 static Window xwin; | |
| 107 static Pixmap xicon, xmask; | |
| 108 static GC xgc; | |
| 109 static XEvent xevt; | |
| 110 static int mapped = 0; | |
| 111 static Cursor xcarrow, xchand, xcwait, xccaret; | |
| 112 static int justcopied = 0; | |
| 113 static int dirty = 0; | |
| 114 static int transition_dirty = 0; | |
| 115 static int dirtysearch = 0; | |
| 116 static char *password = ""; | |
| 117 static XColor xbgcolor; | |
| 118 static int reqw = 0; | |
| 119 static int reqh = 0; | |
| 120 static char copylatin1[1024 * 16] = ""; | |
| 121 static char copyutf8[1024 * 48] = ""; | |
| 122 static Time copytime; | |
| 123 static char *filename; | |
| 124 static char message[1024] = ""; | |
| 125 | |
| 126 static pdfapp_t gapp; | |
| 127 static int closing = 0; | |
| 128 static int reloading = 0; | |
| 129 static int showingpage = 0; | |
| 130 static int showingmessage = 0; | |
| 131 | |
| 132 static int advance_scheduled = 0; | |
| 133 static struct timeval tmo; | |
| 134 static struct timeval tmo_advance; | |
| 135 static struct timeval tmo_at; | |
| 136 | |
| 137 /* | |
| 138 * Dialog boxes | |
| 139 */ | |
| 140 static void showmessage(pdfapp_t *app, int timeout, char *msg) | |
| 141 { | |
| 142 struct timeval now; | |
| 143 | |
| 144 showingmessage = 1; | |
| 145 showingpage = 0; | |
| 146 | |
| 147 fz_strlcpy(message, msg, sizeof message); | |
| 148 | |
| 149 if ((!tmo_at.tv_sec && !tmo_at.tv_usec) || tmo.tv_sec < timeout) | |
| 150 { | |
| 151 tmo.tv_sec = timeout; | |
| 152 tmo.tv_usec = 0; | |
| 153 gettimeofday(&now, NULL); | |
| 154 timeradd(&now, &tmo, &tmo_at); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 void winerror(pdfapp_t *app, char *msg) | |
| 159 { | |
| 160 fprintf(stderr, "mupdf: error: %s\n", msg); | |
| 161 cleanup(app); | |
| 162 exit(1); | |
| 163 } | |
| 164 | |
| 165 void winwarn(pdfapp_t *app, char *msg) | |
| 166 { | |
| 167 char buf[1024]; | |
| 168 snprintf(buf, sizeof buf, "warning: %s", msg); | |
| 169 showmessage(app, 10, buf); | |
| 170 fprintf(stderr, "mupdf: %s\n", buf); | |
| 171 } | |
| 172 | |
| 173 void winalert(pdfapp_t *app, pdf_alert_event *alert) | |
| 174 { | |
| 175 char buf[1024]; | |
| 176 snprintf(buf, sizeof buf, "Alert %s: %s", alert->title, alert->message); | |
| 177 fprintf(stderr, "%s\n", buf); | |
| 178 switch (alert->button_group_type) | |
| 179 { | |
| 180 case PDF_ALERT_BUTTON_GROUP_OK: | |
| 181 case PDF_ALERT_BUTTON_GROUP_OK_CANCEL: | |
| 182 alert->button_pressed = PDF_ALERT_BUTTON_OK; | |
| 183 break; | |
| 184 case PDF_ALERT_BUTTON_GROUP_YES_NO: | |
| 185 case PDF_ALERT_BUTTON_GROUP_YES_NO_CANCEL: | |
| 186 alert->button_pressed = PDF_ALERT_BUTTON_YES; | |
| 187 break; | |
| 188 } | |
| 189 } | |
| 190 | |
| 191 void winprint(pdfapp_t *app) | |
| 192 { | |
| 193 fprintf(stderr, "The MuPDF library supports printing, but this application currently does not\n"); | |
| 194 } | |
| 195 | |
| 196 char *winpassword(pdfapp_t *app, char *fname) | |
| 197 { | |
| 198 char *r = password; | |
| 199 password = NULL; | |
| 200 return r; | |
| 201 } | |
| 202 | |
| 203 char *wintextinput(pdfapp_t *app, char *inittext, int retry) | |
| 204 { | |
| 205 /* We don't support text input on the x11 viewer */ | |
| 206 return NULL; | |
| 207 } | |
| 208 | |
| 209 int winchoiceinput(pdfapp_t *app, int nopts, const char *opts[], int *nvals, const char *vals[]) | |
| 210 { | |
| 211 /* FIXME: temporary dummy implementation */ | |
| 212 return 0; | |
| 213 } | |
| 214 | |
| 215 /* | |
| 216 * X11 magic | |
| 217 */ | |
| 218 | |
| 219 static void winopen(void) | |
| 220 { | |
| 221 XWMHints *wmhints; | |
| 222 XClassHint *classhint; | |
| 223 | |
| 224 #ifdef HAVE_CURL | |
| 225 if (!XInitThreads()) | |
| 226 fz_throw(gapp.ctx, FZ_ERROR_GENERIC, "cannot initialize X11 for multi-threading"); | |
| 227 #endif | |
| 228 | |
| 229 xdpy = XOpenDisplay(NULL); | |
| 230 if (!xdpy) | |
| 231 fz_throw(gapp.ctx, FZ_ERROR_GENERIC, "cannot open display"); | |
| 232 | |
| 233 XA_CLIPBOARD = XInternAtom(xdpy, "CLIPBOARD", False); | |
| 234 XA_TARGETS = XInternAtom(xdpy, "TARGETS", False); | |
| 235 XA_TIMESTAMP = XInternAtom(xdpy, "TIMESTAMP", False); | |
| 236 XA_UTF8_STRING = XInternAtom(xdpy, "UTF8_STRING", False); | |
| 237 WM_DELETE_WINDOW = XInternAtom(xdpy, "WM_DELETE_WINDOW", False); | |
| 238 NET_WM_NAME = XInternAtom(xdpy, "_NET_WM_NAME", False); | |
| 239 NET_WM_STATE = XInternAtom(xdpy, "_NET_WM_STATE", False); | |
| 240 NET_WM_STATE_FULLSCREEN = XInternAtom(xdpy, "_NET_WM_STATE_FULLSCREEN", False); | |
| 241 WM_RELOAD_PAGE = XInternAtom(xdpy, "_WM_RELOAD_PAGE", False); | |
| 242 | |
| 243 xscr = DefaultScreen(xdpy); | |
| 244 | |
| 245 ximage_init(xdpy, xscr, DefaultVisual(xdpy, xscr)); | |
| 246 | |
| 247 xcarrow = XCreateFontCursor(xdpy, XC_left_ptr); | |
| 248 xchand = XCreateFontCursor(xdpy, XC_hand2); | |
| 249 xcwait = XCreateFontCursor(xdpy, XC_watch); | |
| 250 xccaret = XCreateFontCursor(xdpy, XC_xterm); | |
| 251 | |
| 252 xbgcolor.red = 0x7000; | |
| 253 xbgcolor.green = 0x7000; | |
| 254 xbgcolor.blue = 0x7000; | |
| 255 | |
| 256 XAllocColor(xdpy, DefaultColormap(xdpy, xscr), &xbgcolor); | |
| 257 | |
| 258 xwin = XCreateWindow(xdpy, DefaultRootWindow(xdpy), | |
| 259 10, 10, 200, 100, 0, | |
| 260 ximage_get_depth(), | |
| 261 InputOutput, | |
| 262 ximage_get_visual(), | |
| 263 0, | |
| 264 NULL); | |
| 265 if (xwin == None) | |
| 266 fz_throw(gapp.ctx, FZ_ERROR_GENERIC, "cannot create window"); | |
| 267 | |
| 268 XSetWindowColormap(xdpy, xwin, ximage_get_colormap()); | |
| 269 XSelectInput(xdpy, xwin, | |
| 270 StructureNotifyMask | ExposureMask | KeyPressMask | | |
| 271 PointerMotionMask | ButtonPressMask | ButtonReleaseMask); | |
| 272 | |
| 273 mapped = 0; | |
| 274 | |
| 275 xgc = XCreateGC(xdpy, xwin, 0, NULL); | |
| 276 | |
| 277 XDefineCursor(xdpy, xwin, xcarrow); | |
| 278 | |
| 279 wmhints = XAllocWMHints(); | |
| 280 if (wmhints) | |
| 281 { | |
| 282 wmhints->flags = IconPixmapHint | IconMaskHint; | |
| 283 xicon = XCreateBitmapFromData(xdpy, xwin, | |
| 284 (char*)mupdf_icon_bitmap_16_bits, | |
| 285 mupdf_icon_bitmap_16_width, | |
| 286 mupdf_icon_bitmap_16_height); | |
| 287 xmask = XCreateBitmapFromData(xdpy, xwin, | |
| 288 (char*)mupdf_icon_bitmap_16_mask_bits, | |
| 289 mupdf_icon_bitmap_16_mask_width, | |
| 290 mupdf_icon_bitmap_16_mask_height); | |
| 291 if (xicon && xmask) | |
| 292 { | |
| 293 wmhints->icon_pixmap = xicon; | |
| 294 wmhints->icon_mask = xmask; | |
| 295 XSetWMHints(xdpy, xwin, wmhints); | |
| 296 } | |
| 297 XFree(wmhints); | |
| 298 } | |
| 299 | |
| 300 classhint = XAllocClassHint(); | |
| 301 if (classhint) | |
| 302 { | |
| 303 classhint->res_name = "mupdf"; | |
| 304 classhint->res_class = "MuPDF"; | |
| 305 XSetClassHint(xdpy, xwin, classhint); | |
| 306 XFree(classhint); | |
| 307 } | |
| 308 | |
| 309 XSetWMProtocols(xdpy, xwin, &WM_DELETE_WINDOW, 1); | |
| 310 | |
| 311 x11fd = ConnectionNumber(xdpy); | |
| 312 } | |
| 313 | |
| 314 void winclose(pdfapp_t *app) | |
| 315 { | |
| 316 if (pdfapp_preclose(app)) | |
| 317 { | |
| 318 closing = 1; | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 int winsavequery(pdfapp_t *app) | |
| 323 { | |
| 324 fprintf(stderr, "mupdf: discarded changes to document\n"); | |
| 325 /* FIXME: temporary dummy implementation */ | |
| 326 return DISCARD; | |
| 327 } | |
| 328 | |
| 329 int wingetsavepath(pdfapp_t *app, char *buf, int len) | |
| 330 { | |
| 331 /* FIXME: temporary dummy implementation */ | |
| 332 return 0; | |
| 333 } | |
| 334 | |
| 335 void winreplacefile(pdfapp_t *app, char *source, char *target) | |
| 336 { | |
| 337 if (rename(source, target) == -1) | |
| 338 pdfapp_warn(app, "unable to rename file"); | |
| 339 } | |
| 340 | |
| 341 void wincopyfile(pdfapp_t *app, char *source, char *target) | |
| 342 { | |
| 343 FILE *in, *out; | |
| 344 char buf[32 << 10]; | |
| 345 size_t n; | |
| 346 | |
| 347 in = fopen(source, "rb"); | |
| 348 if (!in) | |
| 349 { | |
| 350 pdfapp_error(app, "cannot open source file for copying"); | |
| 351 return; | |
| 352 } | |
| 353 out = fopen(target, "wb"); | |
| 354 if (!out) | |
| 355 { | |
| 356 pdfapp_error(app, "cannot open target file for copying"); | |
| 357 fclose(in); | |
| 358 return; | |
| 359 } | |
| 360 | |
| 361 for (;;) | |
| 362 { | |
| 363 n = fread(buf, 1, sizeof buf, in); | |
| 364 fwrite(buf, 1, n, out); | |
| 365 if (n < sizeof buf) | |
| 366 { | |
| 367 if (ferror(in)) | |
| 368 pdfapp_error(app, "cannot read data from source file"); | |
| 369 break; | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 fclose(out); | |
| 374 fclose(in); | |
| 375 } | |
| 376 | |
| 377 static void cleanup(pdfapp_t *app) | |
| 378 { | |
| 379 fz_context *ctx = app->ctx; | |
| 380 | |
| 381 pdfapp_close(app); | |
| 382 | |
| 383 XDestroyWindow(xdpy, xwin); | |
| 384 | |
| 385 XFreePixmap(xdpy, xicon); | |
| 386 | |
| 387 XFreeCursor(xdpy, xccaret); | |
| 388 XFreeCursor(xdpy, xcwait); | |
| 389 XFreeCursor(xdpy, xchand); | |
| 390 XFreeCursor(xdpy, xcarrow); | |
| 391 | |
| 392 XFreeGC(xdpy, xgc); | |
| 393 | |
| 394 XCloseDisplay(xdpy); | |
| 395 | |
| 396 fz_drop_context(ctx); | |
| 397 } | |
| 398 | |
| 399 static int winresolution(void) | |
| 400 { | |
| 401 return DisplayWidth(xdpy, xscr) * 25.4f / | |
| 402 DisplayWidthMM(xdpy, xscr) + 0.5f; | |
| 403 } | |
| 404 | |
| 405 void wincursor(pdfapp_t *app, int curs) | |
| 406 { | |
| 407 if (curs == ARROW) | |
| 408 XDefineCursor(xdpy, xwin, xcarrow); | |
| 409 if (curs == HAND) | |
| 410 XDefineCursor(xdpy, xwin, xchand); | |
| 411 if (curs == WAIT) | |
| 412 XDefineCursor(xdpy, xwin, xcwait); | |
| 413 if (curs == CARET) | |
| 414 XDefineCursor(xdpy, xwin, xccaret); | |
| 415 XFlush(xdpy); | |
| 416 } | |
| 417 | |
| 418 void wintitle(pdfapp_t *app, char *s) | |
| 419 { | |
| 420 XStoreName(xdpy, xwin, s); | |
| 421 #ifdef X_HAVE_UTF8_STRING | |
| 422 Xutf8SetWMProperties(xdpy, xwin, s, s, NULL, 0, NULL, NULL, NULL); | |
| 423 #else | |
| 424 XmbSetWMProperties(xdpy, xwin, s, s, NULL, 0, NULL, NULL, NULL); | |
| 425 #endif | |
| 426 XChangeProperty(xdpy, xwin, NET_WM_NAME, XA_UTF8_STRING, 8, | |
| 427 PropModeReplace, (unsigned char *)s, strlen(s)); | |
| 428 } | |
| 429 | |
| 430 void winhelp(pdfapp_t *app) | |
| 431 { | |
| 432 fprintf(stderr, "%s\n%s", pdfapp_version(app), pdfapp_usage(app)); | |
| 433 } | |
| 434 | |
| 435 void winresize(pdfapp_t *app, int w, int h) | |
| 436 { | |
| 437 int image_w = gapp.layout_w; | |
| 438 int image_h = gapp.layout_h; | |
| 439 XWindowChanges values; | |
| 440 int mask, width, height; | |
| 441 | |
| 442 if (gapp.image) | |
| 443 { | |
| 444 image_w = fz_pixmap_width(gapp.ctx, gapp.image); | |
| 445 image_h = fz_pixmap_height(gapp.ctx, gapp.image); | |
| 446 } | |
| 447 | |
| 448 mask = CWWidth | CWHeight; | |
| 449 values.width = w; | |
| 450 values.height = h; | |
| 451 XConfigureWindow(xdpy, xwin, mask, &values); | |
| 452 | |
| 453 reqw = w; | |
| 454 reqh = h; | |
| 455 | |
| 456 if (!mapped) | |
| 457 { | |
| 458 gapp.winw = w; | |
| 459 gapp.winh = h; | |
| 460 width = -1; | |
| 461 height = -1; | |
| 462 | |
| 463 XMapWindow(xdpy, xwin); | |
| 464 XFlush(xdpy); | |
| 465 | |
| 466 while (1) | |
| 467 { | |
| 468 XNextEvent(xdpy, &xevt); | |
| 469 if (xevt.type == ConfigureNotify) | |
| 470 { | |
| 471 width = xevt.xconfigure.width; | |
| 472 height = xevt.xconfigure.height; | |
| 473 } | |
| 474 if (xevt.type == MapNotify) | |
| 475 break; | |
| 476 } | |
| 477 | |
| 478 XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr)); | |
| 479 XFillRectangle(xdpy, xwin, xgc, 0, 0, image_w, image_h); | |
| 480 XFlush(xdpy); | |
| 481 | |
| 482 if (width != reqw || height != reqh) | |
| 483 { | |
| 484 gapp.shrinkwrap = 0; | |
| 485 dirty = 1; | |
| 486 pdfapp_onresize(&gapp, width, height); | |
| 487 } | |
| 488 | |
| 489 mapped = 1; | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 void winfullscreen(pdfapp_t *app, int state) | |
| 494 { | |
| 495 XEvent xev; | |
| 496 xev.xclient.type = ClientMessage; | |
| 497 xev.xclient.serial = 0; | |
| 498 xev.xclient.send_event = True; | |
| 499 xev.xclient.window = xwin; | |
| 500 xev.xclient.message_type = NET_WM_STATE; | |
| 501 xev.xclient.format = 32; | |
| 502 xev.xclient.data.l[0] = state; | |
| 503 xev.xclient.data.l[1] = NET_WM_STATE_FULLSCREEN; | |
| 504 xev.xclient.data.l[2] = 0; | |
| 505 XSendEvent(xdpy, DefaultRootWindow(xdpy), False, | |
| 506 SubstructureRedirectMask | SubstructureNotifyMask, | |
| 507 &xev); | |
| 508 } | |
| 509 | |
| 510 static void fillrect(int x, int y, int w, int h) | |
| 511 { | |
| 512 if (w > 0 && h > 0) | |
| 513 XFillRectangle(xdpy, xwin, xgc, x, y, w, h); | |
| 514 } | |
| 515 | |
| 516 static void winblitstatusbar(pdfapp_t *app) | |
| 517 { | |
| 518 if (gapp.issearching) | |
| 519 { | |
| 520 char buf[sizeof(gapp.search) + 50]; | |
| 521 sprintf(buf, "Search: %s", gapp.search); | |
| 522 XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr)); | |
| 523 fillrect(0, 0, gapp.winw, 30); | |
| 524 windrawstring(&gapp, 10, 20, buf); | |
| 525 } | |
| 526 else if (showingmessage) | |
| 527 { | |
| 528 XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr)); | |
| 529 fillrect(0, 0, gapp.winw, 30); | |
| 530 windrawstring(&gapp, 10, 20, message); | |
| 531 } | |
| 532 else if (showingpage) | |
| 533 { | |
| 534 char buf[42]; | |
| 535 snprintf(buf, sizeof buf, "Page %d/%d", gapp.pageno, gapp.pagecount); | |
| 536 windrawstringxor(&gapp, 10, 20, buf); | |
| 537 } | |
| 538 } | |
| 539 | |
| 540 int winisresolutionacceptable(pdfapp_t *app, fz_matrix ctm) | |
| 541 { | |
| 542 fz_rect bounds; | |
| 543 fz_irect ibounds; | |
| 544 int w, h; | |
| 545 | |
| 546 bounds = fz_transform_rect(app->page_bbox, ctm); | |
| 547 ibounds = fz_round_rect(bounds); | |
| 548 w = ibounds.x1 - ibounds.x0; | |
| 549 h = ibounds.y1 - ibounds.y0; | |
| 550 | |
| 551 return w < (INT_MAX / 4) / h; | |
| 552 } | |
| 553 | |
| 554 static void winblit(pdfapp_t *app) | |
| 555 { | |
| 556 if (gapp.image) | |
| 557 { | |
| 558 int image_w = fz_pixmap_width(gapp.ctx, gapp.image); | |
| 559 int image_h = fz_pixmap_height(gapp.ctx, gapp.image); | |
| 560 int image_n = fz_pixmap_components(gapp.ctx, gapp.image); | |
| 561 unsigned char *image_samples = fz_pixmap_samples(gapp.ctx, gapp.image); | |
| 562 int x0 = gapp.panx; | |
| 563 int y0 = gapp.pany; | |
| 564 int x1 = gapp.panx + image_w; | |
| 565 int y1 = gapp.pany + image_h; | |
| 566 | |
| 567 if (app->invert) | |
| 568 XSetForeground(xdpy, xgc, BlackPixel(xdpy, DefaultScreen(xdpy))); | |
| 569 else | |
| 570 XSetForeground(xdpy, xgc, xbgcolor.pixel); | |
| 571 fillrect(0, 0, x0, gapp.winh); | |
| 572 fillrect(x1, 0, gapp.winw - x1, gapp.winh); | |
| 573 fillrect(0, 0, gapp.winw, y0); | |
| 574 fillrect(0, y1, gapp.winw, gapp.winh - y1); | |
| 575 | |
| 576 if (gapp.iscopying || justcopied) | |
| 577 { | |
| 578 pdfapp_invert(&gapp, gapp.selr); | |
| 579 justcopied = 1; | |
| 580 } | |
| 581 | |
| 582 pdfapp_inverthit(&gapp); | |
| 583 | |
| 584 if (image_n == 4) | |
| 585 ximage_blit(xwin, xgc, | |
| 586 x0, y0, | |
| 587 image_samples, | |
| 588 0, 0, | |
| 589 image_w, | |
| 590 image_h, | |
| 591 image_w * image_n); | |
| 592 else if (image_n == 2) | |
| 593 { | |
| 594 int i = image_w*image_h; | |
| 595 unsigned char *color = malloc(i*4); | |
| 596 if (color) | |
| 597 { | |
| 598 unsigned char *s = image_samples; | |
| 599 unsigned char *d = color; | |
| 600 for (; i > 0 ; i--) | |
| 601 { | |
| 602 d[2] = d[1] = d[0] = *s++; | |
| 603 d[3] = *s++; | |
| 604 d += 4; | |
| 605 } | |
| 606 ximage_blit(xwin, xgc, | |
| 607 x0, y0, | |
| 608 color, | |
| 609 0, 0, | |
| 610 image_w, | |
| 611 image_h, | |
| 612 image_w * 4); | |
| 613 free(color); | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 pdfapp_inverthit(&gapp); | |
| 618 | |
| 619 if (gapp.iscopying || justcopied) | |
| 620 { | |
| 621 pdfapp_invert(&gapp, gapp.selr); | |
| 622 justcopied = 1; | |
| 623 } | |
| 624 } | |
| 625 else | |
| 626 { | |
| 627 XSetForeground(xdpy, xgc, xbgcolor.pixel); | |
| 628 fillrect(0, 0, gapp.winw, gapp.winh); | |
| 629 } | |
| 630 | |
| 631 winblitstatusbar(app); | |
| 632 } | |
| 633 | |
| 634 void winrepaint(pdfapp_t *app) | |
| 635 { | |
| 636 dirty = 1; | |
| 637 if (app->in_transit) | |
| 638 transition_dirty = 1; | |
| 639 } | |
| 640 | |
| 641 void winrepaintsearch(pdfapp_t *app) | |
| 642 { | |
| 643 dirtysearch = 1; | |
| 644 } | |
| 645 | |
| 646 void winadvancetimer(pdfapp_t *app, float duration) | |
| 647 { | |
| 648 struct timeval now; | |
| 649 | |
| 650 gettimeofday(&now, NULL); | |
| 651 memset(&tmo_advance, 0, sizeof(tmo_advance)); | |
| 652 tmo_advance.tv_sec = (int)duration; | |
| 653 tmo_advance.tv_usec = 1000000 * (duration - tmo_advance.tv_sec); | |
| 654 timeradd(&tmo_advance, &now, &tmo_advance); | |
| 655 advance_scheduled = 1; | |
| 656 } | |
| 657 | |
| 658 static void windrawstringxor(pdfapp_t *app, int x, int y, char *s) | |
| 659 { | |
| 660 int prevfunction; | |
| 661 XGCValues xgcv; | |
| 662 | |
| 663 XGetGCValues(xdpy, xgc, GCFunction, &xgcv); | |
| 664 prevfunction = xgcv.function; | |
| 665 xgcv.function = GXxor; | |
| 666 XChangeGC(xdpy, xgc, GCFunction, &xgcv); | |
| 667 | |
| 668 XSetForeground(xdpy, xgc, WhitePixel(xdpy, DefaultScreen(xdpy))); | |
| 669 | |
| 670 XDrawString(xdpy, xwin, xgc, x, y, s, strlen(s)); | |
| 671 XFlush(xdpy); | |
| 672 | |
| 673 XGetGCValues(xdpy, xgc, GCFunction, &xgcv); | |
| 674 xgcv.function = prevfunction; | |
| 675 XChangeGC(xdpy, xgc, GCFunction, &xgcv); | |
| 676 } | |
| 677 | |
| 678 void windrawstring(pdfapp_t *app, int x, int y, char *s) | |
| 679 { | |
| 680 XSetForeground(xdpy, xgc, BlackPixel(xdpy, DefaultScreen(xdpy))); | |
| 681 XDrawString(xdpy, xwin, xgc, x, y, s, strlen(s)); | |
| 682 } | |
| 683 | |
| 684 static void docopy(pdfapp_t *app, Atom copy_target) | |
| 685 { | |
| 686 unsigned short copyucs2[16 * 1024]; | |
| 687 char *latin1 = copylatin1; | |
| 688 char *utf8 = copyutf8; | |
| 689 unsigned short *ucs2; | |
| 690 int ucs; | |
| 691 | |
| 692 pdfapp_oncopy(&gapp, copyucs2, 16 * 1024); | |
| 693 | |
| 694 for (ucs2 = copyucs2; ucs2[0] != 0; ucs2++) | |
| 695 { | |
| 696 ucs = ucs2[0]; | |
| 697 | |
| 698 utf8 += fz_runetochar(utf8, ucs); | |
| 699 | |
| 700 if (ucs < 256) | |
| 701 *latin1++ = ucs; | |
| 702 else | |
| 703 *latin1++ = '?'; | |
| 704 } | |
| 705 | |
| 706 *utf8 = 0; | |
| 707 *latin1 = 0; | |
| 708 | |
| 709 XSetSelectionOwner(xdpy, copy_target, xwin, copytime); | |
| 710 | |
| 711 justcopied = 1; | |
| 712 } | |
| 713 | |
| 714 void windocopy(pdfapp_t *app) | |
| 715 { | |
| 716 docopy(app, XA_PRIMARY); | |
| 717 } | |
| 718 | |
| 719 static void onselreq(Window requestor, Atom selection, Atom target, Atom property, Time time) | |
| 720 { | |
| 721 XEvent nevt; | |
| 722 | |
| 723 advance_scheduled = 0; | |
| 724 | |
| 725 if (property == None) | |
| 726 property = target; | |
| 727 | |
| 728 nevt.xselection.type = SelectionNotify; | |
| 729 nevt.xselection.send_event = True; | |
| 730 nevt.xselection.display = xdpy; | |
| 731 nevt.xselection.requestor = requestor; | |
| 732 nevt.xselection.selection = selection; | |
| 733 nevt.xselection.target = target; | |
| 734 nevt.xselection.property = property; | |
| 735 nevt.xselection.time = time; | |
| 736 | |
| 737 if (target == XA_TARGETS) | |
| 738 { | |
| 739 Atom atomlist[4]; | |
| 740 atomlist[0] = XA_TARGETS; | |
| 741 atomlist[1] = XA_TIMESTAMP; | |
| 742 atomlist[2] = XA_STRING; | |
| 743 atomlist[3] = XA_UTF8_STRING; | |
| 744 XChangeProperty(xdpy, requestor, property, target, | |
| 745 32, PropModeReplace, | |
| 746 (unsigned char *)atomlist, sizeof(atomlist)/sizeof(Atom)); | |
| 747 } | |
| 748 | |
| 749 else if (target == XA_STRING) | |
| 750 { | |
| 751 XChangeProperty(xdpy, requestor, property, target, | |
| 752 8, PropModeReplace, | |
| 753 (unsigned char *)copylatin1, strlen(copylatin1)); | |
| 754 } | |
| 755 | |
| 756 else if (target == XA_UTF8_STRING) | |
| 757 { | |
| 758 XChangeProperty(xdpy, requestor, property, target, | |
| 759 8, PropModeReplace, | |
| 760 (unsigned char *)copyutf8, strlen(copyutf8)); | |
| 761 } | |
| 762 | |
| 763 else | |
| 764 { | |
| 765 nevt.xselection.property = None; | |
| 766 } | |
| 767 | |
| 768 XSendEvent(xdpy, requestor, False, 0, &nevt); | |
| 769 } | |
| 770 | |
| 771 void winreloadpage(pdfapp_t *app) | |
| 772 { | |
| 773 XEvent xev; | |
| 774 Display *dpy = XOpenDisplay(NULL); | |
| 775 | |
| 776 xev.xclient.type = ClientMessage; | |
| 777 xev.xclient.serial = 0; | |
| 778 xev.xclient.send_event = True; | |
| 779 xev.xclient.window = xwin; | |
| 780 xev.xclient.message_type = WM_RELOAD_PAGE; | |
| 781 xev.xclient.format = 32; | |
| 782 xev.xclient.data.l[0] = 0; | |
| 783 xev.xclient.data.l[1] = 0; | |
| 784 xev.xclient.data.l[2] = 0; | |
| 785 XSendEvent(dpy, xwin, 0, 0, &xev); | |
| 786 XCloseDisplay(dpy); | |
| 787 } | |
| 788 | |
| 789 void winopenuri(pdfapp_t *app, char *buf) | |
| 790 { | |
| 791 char *browser = getenv("BROWSER"); | |
| 792 pid_t pid; | |
| 793 if (!browser) | |
| 794 { | |
| 795 #ifdef __APPLE__ | |
| 796 browser = "open"; | |
| 797 #else | |
| 798 browser = "xdg-open"; | |
| 799 #endif | |
| 800 } | |
| 801 /* Fork once to start a child process that we wait on. This | |
| 802 * child process forks again and immediately exits. The | |
| 803 * grandchild process continues in the background. The purpose | |
| 804 * of this strange two-step is to avoid zombie processes. See | |
| 805 * bug 695701 for an explanation. */ | |
| 806 pid = fork(); | |
| 807 if (pid == 0) | |
| 808 { | |
| 809 if (fork() == 0) | |
| 810 { | |
| 811 execlp(browser, browser, buf, (char*)0); | |
| 812 fprintf(stderr, "cannot exec '%s'\n", browser); | |
| 813 } | |
| 814 _exit(0); | |
| 815 } | |
| 816 waitpid(pid, NULL, 0); | |
| 817 } | |
| 818 | |
| 819 int winquery(pdfapp_t *app, const char *query) | |
| 820 { | |
| 821 return QUERY_NO; | |
| 822 } | |
| 823 | |
| 824 int wingetcertpath(pdfapp_t *app, char *buf, int len) | |
| 825 { | |
| 826 return 0; | |
| 827 } | |
| 828 | |
| 829 static void onkey(int c, int modifiers) | |
| 830 { | |
| 831 advance_scheduled = 0; | |
| 832 | |
| 833 if (justcopied) | |
| 834 { | |
| 835 justcopied = 0; | |
| 836 winrepaint(&gapp); | |
| 837 } | |
| 838 | |
| 839 if (!gapp.issearching && c == 'P') | |
| 840 { | |
| 841 struct timeval now; | |
| 842 struct timeval t; | |
| 843 t.tv_sec = 2; | |
| 844 t.tv_usec = 0; | |
| 845 gettimeofday(&now, NULL); | |
| 846 timeradd(&now, &t, &tmo_at); | |
| 847 showingpage = 1; | |
| 848 winrepaint(&gapp); | |
| 849 return; | |
| 850 } | |
| 851 | |
| 852 pdfapp_onkey(&gapp, c, modifiers); | |
| 853 | |
| 854 if (gapp.issearching) | |
| 855 { | |
| 856 showingpage = 0; | |
| 857 showingmessage = 0; | |
| 858 } | |
| 859 } | |
| 860 | |
| 861 static void onmouse(int x, int y, int btn, int modifiers, int state) | |
| 862 { | |
| 863 if (state != 0) | |
| 864 advance_scheduled = 0; | |
| 865 | |
| 866 if (state != 0 && justcopied) | |
| 867 { | |
| 868 justcopied = 0; | |
| 869 winrepaint(&gapp); | |
| 870 } | |
| 871 | |
| 872 pdfapp_onmouse(&gapp, x, y, btn, modifiers, state); | |
| 873 } | |
| 874 | |
| 875 static void signal_handler(int signal) | |
| 876 { | |
| 877 if (signal == SIGHUP) | |
| 878 reloading = 1; | |
| 879 } | |
| 880 | |
| 881 static void usage(const char *argv0) | |
| 882 { | |
| 883 fprintf(stderr, "usage: %s [options] file.pdf [page]\n", argv0); | |
| 884 fprintf(stderr, "\t-p -\tpassword\n"); | |
| 885 fprintf(stderr, "\t-r -\tresolution\n"); | |
| 886 fprintf(stderr, "\t-A -\tset anti-aliasing quality in bits (0=off, 8=best)\n"); | |
| 887 fprintf(stderr, "\t-C -\tRRGGBB (tint color in hexadecimal syntax)\n"); | |
| 888 fprintf(stderr, "\t-W -\tpage width for EPUB layout\n"); | |
| 889 fprintf(stderr, "\t-H -\tpage height for EPUB layout\n"); | |
| 890 fprintf(stderr, "\t-I -\tinvert colors\n"); | |
| 891 fprintf(stderr, "\t-S -\tfont size for EPUB layout\n"); | |
| 892 fprintf(stderr, "\t-U -\tuser style sheet for EPUB layout\n"); | |
| 893 fprintf(stderr, "\t-X\tdisable document styles for EPUB layout\n"); | |
| 894 exit(1); | |
| 895 } | |
| 896 | |
| 897 int main(int argc, char **argv) | |
| 898 { | |
| 899 int c; | |
| 900 int len; | |
| 901 char buf[128]; | |
| 902 KeySym keysym; | |
| 903 int oldx = 0; | |
| 904 int oldy = 0; | |
| 905 int resolution = -1; | |
| 906 int pageno = 1; | |
| 907 fd_set fds; | |
| 908 int width = -1; | |
| 909 int height = -1; | |
| 910 fz_context *ctx; | |
| 911 struct timeval now; | |
| 912 struct timeval *timeout; | |
| 913 struct timeval tmo_advance_delay; | |
| 914 char *profile_name = NULL; | |
| 915 int kbps = 0; | |
| 916 | |
| 917 ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT); | |
| 918 if (!ctx) | |
| 919 { | |
| 920 fprintf(stderr, "cannot initialise context\n"); | |
| 921 exit(1); | |
| 922 } | |
| 923 | |
| 924 pdfapp_init(ctx, &gapp); | |
| 925 | |
| 926 while ((c = fz_getopt(argc, argv, "Ip:r:A:C:W:H:S:U:Xb:c:")) != -1) | |
| 927 { | |
| 928 switch (c) | |
| 929 { | |
| 930 case 'C': | |
| 931 c = strtol(fz_optarg, NULL, 16); | |
| 932 gapp.tint = 1; | |
| 933 gapp.tint_white = c; | |
| 934 break; | |
| 935 case 'p': password = fz_optarg; break; | |
| 936 case 'r': resolution = atoi(fz_optarg); break; | |
| 937 case 'I': gapp.invert = 1; break; | |
| 938 case 'A': fz_set_aa_level(ctx, atoi(fz_optarg)); break; | |
| 939 case 'c': profile_name = fz_optarg; break; | |
| 940 case 'W': gapp.layout_w = fz_atof(fz_optarg); break; | |
| 941 case 'H': gapp.layout_h = fz_atof(fz_optarg); break; | |
| 942 case 'S': gapp.layout_em = fz_atof(fz_optarg); break; | |
| 943 case 'U': gapp.layout_css = fz_optarg; break; | |
| 944 case 'X': gapp.layout_use_doc_css = 0; break; | |
| 945 case 'b': kbps = fz_atoi(fz_optarg); break; | |
| 946 default: usage(argv[0]); | |
| 947 } | |
| 948 } | |
| 949 | |
| 950 if (argc - fz_optind == 0) | |
| 951 usage(argv[0]); | |
| 952 | |
| 953 filename = argv[fz_optind++]; | |
| 954 | |
| 955 if (argc - fz_optind == 1) | |
| 956 pageno = atoi(argv[fz_optind++]); | |
| 957 | |
| 958 winopen(); | |
| 959 | |
| 960 if (resolution == -1) | |
| 961 resolution = winresolution(); | |
| 962 if (resolution < MINRES) | |
| 963 resolution = MINRES; | |
| 964 if (resolution > MAXRES) | |
| 965 resolution = MAXRES; | |
| 966 | |
| 967 gapp.transitions_enabled = 1; | |
| 968 gapp.scrw = DisplayWidth(xdpy, xscr); | |
| 969 gapp.scrh = DisplayHeight(xdpy, xscr); | |
| 970 gapp.default_resolution = resolution; | |
| 971 gapp.resolution = resolution; | |
| 972 gapp.pageno = pageno; | |
| 973 | |
| 974 if (profile_name) | |
| 975 pdfapp_load_profile(&gapp, profile_name); | |
| 976 | |
| 977 tmo_at.tv_sec = 0; | |
| 978 tmo_at.tv_usec = 0; | |
| 979 timeout = NULL; | |
| 980 | |
| 981 if (kbps) | |
| 982 pdfapp_open_progressive(&gapp, filename, 0, kbps); | |
| 983 else | |
| 984 pdfapp_open(&gapp, filename, 0); | |
| 985 | |
| 986 FD_ZERO(&fds); | |
| 987 | |
| 988 signal(SIGHUP, signal_handler); | |
| 989 | |
| 990 while (!closing) | |
| 991 { | |
| 992 while (!closing && XPending(xdpy) && !transition_dirty) | |
| 993 { | |
| 994 XNextEvent(xdpy, &xevt); | |
| 995 | |
| 996 switch (xevt.type) | |
| 997 { | |
| 998 case Expose: | |
| 999 dirty = 1; | |
| 1000 break; | |
| 1001 | |
| 1002 case ConfigureNotify: | |
| 1003 if (gapp.image) | |
| 1004 { | |
| 1005 if (xevt.xconfigure.width != reqw || | |
| 1006 xevt.xconfigure.height != reqh) | |
| 1007 gapp.shrinkwrap = 0; | |
| 1008 } | |
| 1009 width = xevt.xconfigure.width; | |
| 1010 height = xevt.xconfigure.height; | |
| 1011 | |
| 1012 break; | |
| 1013 | |
| 1014 case KeyPress: | |
| 1015 len = XLookupString(&xevt.xkey, buf, sizeof buf, &keysym, NULL); | |
| 1016 | |
| 1017 if (!gapp.issearching) | |
| 1018 switch (keysym) | |
| 1019 { | |
| 1020 case XK_Escape: | |
| 1021 len = 1; buf[0] = '\033'; | |
| 1022 break; | |
| 1023 | |
| 1024 case XK_Up: | |
| 1025 case XK_KP_Up: | |
| 1026 len = 1; buf[0] = 'k'; | |
| 1027 break; | |
| 1028 case XK_Down: | |
| 1029 case XK_KP_Down: | |
| 1030 len = 1; buf[0] = 'j'; | |
| 1031 break; | |
| 1032 | |
| 1033 case XK_Left: | |
| 1034 case XK_KP_Left: | |
| 1035 len = 1; buf[0] = 'h'; | |
| 1036 break; | |
| 1037 case XK_Right: | |
| 1038 case XK_KP_Right: | |
| 1039 len = 1; buf[0] = 'l'; | |
| 1040 break; | |
| 1041 | |
| 1042 case XK_Page_Up: | |
| 1043 case XK_KP_Page_Up: | |
| 1044 case XF86XK_Back: | |
| 1045 len = 1; buf[0] = ','; | |
| 1046 break; | |
| 1047 case XK_Page_Down: | |
| 1048 case XK_KP_Page_Down: | |
| 1049 case XF86XK_Forward: | |
| 1050 len = 1; buf[0] = '.'; | |
| 1051 break; | |
| 1052 } | |
| 1053 if (xevt.xkey.state & ControlMask && keysym == XK_c) | |
| 1054 docopy(&gapp, XA_CLIPBOARD); | |
| 1055 else if (len) | |
| 1056 onkey(buf[0], xevt.xkey.state); | |
| 1057 | |
| 1058 onmouse(oldx, oldy, 0, 0, 0); | |
| 1059 | |
| 1060 break; | |
| 1061 | |
| 1062 case MotionNotify: | |
| 1063 oldx = xevt.xmotion.x; | |
| 1064 oldy = xevt.xmotion.y; | |
| 1065 onmouse(xevt.xmotion.x, xevt.xmotion.y, 0, xevt.xmotion.state, 0); | |
| 1066 break; | |
| 1067 | |
| 1068 case ButtonPress: | |
| 1069 onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, xevt.xbutton.state, 1); | |
| 1070 break; | |
| 1071 | |
| 1072 case ButtonRelease: | |
| 1073 copytime = xevt.xbutton.time; | |
| 1074 onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, xevt.xbutton.state, -1); | |
| 1075 break; | |
| 1076 | |
| 1077 case SelectionRequest: | |
| 1078 onselreq(xevt.xselectionrequest.requestor, | |
| 1079 xevt.xselectionrequest.selection, | |
| 1080 xevt.xselectionrequest.target, | |
| 1081 xevt.xselectionrequest.property, | |
| 1082 xevt.xselectionrequest.time); | |
| 1083 break; | |
| 1084 | |
| 1085 case ClientMessage: | |
| 1086 if (xevt.xclient.message_type == WM_RELOAD_PAGE) | |
| 1087 pdfapp_reloadpage(&gapp); | |
| 1088 else if (xevt.xclient.format == 32 && ((Atom) xevt.xclient.data.l[0]) == WM_DELETE_WINDOW) | |
| 1089 closing = 1; | |
| 1090 break; | |
| 1091 } | |
| 1092 } | |
| 1093 | |
| 1094 if (closing) | |
| 1095 continue; | |
| 1096 | |
| 1097 if (width != -1 || height != -1) | |
| 1098 { | |
| 1099 pdfapp_onresize(&gapp, width, height); | |
| 1100 width = -1; | |
| 1101 height = -1; | |
| 1102 } | |
| 1103 | |
| 1104 if (dirty || dirtysearch) | |
| 1105 { | |
| 1106 if (dirty) | |
| 1107 winblit(&gapp); | |
| 1108 else if (dirtysearch) | |
| 1109 winblitstatusbar(&gapp); | |
| 1110 dirty = 0; | |
| 1111 transition_dirty = 0; | |
| 1112 dirtysearch = 0; | |
| 1113 pdfapp_postblit(&gapp); | |
| 1114 } | |
| 1115 | |
| 1116 if (!showingpage && !showingmessage && (tmo_at.tv_sec || tmo_at.tv_usec)) | |
| 1117 { | |
| 1118 tmo_at.tv_sec = 0; | |
| 1119 tmo_at.tv_usec = 0; | |
| 1120 timeout = NULL; | |
| 1121 } | |
| 1122 | |
| 1123 if (XPending(xdpy) || transition_dirty) | |
| 1124 continue; | |
| 1125 | |
| 1126 timeout = NULL; | |
| 1127 | |
| 1128 if (tmo_at.tv_sec || tmo_at.tv_usec) | |
| 1129 { | |
| 1130 gettimeofday(&now, NULL); | |
| 1131 timersub(&tmo_at, &now, &tmo); | |
| 1132 if (tmo.tv_sec <= 0) | |
| 1133 { | |
| 1134 tmo_at.tv_sec = 0; | |
| 1135 tmo_at.tv_usec = 0; | |
| 1136 timeout = NULL; | |
| 1137 showingpage = 0; | |
| 1138 showingmessage = 0; | |
| 1139 winrepaint(&gapp); | |
| 1140 } | |
| 1141 else | |
| 1142 timeout = &tmo; | |
| 1143 } | |
| 1144 | |
| 1145 if (advance_scheduled) | |
| 1146 { | |
| 1147 gettimeofday(&now, NULL); | |
| 1148 timersub(&tmo_advance, &now, &tmo_advance_delay); | |
| 1149 if (tmo_advance_delay.tv_sec <= 0) | |
| 1150 { | |
| 1151 /* Too late already */ | |
| 1152 onkey(' ', 0); | |
| 1153 onmouse(oldx, oldy, 0, 0, 0); | |
| 1154 advance_scheduled = 0; | |
| 1155 } | |
| 1156 else if (timeout == NULL) | |
| 1157 { | |
| 1158 timeout = &tmo_advance_delay; | |
| 1159 } | |
| 1160 else | |
| 1161 { | |
| 1162 struct timeval tmp; | |
| 1163 timersub(&tmo_advance_delay, timeout, &tmp); | |
| 1164 if (tmp.tv_sec < 0) | |
| 1165 { | |
| 1166 timeout = &tmo_advance_delay; | |
| 1167 } | |
| 1168 } | |
| 1169 } | |
| 1170 | |
| 1171 FD_SET(x11fd, &fds); | |
| 1172 if (select(x11fd + 1, &fds, NULL, NULL, timeout) < 0) | |
| 1173 { | |
| 1174 if (reloading) | |
| 1175 { | |
| 1176 pdfapp_reloadfile(&gapp); | |
| 1177 reloading = 0; | |
| 1178 } | |
| 1179 } | |
| 1180 if (!FD_ISSET(x11fd, &fds)) | |
| 1181 { | |
| 1182 if (timeout == &tmo_advance_delay) | |
| 1183 { | |
| 1184 onkey(' ', 0); | |
| 1185 onmouse(oldx, oldy, 0, 0, 0); | |
| 1186 advance_scheduled = 0; | |
| 1187 } | |
| 1188 else | |
| 1189 { | |
| 1190 tmo_at.tv_sec = 0; | |
| 1191 tmo_at.tv_usec = 0; | |
| 1192 timeout = NULL; | |
| 1193 showingpage = 0; | |
| 1194 showingmessage = 0; | |
| 1195 winrepaint(&gapp); | |
| 1196 } | |
| 1197 } | |
| 1198 } | |
| 1199 | |
| 1200 cleanup(&gapp); | |
| 1201 | |
| 1202 return 0; | |
| 1203 } |
