fbpdf: add keybinding to invert colors
[fbpdf.git] / fbpdf.c
bloba58797f983a6ef47f70eb857baae6a90d89bffed
1 /*
2 * FBPDF LINUX FRAMEBUFFER PDF VIEWER
4 * Copyright (C) 2009-2016 Ali Gholami Rudi <ali at rudi dot ir>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <ctype.h>
19 #include <signal.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <termios.h>
24 #include <unistd.h>
25 #include "draw.h"
26 #include "doc.h"
28 #define MIN(a, b) ((a) < (b) ? (a) : (b))
29 #define MAX(a, b) ((a) > (b) ? (a) : (b))
31 #define PAGESTEPS 8
32 #define MAXZOOM 100
33 #define MARGIN 1
34 #define CTRLKEY(x) ((x) - 96)
35 #define ISMARK(x) (isalpha(x) || (x) == '\'' || (x) == '`')
37 static struct doc *doc;
38 static fbval_t *pbuf; /* current page */
39 static int srows, scols; /* screen dimentions */
40 static int prows, pcols; /* current page dimensions */
41 static int prow, pcol; /* page position */
42 static int srow, scol; /* screen position */
44 static struct termios termios;
45 static char filename[256];
46 static int mark[128]; /* mark page number */
47 static int mark_row[128]; /* mark head position */
48 static int num = 1; /* page number */
49 static int numdiff; /* G command page number difference */
50 static int zoom = 15;
51 static int zoom_def = 15; /* default zoom */
52 static int rotate;
53 static int count;
54 static int invert; /* invert colors? */
56 static void draw(void)
58 int i;
59 fbval_t *rbuf = malloc(scols * sizeof(rbuf[0]));
60 for (i = srow; i < srow + srows; i++) {
61 int cbeg = MAX(scol, pcol);
62 int cend = MIN(scol + scols, pcol + pcols);
63 memset(rbuf, 0, scols * sizeof(rbuf[0]));
64 if (i >= prow && i < prow + prows && cbeg < cend) {
65 memcpy(rbuf + cbeg - scol,
66 pbuf + (i - prow) * pcols + cbeg - pcol,
67 (cend - cbeg) * sizeof(rbuf[0]));
69 fb_set(i - srow, 0, rbuf, scols);
71 free(rbuf);
74 static int loadpage(int p)
76 int i;
77 if (p < 1 || p > doc_pages(doc))
78 return 1;
79 prows = 0;
80 free(pbuf);
81 pbuf = doc_draw(doc, p, zoom, rotate, &prows, &pcols);
82 if (invert) {
83 for (i = 0; i < prows * pcols; i++)
84 pbuf[i] = pbuf[i] ^ 0xffffffff;
86 prow = -prows / 2;
87 pcol = -pcols / 2;
88 num = p;
89 return 0;
92 static void zoom_page(int z)
94 int _zoom = zoom;
95 zoom = MIN(MAXZOOM, MAX(1, z));
96 if (!loadpage(num))
97 srow = srow * zoom / _zoom;
100 static void setmark(int c)
102 if (ISMARK(c)) {
103 mark[c] = num;
104 mark_row[c] = srow / zoom;
108 static void jmpmark(int c, int offset)
110 if (c == '`')
111 c = '\'';
112 if (ISMARK(c) && mark[c]) {
113 int dst = mark[c];
114 int dst_row = offset ? mark_row[c] * zoom : 0;
115 setmark('\'');
116 if (!loadpage(dst))
117 srow = offset ? dst_row : prow;
121 static int readkey(void)
123 unsigned char b;
124 if (read(0, &b, 1) <= 0)
125 return -1;
126 return b;
129 static int getcount(int def)
131 int result = count ? count : def;
132 count = 0;
133 return result;
136 static void printinfo(void)
138 printf("\x1b[H");
139 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K\r",
140 filename, num, doc_pages(doc), zoom * 10);
141 fflush(stdout);
144 static void term_setup(void)
146 struct termios newtermios;
147 tcgetattr(0, &termios);
148 newtermios = termios;
149 newtermios.c_lflag &= ~ICANON;
150 newtermios.c_lflag &= ~ECHO;
151 tcsetattr(0, TCSAFLUSH, &newtermios);
152 printf("\x1b[?25l"); /* hide the cursor */
153 printf("\x1b[2J"); /* clear the screen */
154 fflush(stdout);
157 static void term_cleanup(void)
159 tcsetattr(0, 0, &termios);
160 printf("\x1b[?25h\n"); /* show the cursor */
163 static void sigcont(int sig)
165 term_setup();
168 static int reload(void)
170 doc_close(doc);
171 doc = doc_open(filename);
172 if (!doc || !doc_pages(doc)) {
173 fprintf(stderr, "\nfbpdf: cannot open <%s>\n", filename);
174 return 1;
176 if (!loadpage(num))
177 draw();
178 return 0;
181 static int rmargin(void)
183 int ret = 0;
184 int i, j;
185 for (i = 0; i < prows; i++) {
186 j = pcols - 1;
187 while (j > ret && pbuf[i * pcols + j] == FB_VAL(255, 255, 255))
188 j--;
189 if (ret < j)
190 ret = j;
192 return ret;
195 static int lmargin(void)
197 int ret = pcols;
198 int i, j;
199 for (i = 0; i < prows; i++) {
200 j = 0;
201 while (j < ret && pbuf[i * pcols + j] == FB_VAL(255, 255, 255))
202 j++;
203 if (ret > j)
204 ret = j;
206 return ret;
209 static void mainloop(void)
211 int step = srows / PAGESTEPS;
212 int hstep = scols / PAGESTEPS;
213 int c;
214 term_setup();
215 signal(SIGCONT, sigcont);
216 loadpage(num);
217 srow = prow;
218 scol = -scols / 2;
219 draw();
220 while ((c = readkey()) != -1) {
221 if (c == 'q')
222 break;
223 if (c == 'e' && reload())
224 break;
225 switch (c) { /* commands that do not require redrawing */
226 case 'o':
227 numdiff = num - getcount(num);
228 break;
229 case 'Z':
230 zoom_def = getcount(zoom);
231 break;
232 case 'i':
233 printinfo();
234 break;
235 case 27:
236 count = 0;
237 break;
238 case 'm':
239 setmark(readkey());
240 break;
241 case 'd':
242 sleep(getcount(1));
243 break;
244 default:
245 if (isdigit(c))
246 count = count * 10 + c - '0';
248 switch (c) { /* commands that require redrawing */
249 case CTRLKEY('f'):
250 case 'J':
251 if (!loadpage(num + getcount(1)))
252 srow = prow;
253 break;
254 case CTRLKEY('b'):
255 case 'K':
256 if (!loadpage(num - getcount(1)))
257 srow = prow;
258 break;
259 case 'G':
260 setmark('\'');
261 if (!loadpage(getcount(doc_pages(doc) - numdiff) + numdiff))
262 srow = prow;
263 break;
264 case 'O':
265 numdiff = num - getcount(num);
266 setmark('\'');
267 if (!loadpage(num + numdiff))
268 srow = prow;
269 break;
270 case 'z':
271 zoom_page(getcount(zoom_def));
272 break;
273 case 'w':
274 zoom_page(pcols ? zoom * scols / pcols : zoom);
275 break;
276 case 'W':
277 if (lmargin() < rmargin())
278 zoom_page(zoom * (scols - hstep) /
279 (rmargin() - lmargin()));
280 break;
281 case 'f':
282 zoom_page(prows ? zoom * srows / prows : zoom);
283 break;
284 case 'r':
285 rotate = getcount(0);
286 if (!loadpage(num))
287 srow = prow;
288 break;
289 case '`':
290 case '\'':
291 jmpmark(readkey(), c == '`');
292 break;
293 case 'j':
294 srow += step * getcount(1);
295 break;
296 case 'k':
297 srow -= step * getcount(1);
298 break;
299 case 'l':
300 scol += hstep * getcount(1);
301 break;
302 case 'h':
303 scol -= hstep * getcount(1);
304 break;
305 case 'H':
306 srow = prow;
307 break;
308 case 'L':
309 srow = prow + prows - srows;
310 break;
311 case 'M':
312 srow = prow + prows / 2 - srows / 2;
313 break;
314 case 'C':
315 scol = -scols / 2;
316 break;
317 case ' ':
318 case CTRLKEY('d'):
319 srow += srows * getcount(1) - step;
320 break;
321 case 127:
322 case CTRLKEY('u'):
323 srow -= srows * getcount(1) - step;
324 break;
325 case '[':
326 scol = pcol;
327 break;
328 case ']':
329 scol = pcol + pcols - scols;
330 break;
331 case '{':
332 scol = pcol + lmargin() - hstep / 2;
333 break;
334 case '}':
335 scol = pcol + rmargin() + hstep / 2 - scols;
336 break;
337 case CTRLKEY('l'):
338 break;
339 case 'I':
340 invert = !invert;
341 loadpage(num);
342 break;
343 default: /* no need to redraw */
344 continue;
346 srow = MAX(prow - srows + MARGIN, MIN(prow + prows - MARGIN, srow));
347 scol = MAX(pcol - scols + MARGIN, MIN(pcol + pcols - MARGIN, scol));
348 draw();
350 term_cleanup();
353 static char *usage =
354 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename\n";
356 int main(int argc, char *argv[])
358 int i = 1;
359 if (argc < 2) {
360 printf(usage);
361 return 1;
363 strcpy(filename, argv[argc - 1]);
364 doc = doc_open(filename);
365 if (!doc || !doc_pages(doc)) {
366 fprintf(stderr, "fbpdf: cannot open <%s>\n", filename);
367 return 1;
369 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
370 switch (argv[i][1]) {
371 case 'r':
372 rotate = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
373 break;
374 case 'z':
375 zoom = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
376 break;
377 case 'p':
378 num = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
379 break;
382 printinfo();
383 if (fb_init())
384 return 1;
385 srows = fb_rows();
386 scols = fb_cols();
387 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
388 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
389 else
390 mainloop();
391 fb_free();
392 free(pbuf);
393 if (doc)
394 doc_close(doc);
395 return 0;