fbpdf.1: update and improve the manual page
[fbpdf.git] / fbpdf.c
blob446d23d9c12bb85a1cba04cd49c988c193495abc
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 1000
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 = 150;
51 static int zoom_def = 150; /* default zoom */
52 static int rotate;
53 static int count;
54 static int invert; /* invert colors? */
56 static void draw(void)
58 int bpp = FBM_BPP(fb_mode());
59 int i;
60 fbval_t *rbuf = malloc(scols * sizeof(rbuf[0]));
61 for (i = srow; i < srow + srows; i++) {
62 int cbeg = MAX(scol, pcol);
63 int cend = MIN(scol + scols, pcol + pcols);
64 memset(rbuf, 0, scols * sizeof(rbuf[0]));
65 if (i >= prow && i < prow + prows && cbeg < cend) {
66 memcpy(rbuf + cbeg - scol,
67 pbuf + (i - prow) * pcols + cbeg - pcol,
68 (cend - cbeg) * sizeof(rbuf[0]));
70 memcpy(fb_mem(i - srow), rbuf, scols * bpp);
72 free(rbuf);
75 static int loadpage(int p)
77 int i;
78 if (p < 1 || p > doc_pages(doc))
79 return 1;
80 prows = 0;
81 free(pbuf);
82 pbuf = doc_draw(doc, p, zoom, rotate, &prows, &pcols);
83 if (invert) {
84 for (i = 0; i < prows * pcols; i++)
85 pbuf[i] = pbuf[i] ^ 0xffffffff;
87 prow = -prows / 2;
88 pcol = -pcols / 2;
89 num = p;
90 return 0;
93 static void zoom_page(int z)
95 int _zoom = zoom;
96 zoom = MIN(MAXZOOM, MAX(1, z));
97 if (!loadpage(num))
98 srow = srow * zoom / _zoom;
101 static void setmark(int c)
103 if (ISMARK(c)) {
104 mark[c] = num;
105 mark_row[c] = srow * 100 / zoom;
109 static void jmpmark(int c, int offset)
111 if (c == '`')
112 c = '\'';
113 if (ISMARK(c) && mark[c]) {
114 int dst = mark[c];
115 int dst_row = offset ? mark_row[c] * zoom / 100 : 0;
116 setmark('\'');
117 if (!loadpage(dst))
118 srow = offset ? dst_row : prow;
122 static int readkey(void)
124 unsigned char b;
125 if (read(0, &b, 1) <= 0)
126 return -1;
127 return b;
130 static int getcount(int def)
132 int result = count ? count : def;
133 count = 0;
134 return result;
137 static void printinfo(void)
139 printf("\x1b[H");
140 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K\r",
141 filename, num, doc_pages(doc), zoom);
142 fflush(stdout);
145 static void term_setup(void)
147 struct termios newtermios;
148 tcgetattr(0, &termios);
149 newtermios = termios;
150 newtermios.c_lflag &= ~ICANON;
151 newtermios.c_lflag &= ~ECHO;
152 tcsetattr(0, TCSAFLUSH, &newtermios);
153 printf("\x1b[?25l"); /* hide the cursor */
154 printf("\x1b[2J"); /* clear the screen */
155 fflush(stdout);
158 static void term_cleanup(void)
160 tcsetattr(0, 0, &termios);
161 printf("\x1b[?25h\n"); /* show the cursor */
164 static void sigcont(int sig)
166 term_setup();
169 static int reload(void)
171 doc_close(doc);
172 doc = doc_open(filename);
173 if (!doc || !doc_pages(doc)) {
174 fprintf(stderr, "\nfbpdf: cannot open <%s>\n", filename);
175 return 1;
177 if (!loadpage(num))
178 draw();
179 return 0;
182 static int rmargin(void)
184 int ret = 0;
185 int i, j;
186 for (i = 0; i < prows; i++) {
187 j = pcols - 1;
188 while (j > ret && pbuf[i * pcols + j] == FB_VAL(255, 255, 255))
189 j--;
190 if (ret < j)
191 ret = j;
193 return ret;
196 static int lmargin(void)
198 int ret = pcols;
199 int i, j;
200 for (i = 0; i < prows; i++) {
201 j = 0;
202 while (j < ret && pbuf[i * pcols + j] == FB_VAL(255, 255, 255))
203 j++;
204 if (ret > j)
205 ret = j;
207 return ret;
210 static void mainloop(void)
212 int step = srows / PAGESTEPS;
213 int hstep = scols / PAGESTEPS;
214 int c;
215 term_setup();
216 signal(SIGCONT, sigcont);
217 loadpage(num);
218 srow = prow;
219 scol = -scols / 2;
220 draw();
221 while ((c = readkey()) != -1) {
222 if (c == 'q')
223 break;
224 if (c == 'e' && reload())
225 break;
226 switch (c) { /* commands that do not require redrawing */
227 case 'o':
228 numdiff = num - getcount(num);
229 break;
230 case 'Z':
231 count *= 10;
232 zoom_def = getcount(zoom);
233 break;
234 case 'i':
235 printinfo();
236 break;
237 case 27:
238 count = 0;
239 break;
240 case 'm':
241 setmark(readkey());
242 break;
243 case 'd':
244 sleep(getcount(1));
245 break;
246 default:
247 if (isdigit(c))
248 count = count * 10 + c - '0';
250 switch (c) { /* commands that require redrawing */
251 case CTRLKEY('f'):
252 case 'J':
253 if (!loadpage(num + getcount(1)))
254 srow = prow;
255 break;
256 case CTRLKEY('b'):
257 case 'K':
258 if (!loadpage(num - getcount(1)))
259 srow = prow;
260 break;
261 case 'G':
262 setmark('\'');
263 if (!loadpage(getcount(doc_pages(doc) - numdiff) + numdiff))
264 srow = prow;
265 break;
266 case 'O':
267 numdiff = num - getcount(num);
268 setmark('\'');
269 if (!loadpage(num + numdiff))
270 srow = prow;
271 break;
272 case 'z':
273 count *= 10;
274 zoom_page(getcount(zoom_def));
275 break;
276 case 'w':
277 zoom_page(pcols ? zoom * scols / pcols : zoom);
278 break;
279 case 'W':
280 if (lmargin() < rmargin())
281 zoom_page(zoom * (scols - hstep) /
282 (rmargin() - lmargin()));
283 break;
284 case 'f':
285 zoom_page(prows ? zoom * srows / prows : zoom);
286 break;
287 case 'r':
288 rotate = getcount(0);
289 if (!loadpage(num))
290 srow = prow;
291 break;
292 case '`':
293 case '\'':
294 jmpmark(readkey(), c == '`');
295 break;
296 case 'j':
297 srow += step * getcount(1);
298 break;
299 case 'k':
300 srow -= step * getcount(1);
301 break;
302 case 'l':
303 scol += hstep * getcount(1);
304 break;
305 case 'h':
306 scol -= hstep * getcount(1);
307 break;
308 case 'H':
309 srow = prow;
310 break;
311 case 'L':
312 srow = prow + prows - srows;
313 break;
314 case 'M':
315 srow = prow + prows / 2 - srows / 2;
316 break;
317 case 'C':
318 scol = -scols / 2;
319 break;
320 case ' ':
321 case CTRLKEY('d'):
322 srow += srows * getcount(1) - step;
323 break;
324 case 127:
325 case CTRLKEY('u'):
326 srow -= srows * getcount(1) - step;
327 break;
328 case '[':
329 scol = pcol;
330 break;
331 case ']':
332 scol = pcol + pcols - scols;
333 break;
334 case '{':
335 scol = pcol + lmargin() - hstep / 2;
336 break;
337 case '}':
338 scol = pcol + rmargin() + hstep / 2 - scols;
339 break;
340 case CTRLKEY('l'):
341 break;
342 case 'I':
343 invert = !invert;
344 loadpage(num);
345 break;
346 default: /* no need to redraw */
347 continue;
349 srow = MAX(prow - srows + MARGIN, MIN(prow + prows - MARGIN, srow));
350 scol = MAX(pcol - scols + MARGIN, MIN(pcol + pcols - MARGIN, scol));
351 draw();
353 term_cleanup();
356 static char *usage =
357 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename";
359 int main(int argc, char *argv[])
361 int i = 1;
362 if (argc < 2) {
363 puts(usage);
364 return 1;
366 strcpy(filename, argv[argc - 1]);
367 doc = doc_open(filename);
368 if (!doc || !doc_pages(doc)) {
369 fprintf(stderr, "fbpdf: cannot open <%s>\n", filename);
370 return 1;
372 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
373 switch (argv[i][1]) {
374 case 'r':
375 rotate = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
376 break;
377 case 'z':
378 zoom = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]) * 10;
379 break;
380 case 'p':
381 num = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
382 break;
385 printinfo();
386 if (fb_init(getenv("FBDEV")))
387 return 1;
388 srows = fb_rows();
389 scols = fb_cols();
390 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
391 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
392 else
393 mainloop();
394 fb_free();
395 free(pbuf);
396 if (doc)
397 doc_close(doc);
398 return 0;