| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203 |
- // Copyright (C) 2004-2025 Artifex Software, Inc.
- //
- // This file is part of MuPDF.
- //
- // MuPDF is free software: you can redistribute it and/or modify it under the
- // terms of the GNU Affero General Public License as published by the Free
- // Software Foundation, either version 3 of the License, or (at your option)
- // any later version.
- //
- // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
- // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
- // details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
- //
- // Alternative licensing terms are available from the licensor.
- // For commercial licensing, see <https://www.artifex.com/> or contact
- // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
- // CA 94129, USA, for further information.
- #include "pdfapp.h"
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- #include <X11/Xatom.h>
- #include <X11/cursorfont.h>
- #include <X11/keysym.h>
- #include <X11/XF86keysym.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <sys/select.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <signal.h>
- #include <limits.h>
- #define mupdf_icon_bitmap_16_width 16
- #define mupdf_icon_bitmap_16_height 16
- static unsigned char mupdf_icon_bitmap_16_bits[] = {
- 0x00, 0x00, 0x00, 0x1e, 0x00, 0x2b, 0x80, 0x55, 0x8c, 0x62, 0x8c, 0x51,
- 0x9c, 0x61, 0x1c, 0x35, 0x3c, 0x1f, 0x3c, 0x0f, 0xfc, 0x0f, 0xec, 0x0d,
- 0xec, 0x0d, 0xcc, 0x0c, 0xcc, 0x0c, 0x00, 0x00 };
- #define mupdf_icon_bitmap_16_mask_width 16
- #define mupdf_icon_bitmap_16_mask_height 16
- static unsigned char mupdf_icon_bitmap_16_mask_bits[] = {
- 0x00, 0x1e, 0x00, 0x3f, 0x80, 0x7f, 0xce, 0xff, 0xde, 0xff, 0xde, 0xff,
- 0xfe, 0xff, 0xfe, 0x7f, 0xfe, 0x3f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
- 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xce, 0x1c };
- #ifndef timeradd
- #define timeradd(a, b, result) \
- do { \
- (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
- (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
- if ((result)->tv_usec >= 1000000) \
- { \
- ++(result)->tv_sec; \
- (result)->tv_usec -= 1000000; \
- } \
- } while (0)
- #endif
- #ifndef timersub
- #define timersub(a, b, result) \
- do { \
- (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
- (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
- if ((result)->tv_usec < 0) { \
- --(result)->tv_sec; \
- (result)->tv_usec += 1000000; \
- } \
- } while (0)
- #endif
- extern int ximage_init(Display *display, int screen, Visual *visual);
- extern int ximage_get_depth(void);
- extern Visual *ximage_get_visual(void);
- extern Colormap ximage_get_colormap(void);
- extern void ximage_blit(Drawable d, GC gc, int dstx, int dsty,
- unsigned char *srcdata,
- int srcx, int srcy, int srcw, int srch, int srcstride);
- static void windrawstringxor(pdfapp_t *app, int x, int y, char *s);
- static void cleanup(pdfapp_t *app);
- static Display *xdpy;
- static Atom XA_CLIPBOARD;
- static Atom XA_TARGETS;
- static Atom XA_TIMESTAMP;
- static Atom XA_UTF8_STRING;
- static Atom WM_DELETE_WINDOW;
- static Atom NET_WM_NAME;
- static Atom NET_WM_STATE;
- static Atom NET_WM_STATE_FULLSCREEN;
- static Atom WM_RELOAD_PAGE;
- static int x11fd;
- static int xscr;
- static Window xwin;
- static Pixmap xicon, xmask;
- static GC xgc;
- static XEvent xevt;
- static int mapped = 0;
- static Cursor xcarrow, xchand, xcwait, xccaret;
- static int justcopied = 0;
- static int dirty = 0;
- static int transition_dirty = 0;
- static int dirtysearch = 0;
- static char *password = "";
- static XColor xbgcolor;
- static int reqw = 0;
- static int reqh = 0;
- static char copylatin1[1024 * 16] = "";
- static char copyutf8[1024 * 48] = "";
- static Time copytime;
- static char *filename;
- static char message[1024] = "";
- static pdfapp_t gapp;
- static int closing = 0;
- static int reloading = 0;
- static int showingpage = 0;
- static int showingmessage = 0;
- static int advance_scheduled = 0;
- static struct timeval tmo;
- static struct timeval tmo_advance;
- static struct timeval tmo_at;
- /*
- * Dialog boxes
- */
- static void showmessage(pdfapp_t *app, int timeout, char *msg)
- {
- struct timeval now;
- showingmessage = 1;
- showingpage = 0;
- fz_strlcpy(message, msg, sizeof message);
- if ((!tmo_at.tv_sec && !tmo_at.tv_usec) || tmo.tv_sec < timeout)
- {
- tmo.tv_sec = timeout;
- tmo.tv_usec = 0;
- gettimeofday(&now, NULL);
- timeradd(&now, &tmo, &tmo_at);
- }
- }
- void winerror(pdfapp_t *app, char *msg)
- {
- fprintf(stderr, "mupdf: error: %s\n", msg);
- cleanup(app);
- exit(1);
- }
- void winwarn(pdfapp_t *app, char *msg)
- {
- char buf[1024];
- snprintf(buf, sizeof buf, "warning: %s", msg);
- showmessage(app, 10, buf);
- fprintf(stderr, "mupdf: %s\n", buf);
- }
- void winalert(pdfapp_t *app, pdf_alert_event *alert)
- {
- char buf[1024];
- snprintf(buf, sizeof buf, "Alert %s: %s", alert->title, alert->message);
- fprintf(stderr, "%s\n", buf);
- switch (alert->button_group_type)
- {
- case PDF_ALERT_BUTTON_GROUP_OK:
- case PDF_ALERT_BUTTON_GROUP_OK_CANCEL:
- alert->button_pressed = PDF_ALERT_BUTTON_OK;
- break;
- case PDF_ALERT_BUTTON_GROUP_YES_NO:
- case PDF_ALERT_BUTTON_GROUP_YES_NO_CANCEL:
- alert->button_pressed = PDF_ALERT_BUTTON_YES;
- break;
- }
- }
- void winprint(pdfapp_t *app)
- {
- fprintf(stderr, "The MuPDF library supports printing, but this application currently does not\n");
- }
- char *winpassword(pdfapp_t *app, char *fname)
- {
- char *r = password;
- password = NULL;
- return r;
- }
- char *wintextinput(pdfapp_t *app, char *inittext, int retry)
- {
- /* We don't support text input on the x11 viewer */
- return NULL;
- }
- int winchoiceinput(pdfapp_t *app, int nopts, const char *opts[], int *nvals, const char *vals[])
- {
- /* FIXME: temporary dummy implementation */
- return 0;
- }
- /*
- * X11 magic
- */
- static void winopen(void)
- {
- XWMHints *wmhints;
- XClassHint *classhint;
- #ifdef HAVE_CURL
- if (!XInitThreads())
- fz_throw(gapp.ctx, FZ_ERROR_GENERIC, "cannot initialize X11 for multi-threading");
- #endif
- xdpy = XOpenDisplay(NULL);
- if (!xdpy)
- fz_throw(gapp.ctx, FZ_ERROR_GENERIC, "cannot open display");
- XA_CLIPBOARD = XInternAtom(xdpy, "CLIPBOARD", False);
- XA_TARGETS = XInternAtom(xdpy, "TARGETS", False);
- XA_TIMESTAMP = XInternAtom(xdpy, "TIMESTAMP", False);
- XA_UTF8_STRING = XInternAtom(xdpy, "UTF8_STRING", False);
- WM_DELETE_WINDOW = XInternAtom(xdpy, "WM_DELETE_WINDOW", False);
- NET_WM_NAME = XInternAtom(xdpy, "_NET_WM_NAME", False);
- NET_WM_STATE = XInternAtom(xdpy, "_NET_WM_STATE", False);
- NET_WM_STATE_FULLSCREEN = XInternAtom(xdpy, "_NET_WM_STATE_FULLSCREEN", False);
- WM_RELOAD_PAGE = XInternAtom(xdpy, "_WM_RELOAD_PAGE", False);
- xscr = DefaultScreen(xdpy);
- ximage_init(xdpy, xscr, DefaultVisual(xdpy, xscr));
- xcarrow = XCreateFontCursor(xdpy, XC_left_ptr);
- xchand = XCreateFontCursor(xdpy, XC_hand2);
- xcwait = XCreateFontCursor(xdpy, XC_watch);
- xccaret = XCreateFontCursor(xdpy, XC_xterm);
- xbgcolor.red = 0x7000;
- xbgcolor.green = 0x7000;
- xbgcolor.blue = 0x7000;
- XAllocColor(xdpy, DefaultColormap(xdpy, xscr), &xbgcolor);
- xwin = XCreateWindow(xdpy, DefaultRootWindow(xdpy),
- 10, 10, 200, 100, 0,
- ximage_get_depth(),
- InputOutput,
- ximage_get_visual(),
- 0,
- NULL);
- if (xwin == None)
- fz_throw(gapp.ctx, FZ_ERROR_GENERIC, "cannot create window");
- XSetWindowColormap(xdpy, xwin, ximage_get_colormap());
- XSelectInput(xdpy, xwin,
- StructureNotifyMask | ExposureMask | KeyPressMask |
- PointerMotionMask | ButtonPressMask | ButtonReleaseMask);
- mapped = 0;
- xgc = XCreateGC(xdpy, xwin, 0, NULL);
- XDefineCursor(xdpy, xwin, xcarrow);
- wmhints = XAllocWMHints();
- if (wmhints)
- {
- wmhints->flags = IconPixmapHint | IconMaskHint;
- xicon = XCreateBitmapFromData(xdpy, xwin,
- (char*)mupdf_icon_bitmap_16_bits,
- mupdf_icon_bitmap_16_width,
- mupdf_icon_bitmap_16_height);
- xmask = XCreateBitmapFromData(xdpy, xwin,
- (char*)mupdf_icon_bitmap_16_mask_bits,
- mupdf_icon_bitmap_16_mask_width,
- mupdf_icon_bitmap_16_mask_height);
- if (xicon && xmask)
- {
- wmhints->icon_pixmap = xicon;
- wmhints->icon_mask = xmask;
- XSetWMHints(xdpy, xwin, wmhints);
- }
- XFree(wmhints);
- }
- classhint = XAllocClassHint();
- if (classhint)
- {
- classhint->res_name = "mupdf";
- classhint->res_class = "MuPDF";
- XSetClassHint(xdpy, xwin, classhint);
- XFree(classhint);
- }
- XSetWMProtocols(xdpy, xwin, &WM_DELETE_WINDOW, 1);
- x11fd = ConnectionNumber(xdpy);
- }
- void winclose(pdfapp_t *app)
- {
- if (pdfapp_preclose(app))
- {
- closing = 1;
- }
- }
- int winsavequery(pdfapp_t *app)
- {
- fprintf(stderr, "mupdf: discarded changes to document\n");
- /* FIXME: temporary dummy implementation */
- return DISCARD;
- }
- int wingetsavepath(pdfapp_t *app, char *buf, int len)
- {
- /* FIXME: temporary dummy implementation */
- return 0;
- }
- void winreplacefile(pdfapp_t *app, char *source, char *target)
- {
- if (rename(source, target) == -1)
- pdfapp_warn(app, "unable to rename file");
- }
- void wincopyfile(pdfapp_t *app, char *source, char *target)
- {
- FILE *in, *out;
- char buf[32 << 10];
- size_t n;
- in = fopen(source, "rb");
- if (!in)
- {
- pdfapp_error(app, "cannot open source file for copying");
- return;
- }
- out = fopen(target, "wb");
- if (!out)
- {
- pdfapp_error(app, "cannot open target file for copying");
- fclose(in);
- return;
- }
- for (;;)
- {
- n = fread(buf, 1, sizeof buf, in);
- fwrite(buf, 1, n, out);
- if (n < sizeof buf)
- {
- if (ferror(in))
- pdfapp_error(app, "cannot read data from source file");
- break;
- }
- }
- fclose(out);
- fclose(in);
- }
- static void cleanup(pdfapp_t *app)
- {
- fz_context *ctx = app->ctx;
- pdfapp_close(app);
- XDestroyWindow(xdpy, xwin);
- XFreePixmap(xdpy, xicon);
- XFreeCursor(xdpy, xccaret);
- XFreeCursor(xdpy, xcwait);
- XFreeCursor(xdpy, xchand);
- XFreeCursor(xdpy, xcarrow);
- XFreeGC(xdpy, xgc);
- XCloseDisplay(xdpy);
- fz_drop_context(ctx);
- }
- static int winresolution(void)
- {
- return DisplayWidth(xdpy, xscr) * 25.4f /
- DisplayWidthMM(xdpy, xscr) + 0.5f;
- }
- void wincursor(pdfapp_t *app, int curs)
- {
- if (curs == ARROW)
- XDefineCursor(xdpy, xwin, xcarrow);
- if (curs == HAND)
- XDefineCursor(xdpy, xwin, xchand);
- if (curs == WAIT)
- XDefineCursor(xdpy, xwin, xcwait);
- if (curs == CARET)
- XDefineCursor(xdpy, xwin, xccaret);
- XFlush(xdpy);
- }
- void wintitle(pdfapp_t *app, char *s)
- {
- XStoreName(xdpy, xwin, s);
- #ifdef X_HAVE_UTF8_STRING
- Xutf8SetWMProperties(xdpy, xwin, s, s, NULL, 0, NULL, NULL, NULL);
- #else
- XmbSetWMProperties(xdpy, xwin, s, s, NULL, 0, NULL, NULL, NULL);
- #endif
- XChangeProperty(xdpy, xwin, NET_WM_NAME, XA_UTF8_STRING, 8,
- PropModeReplace, (unsigned char *)s, strlen(s));
- }
- void winhelp(pdfapp_t *app)
- {
- fprintf(stderr, "%s\n%s", pdfapp_version(app), pdfapp_usage(app));
- }
- void winresize(pdfapp_t *app, int w, int h)
- {
- int image_w = gapp.layout_w;
- int image_h = gapp.layout_h;
- XWindowChanges values;
- int mask, width, height;
- if (gapp.image)
- {
- image_w = fz_pixmap_width(gapp.ctx, gapp.image);
- image_h = fz_pixmap_height(gapp.ctx, gapp.image);
- }
- mask = CWWidth | CWHeight;
- values.width = w;
- values.height = h;
- XConfigureWindow(xdpy, xwin, mask, &values);
- reqw = w;
- reqh = h;
- if (!mapped)
- {
- gapp.winw = w;
- gapp.winh = h;
- width = -1;
- height = -1;
- XMapWindow(xdpy, xwin);
- XFlush(xdpy);
- while (1)
- {
- XNextEvent(xdpy, &xevt);
- if (xevt.type == ConfigureNotify)
- {
- width = xevt.xconfigure.width;
- height = xevt.xconfigure.height;
- }
- if (xevt.type == MapNotify)
- break;
- }
- XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr));
- XFillRectangle(xdpy, xwin, xgc, 0, 0, image_w, image_h);
- XFlush(xdpy);
- if (width != reqw || height != reqh)
- {
- gapp.shrinkwrap = 0;
- dirty = 1;
- pdfapp_onresize(&gapp, width, height);
- }
- mapped = 1;
- }
- }
- void winfullscreen(pdfapp_t *app, int state)
- {
- XEvent xev;
- xev.xclient.type = ClientMessage;
- xev.xclient.serial = 0;
- xev.xclient.send_event = True;
- xev.xclient.window = xwin;
- xev.xclient.message_type = NET_WM_STATE;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = state;
- xev.xclient.data.l[1] = NET_WM_STATE_FULLSCREEN;
- xev.xclient.data.l[2] = 0;
- XSendEvent(xdpy, DefaultRootWindow(xdpy), False,
- SubstructureRedirectMask | SubstructureNotifyMask,
- &xev);
- }
- static void fillrect(int x, int y, int w, int h)
- {
- if (w > 0 && h > 0)
- XFillRectangle(xdpy, xwin, xgc, x, y, w, h);
- }
- static void winblitstatusbar(pdfapp_t *app)
- {
- if (gapp.issearching)
- {
- char buf[sizeof(gapp.search) + 50];
- sprintf(buf, "Search: %s", gapp.search);
- XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr));
- fillrect(0, 0, gapp.winw, 30);
- windrawstring(&gapp, 10, 20, buf);
- }
- else if (showingmessage)
- {
- XSetForeground(xdpy, xgc, WhitePixel(xdpy, xscr));
- fillrect(0, 0, gapp.winw, 30);
- windrawstring(&gapp, 10, 20, message);
- }
- else if (showingpage)
- {
- char buf[42];
- snprintf(buf, sizeof buf, "Page %d/%d", gapp.pageno, gapp.pagecount);
- windrawstringxor(&gapp, 10, 20, buf);
- }
- }
- int winisresolutionacceptable(pdfapp_t *app, fz_matrix ctm)
- {
- fz_rect bounds;
- fz_irect ibounds;
- int w, h;
- bounds = fz_transform_rect(app->page_bbox, ctm);
- ibounds = fz_round_rect(bounds);
- w = ibounds.x1 - ibounds.x0;
- h = ibounds.y1 - ibounds.y0;
- return w < (INT_MAX / 4) / h;
- }
- static void winblit(pdfapp_t *app)
- {
- if (gapp.image)
- {
- int image_w = fz_pixmap_width(gapp.ctx, gapp.image);
- int image_h = fz_pixmap_height(gapp.ctx, gapp.image);
- int image_n = fz_pixmap_components(gapp.ctx, gapp.image);
- unsigned char *image_samples = fz_pixmap_samples(gapp.ctx, gapp.image);
- int x0 = gapp.panx;
- int y0 = gapp.pany;
- int x1 = gapp.panx + image_w;
- int y1 = gapp.pany + image_h;
- if (app->invert)
- XSetForeground(xdpy, xgc, BlackPixel(xdpy, DefaultScreen(xdpy)));
- else
- XSetForeground(xdpy, xgc, xbgcolor.pixel);
- fillrect(0, 0, x0, gapp.winh);
- fillrect(x1, 0, gapp.winw - x1, gapp.winh);
- fillrect(0, 0, gapp.winw, y0);
- fillrect(0, y1, gapp.winw, gapp.winh - y1);
- if (gapp.iscopying || justcopied)
- {
- pdfapp_invert(&gapp, gapp.selr);
- justcopied = 1;
- }
- pdfapp_inverthit(&gapp);
- if (image_n == 4)
- ximage_blit(xwin, xgc,
- x0, y0,
- image_samples,
- 0, 0,
- image_w,
- image_h,
- image_w * image_n);
- else if (image_n == 2)
- {
- int i = image_w*image_h;
- unsigned char *color = malloc(i*4);
- if (color)
- {
- unsigned char *s = image_samples;
- unsigned char *d = color;
- for (; i > 0 ; i--)
- {
- d[2] = d[1] = d[0] = *s++;
- d[3] = *s++;
- d += 4;
- }
- ximage_blit(xwin, xgc,
- x0, y0,
- color,
- 0, 0,
- image_w,
- image_h,
- image_w * 4);
- free(color);
- }
- }
- pdfapp_inverthit(&gapp);
- if (gapp.iscopying || justcopied)
- {
- pdfapp_invert(&gapp, gapp.selr);
- justcopied = 1;
- }
- }
- else
- {
- XSetForeground(xdpy, xgc, xbgcolor.pixel);
- fillrect(0, 0, gapp.winw, gapp.winh);
- }
- winblitstatusbar(app);
- }
- void winrepaint(pdfapp_t *app)
- {
- dirty = 1;
- if (app->in_transit)
- transition_dirty = 1;
- }
- void winrepaintsearch(pdfapp_t *app)
- {
- dirtysearch = 1;
- }
- void winadvancetimer(pdfapp_t *app, float duration)
- {
- struct timeval now;
- gettimeofday(&now, NULL);
- memset(&tmo_advance, 0, sizeof(tmo_advance));
- tmo_advance.tv_sec = (int)duration;
- tmo_advance.tv_usec = 1000000 * (duration - tmo_advance.tv_sec);
- timeradd(&tmo_advance, &now, &tmo_advance);
- advance_scheduled = 1;
- }
- static void windrawstringxor(pdfapp_t *app, int x, int y, char *s)
- {
- int prevfunction;
- XGCValues xgcv;
- XGetGCValues(xdpy, xgc, GCFunction, &xgcv);
- prevfunction = xgcv.function;
- xgcv.function = GXxor;
- XChangeGC(xdpy, xgc, GCFunction, &xgcv);
- XSetForeground(xdpy, xgc, WhitePixel(xdpy, DefaultScreen(xdpy)));
- XDrawString(xdpy, xwin, xgc, x, y, s, strlen(s));
- XFlush(xdpy);
- XGetGCValues(xdpy, xgc, GCFunction, &xgcv);
- xgcv.function = prevfunction;
- XChangeGC(xdpy, xgc, GCFunction, &xgcv);
- }
- void windrawstring(pdfapp_t *app, int x, int y, char *s)
- {
- XSetForeground(xdpy, xgc, BlackPixel(xdpy, DefaultScreen(xdpy)));
- XDrawString(xdpy, xwin, xgc, x, y, s, strlen(s));
- }
- static void docopy(pdfapp_t *app, Atom copy_target)
- {
- unsigned short copyucs2[16 * 1024];
- char *latin1 = copylatin1;
- char *utf8 = copyutf8;
- unsigned short *ucs2;
- int ucs;
- pdfapp_oncopy(&gapp, copyucs2, 16 * 1024);
- for (ucs2 = copyucs2; ucs2[0] != 0; ucs2++)
- {
- ucs = ucs2[0];
- utf8 += fz_runetochar(utf8, ucs);
- if (ucs < 256)
- *latin1++ = ucs;
- else
- *latin1++ = '?';
- }
- *utf8 = 0;
- *latin1 = 0;
- XSetSelectionOwner(xdpy, copy_target, xwin, copytime);
- justcopied = 1;
- }
- void windocopy(pdfapp_t *app)
- {
- docopy(app, XA_PRIMARY);
- }
- static void onselreq(Window requestor, Atom selection, Atom target, Atom property, Time time)
- {
- XEvent nevt;
- advance_scheduled = 0;
- if (property == None)
- property = target;
- nevt.xselection.type = SelectionNotify;
- nevt.xselection.send_event = True;
- nevt.xselection.display = xdpy;
- nevt.xselection.requestor = requestor;
- nevt.xselection.selection = selection;
- nevt.xselection.target = target;
- nevt.xselection.property = property;
- nevt.xselection.time = time;
- if (target == XA_TARGETS)
- {
- Atom atomlist[4];
- atomlist[0] = XA_TARGETS;
- atomlist[1] = XA_TIMESTAMP;
- atomlist[2] = XA_STRING;
- atomlist[3] = XA_UTF8_STRING;
- XChangeProperty(xdpy, requestor, property, target,
- 32, PropModeReplace,
- (unsigned char *)atomlist, sizeof(atomlist)/sizeof(Atom));
- }
- else if (target == XA_STRING)
- {
- XChangeProperty(xdpy, requestor, property, target,
- 8, PropModeReplace,
- (unsigned char *)copylatin1, strlen(copylatin1));
- }
- else if (target == XA_UTF8_STRING)
- {
- XChangeProperty(xdpy, requestor, property, target,
- 8, PropModeReplace,
- (unsigned char *)copyutf8, strlen(copyutf8));
- }
- else
- {
- nevt.xselection.property = None;
- }
- XSendEvent(xdpy, requestor, False, 0, &nevt);
- }
- void winreloadpage(pdfapp_t *app)
- {
- XEvent xev;
- Display *dpy = XOpenDisplay(NULL);
- xev.xclient.type = ClientMessage;
- xev.xclient.serial = 0;
- xev.xclient.send_event = True;
- xev.xclient.window = xwin;
- xev.xclient.message_type = WM_RELOAD_PAGE;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = 0;
- xev.xclient.data.l[1] = 0;
- xev.xclient.data.l[2] = 0;
- XSendEvent(dpy, xwin, 0, 0, &xev);
- XCloseDisplay(dpy);
- }
- void winopenuri(pdfapp_t *app, char *buf)
- {
- char *browser = getenv("BROWSER");
- pid_t pid;
- if (!browser)
- {
- #ifdef __APPLE__
- browser = "open";
- #else
- browser = "xdg-open";
- #endif
- }
- /* Fork once to start a child process that we wait on. This
- * child process forks again and immediately exits. The
- * grandchild process continues in the background. The purpose
- * of this strange two-step is to avoid zombie processes. See
- * bug 695701 for an explanation. */
- pid = fork();
- if (pid == 0)
- {
- if (fork() == 0)
- {
- execlp(browser, browser, buf, (char*)0);
- fprintf(stderr, "cannot exec '%s'\n", browser);
- }
- _exit(0);
- }
- waitpid(pid, NULL, 0);
- }
- int winquery(pdfapp_t *app, const char *query)
- {
- return QUERY_NO;
- }
- int wingetcertpath(pdfapp_t *app, char *buf, int len)
- {
- return 0;
- }
- static void onkey(int c, int modifiers)
- {
- advance_scheduled = 0;
- if (justcopied)
- {
- justcopied = 0;
- winrepaint(&gapp);
- }
- if (!gapp.issearching && c == 'P')
- {
- struct timeval now;
- struct timeval t;
- t.tv_sec = 2;
- t.tv_usec = 0;
- gettimeofday(&now, NULL);
- timeradd(&now, &t, &tmo_at);
- showingpage = 1;
- winrepaint(&gapp);
- return;
- }
- pdfapp_onkey(&gapp, c, modifiers);
- if (gapp.issearching)
- {
- showingpage = 0;
- showingmessage = 0;
- }
- }
- static void onmouse(int x, int y, int btn, int modifiers, int state)
- {
- if (state != 0)
- advance_scheduled = 0;
- if (state != 0 && justcopied)
- {
- justcopied = 0;
- winrepaint(&gapp);
- }
- pdfapp_onmouse(&gapp, x, y, btn, modifiers, state);
- }
- static void signal_handler(int signal)
- {
- if (signal == SIGHUP)
- reloading = 1;
- }
- static void usage(const char *argv0)
- {
- fprintf(stderr, "usage: %s [options] file.pdf [page]\n", argv0);
- fprintf(stderr, "\t-p -\tpassword\n");
- fprintf(stderr, "\t-r -\tresolution\n");
- fprintf(stderr, "\t-A -\tset anti-aliasing quality in bits (0=off, 8=best)\n");
- fprintf(stderr, "\t-C -\tRRGGBB (tint color in hexadecimal syntax)\n");
- fprintf(stderr, "\t-W -\tpage width for EPUB layout\n");
- fprintf(stderr, "\t-H -\tpage height for EPUB layout\n");
- fprintf(stderr, "\t-I -\tinvert colors\n");
- fprintf(stderr, "\t-S -\tfont size for EPUB layout\n");
- fprintf(stderr, "\t-U -\tuser style sheet for EPUB layout\n");
- fprintf(stderr, "\t-X\tdisable document styles for EPUB layout\n");
- exit(1);
- }
- int main(int argc, char **argv)
- {
- int c;
- int len;
- char buf[128];
- KeySym keysym;
- int oldx = 0;
- int oldy = 0;
- int resolution = -1;
- int pageno = 1;
- fd_set fds;
- int width = -1;
- int height = -1;
- fz_context *ctx;
- struct timeval now;
- struct timeval *timeout;
- struct timeval tmo_advance_delay;
- char *profile_name = NULL;
- int kbps = 0;
- ctx = fz_new_context(NULL, NULL, FZ_STORE_DEFAULT);
- if (!ctx)
- {
- fprintf(stderr, "cannot initialise context\n");
- exit(1);
- }
- pdfapp_init(ctx, &gapp);
- while ((c = fz_getopt(argc, argv, "Ip:r:A:C:W:H:S:U:Xb:c:")) != -1)
- {
- switch (c)
- {
- case 'C':
- c = strtol(fz_optarg, NULL, 16);
- gapp.tint = 1;
- gapp.tint_white = c;
- break;
- case 'p': password = fz_optarg; break;
- case 'r': resolution = atoi(fz_optarg); break;
- case 'I': gapp.invert = 1; break;
- case 'A': fz_set_aa_level(ctx, atoi(fz_optarg)); break;
- case 'c': profile_name = fz_optarg; break;
- case 'W': gapp.layout_w = fz_atof(fz_optarg); break;
- case 'H': gapp.layout_h = fz_atof(fz_optarg); break;
- case 'S': gapp.layout_em = fz_atof(fz_optarg); break;
- case 'U': gapp.layout_css = fz_optarg; break;
- case 'X': gapp.layout_use_doc_css = 0; break;
- case 'b': kbps = fz_atoi(fz_optarg); break;
- default: usage(argv[0]);
- }
- }
- if (argc - fz_optind == 0)
- usage(argv[0]);
- filename = argv[fz_optind++];
- if (argc - fz_optind == 1)
- pageno = atoi(argv[fz_optind++]);
- winopen();
- if (resolution == -1)
- resolution = winresolution();
- if (resolution < MINRES)
- resolution = MINRES;
- if (resolution > MAXRES)
- resolution = MAXRES;
- gapp.transitions_enabled = 1;
- gapp.scrw = DisplayWidth(xdpy, xscr);
- gapp.scrh = DisplayHeight(xdpy, xscr);
- gapp.default_resolution = resolution;
- gapp.resolution = resolution;
- gapp.pageno = pageno;
- if (profile_name)
- pdfapp_load_profile(&gapp, profile_name);
- tmo_at.tv_sec = 0;
- tmo_at.tv_usec = 0;
- timeout = NULL;
- if (kbps)
- pdfapp_open_progressive(&gapp, filename, 0, kbps);
- else
- pdfapp_open(&gapp, filename, 0);
- FD_ZERO(&fds);
- signal(SIGHUP, signal_handler);
- while (!closing)
- {
- while (!closing && XPending(xdpy) && !transition_dirty)
- {
- XNextEvent(xdpy, &xevt);
- switch (xevt.type)
- {
- case Expose:
- dirty = 1;
- break;
- case ConfigureNotify:
- if (gapp.image)
- {
- if (xevt.xconfigure.width != reqw ||
- xevt.xconfigure.height != reqh)
- gapp.shrinkwrap = 0;
- }
- width = xevt.xconfigure.width;
- height = xevt.xconfigure.height;
- break;
- case KeyPress:
- len = XLookupString(&xevt.xkey, buf, sizeof buf, &keysym, NULL);
- if (!gapp.issearching)
- switch (keysym)
- {
- case XK_Escape:
- len = 1; buf[0] = '\033';
- break;
- case XK_Up:
- case XK_KP_Up:
- len = 1; buf[0] = 'k';
- break;
- case XK_Down:
- case XK_KP_Down:
- len = 1; buf[0] = 'j';
- break;
- case XK_Left:
- case XK_KP_Left:
- len = 1; buf[0] = 'h';
- break;
- case XK_Right:
- case XK_KP_Right:
- len = 1; buf[0] = 'l';
- break;
- case XK_Page_Up:
- case XK_KP_Page_Up:
- case XF86XK_Back:
- len = 1; buf[0] = ',';
- break;
- case XK_Page_Down:
- case XK_KP_Page_Down:
- case XF86XK_Forward:
- len = 1; buf[0] = '.';
- break;
- }
- if (xevt.xkey.state & ControlMask && keysym == XK_c)
- docopy(&gapp, XA_CLIPBOARD);
- else if (len)
- onkey(buf[0], xevt.xkey.state);
- onmouse(oldx, oldy, 0, 0, 0);
- break;
- case MotionNotify:
- oldx = xevt.xmotion.x;
- oldy = xevt.xmotion.y;
- onmouse(xevt.xmotion.x, xevt.xmotion.y, 0, xevt.xmotion.state, 0);
- break;
- case ButtonPress:
- onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, xevt.xbutton.state, 1);
- break;
- case ButtonRelease:
- copytime = xevt.xbutton.time;
- onmouse(xevt.xbutton.x, xevt.xbutton.y, xevt.xbutton.button, xevt.xbutton.state, -1);
- break;
- case SelectionRequest:
- onselreq(xevt.xselectionrequest.requestor,
- xevt.xselectionrequest.selection,
- xevt.xselectionrequest.target,
- xevt.xselectionrequest.property,
- xevt.xselectionrequest.time);
- break;
- case ClientMessage:
- if (xevt.xclient.message_type == WM_RELOAD_PAGE)
- pdfapp_reloadpage(&gapp);
- else if (xevt.xclient.format == 32 && ((Atom) xevt.xclient.data.l[0]) == WM_DELETE_WINDOW)
- closing = 1;
- break;
- }
- }
- if (closing)
- continue;
- if (width != -1 || height != -1)
- {
- pdfapp_onresize(&gapp, width, height);
- width = -1;
- height = -1;
- }
- if (dirty || dirtysearch)
- {
- if (dirty)
- winblit(&gapp);
- else if (dirtysearch)
- winblitstatusbar(&gapp);
- dirty = 0;
- transition_dirty = 0;
- dirtysearch = 0;
- pdfapp_postblit(&gapp);
- }
- if (!showingpage && !showingmessage && (tmo_at.tv_sec || tmo_at.tv_usec))
- {
- tmo_at.tv_sec = 0;
- tmo_at.tv_usec = 0;
- timeout = NULL;
- }
- if (XPending(xdpy) || transition_dirty)
- continue;
- timeout = NULL;
- if (tmo_at.tv_sec || tmo_at.tv_usec)
- {
- gettimeofday(&now, NULL);
- timersub(&tmo_at, &now, &tmo);
- if (tmo.tv_sec <= 0)
- {
- tmo_at.tv_sec = 0;
- tmo_at.tv_usec = 0;
- timeout = NULL;
- showingpage = 0;
- showingmessage = 0;
- winrepaint(&gapp);
- }
- else
- timeout = &tmo;
- }
- if (advance_scheduled)
- {
- gettimeofday(&now, NULL);
- timersub(&tmo_advance, &now, &tmo_advance_delay);
- if (tmo_advance_delay.tv_sec <= 0)
- {
- /* Too late already */
- onkey(' ', 0);
- onmouse(oldx, oldy, 0, 0, 0);
- advance_scheduled = 0;
- }
- else if (timeout == NULL)
- {
- timeout = &tmo_advance_delay;
- }
- else
- {
- struct timeval tmp;
- timersub(&tmo_advance_delay, timeout, &tmp);
- if (tmp.tv_sec < 0)
- {
- timeout = &tmo_advance_delay;
- }
- }
- }
- FD_SET(x11fd, &fds);
- if (select(x11fd + 1, &fds, NULL, NULL, timeout) < 0)
- {
- if (reloading)
- {
- pdfapp_reloadfile(&gapp);
- reloading = 0;
- }
- }
- if (!FD_ISSET(x11fd, &fds))
- {
- if (timeout == &tmo_advance_delay)
- {
- onkey(' ', 0);
- onmouse(oldx, oldy, 0, 0, 0);
- advance_scheduled = 0;
- }
- else
- {
- tmo_at.tv_sec = 0;
- tmo_at.tv_usec = 0;
- timeout = NULL;
- showingpage = 0;
- showingmessage = 0;
- winrepaint(&gapp);
- }
- }
- }
- cleanup(&gapp);
- return 0;
- }
|