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