backport to mupdf-0.7
[fbpdf.git] / fbpdf.c
blobce1558e547e8ea12af4fd51db6161fc1aeb10179
1 /*
2 * fbpdf - a small framebuffer pdf viewer using mupdf
4 * Copyright (C) 2009-2014 Ali Gholami Rudi <ali at rudi dot ir>
6 * This program is released under the Modified BSD license.
7 */
8 #include <ctype.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <pty.h>
15 #include "draw.h"
16 #include "doc.h"
18 #define MIN(a, b) ((a) < (b) ? (a) : (b))
19 #define MAX(a, b) ((a) > (b) ? (a) : (b))
21 #define PAGESTEPS 8
22 #define CTRLKEY(x) ((x) - 96)
23 #define ISMARK(x) (isalpha(x) || (x) == '\'' || (x) == '`')
24 #define MAXWIDTH 2
25 #define MAXHEIGHT 3
26 #define PDFCOLS (1 << 11)
27 #define PDFROWS (1 << 12)
28 #define MAXZOOM (100)
30 static struct doc *doc;
31 static fbval_t pbuf[PDFROWS * PDFCOLS]; /* current page */
32 static int prows, pcols; /* the dimensions of current page */
34 static struct termios termios;
35 static char filename[256];
36 static int mark[128]; /* mark page number */
37 static int mark_head[128]; /* mark head position */
38 static int num = 1; /* page number */
39 static int numdiff; /* G command page number difference */
40 static int zoom = 15;
41 static int zoom_def = 15; /* default zoom */
42 static int rotate;
43 static int head;
44 static int left;
45 static int count;
47 static void draw(void)
49 int i;
50 for (i = head; i < MIN(head + fb_rows(), PDFROWS); i++)
51 fb_set(i - head, 0, pbuf + i * PDFCOLS + left, fb_cols());
54 static int showpage(int p, int h)
56 if (p < 1 || p > doc_pages(doc))
57 return 0;
58 memset(pbuf, 0x00, sizeof(pbuf));
59 prows = PDFROWS;
60 pcols = PDFCOLS;
61 doc_draw(doc, p, zoom, rotate, pbuf, &prows, &pcols);
62 num = p;
63 head = h;
64 draw();
65 return 0;
68 static void zoom_page(int z)
70 int _zoom = zoom;
71 zoom = MIN(MAXZOOM, MAX(1, z));
72 showpage(num, MIN(PDFROWS - fb_rows(), head * zoom / _zoom));
75 static void setmark(int c)
77 if (ISMARK(c)) {
78 mark[c] = num;
79 mark_head[c] = head / zoom;
83 static void jmpmark(int c, int offset)
85 if (c == '`')
86 c = '\'';
87 if (ISMARK(c) && mark[c]) {
88 int dst = mark[c];
89 int dst_head = offset ? mark_head[c] * zoom : 0;
90 setmark('\'');
91 showpage(dst, dst_head);
95 static int readkey(void)
97 unsigned char b;
98 if (read(0, &b, 1) <= 0)
99 return -1;
100 return b;
103 static int getcount(int def)
105 int result = count ? count : def;
106 count = 0;
107 return result;
110 static void printinfo(void)
112 printf("\x1b[H");
113 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K\r",
114 filename, num, doc_pages(doc), zoom * 10);
115 fflush(stdout);
118 static void term_setup(void)
120 struct termios newtermios;
121 tcgetattr(0, &termios);
122 newtermios = termios;
123 newtermios.c_lflag &= ~ICANON;
124 newtermios.c_lflag &= ~ECHO;
125 tcsetattr(0, TCSAFLUSH, &newtermios);
126 printf("\x1b[?25l"); /* hide the cursor */
127 printf("\x1b[2J"); /* clear the screen */
128 fflush(stdout);
131 static void term_cleanup(void)
133 tcsetattr(0, 0, &termios);
134 printf("\x1b[?25h\n"); /* show the cursor */
137 static void sigcont(int sig)
139 term_setup();
142 static int reload(void)
144 doc_close(doc);
145 doc = doc_open(filename);
146 if (!doc) {
147 fprintf(stderr, "\nfbpdf: cannot open <%s>\n", filename);
148 return 1;
150 showpage(num, head);
151 return 0;
154 static int rightmost(int cont)
156 int ret = 0;
157 int i, j;
158 for (i = 0; i < prows; i++) {
159 j = PDFCOLS - 1;
160 while (j > ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
161 j--;
162 while (cont && j > ret &&
163 pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
164 j--;
165 if (ret < j)
166 ret = j;
168 return ret;
171 static int leftmost(int cont)
173 int ret = PDFCOLS;
174 int i, j;
175 for (i = 0; i < prows; i++) {
176 j = 0;
177 while (j < ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
178 j++;
179 while (cont && j < ret &&
180 pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
181 j++;
182 if (ret > j)
183 ret = j;
185 return ret;
188 static void mainloop(void)
190 int step = fb_rows() / PAGESTEPS;
191 int hstep = fb_cols() / PAGESTEPS;
192 int c;
193 term_setup();
194 signal(SIGCONT, sigcont);
195 showpage(num, 0);
196 while ((c = readkey()) != -1) {
197 if (c == 'q')
198 break;
199 if (c == 'e' && reload())
200 break;
201 switch (c) {
202 case CTRLKEY('f'):
203 case 'J':
204 showpage(num + getcount(1), 0);
205 break;
206 case CTRLKEY('b'):
207 case 'K':
208 showpage(num - getcount(1), 0);
209 break;
210 case 'G':
211 setmark('\'');
212 showpage(getcount(doc_pages(doc) - numdiff) + numdiff, 0);
213 break;
214 case 'O':
215 numdiff = num - getcount(num);
216 setmark('\'');
217 showpage(num + numdiff, 0);
218 break;
219 case 'o':
220 numdiff = num - getcount(num);
221 break;
222 case 'z':
223 zoom_page(getcount(zoom_def));
224 break;
225 case 'Z':
226 zoom_def = getcount(zoom);
227 break;
228 case 'w':
229 zoom_page(zoom * fb_cols() / pcols);
230 break;
231 case 'W':
232 if (leftmost(1) < rightmost(1))
233 zoom_page(zoom * (fb_cols() - hstep) /
234 (rightmost(1) - leftmost(1)));
235 break;
236 case 'f':
237 zoom_page(zoom * fb_rows() / prows);
238 break;
239 case 'r':
240 rotate = getcount(0);
241 showpage(num, 0);
242 break;
243 case 'i':
244 printinfo();
245 break;
246 case 27:
247 count = 0;
248 break;
249 case 'm':
250 setmark(readkey());
251 break;
252 case '`':
253 case '\'':
254 jmpmark(readkey(), c == '`');
255 break;
256 case 'd':
257 sleep(getcount(1));
258 break;
259 default:
260 if (isdigit(c))
261 count = count * 10 + c - '0';
263 switch (c) {
264 case 'j':
265 head += step * getcount(1);
266 break;
267 case 'k':
268 head -= step * getcount(1);
269 break;
270 case 'l':
271 left += hstep * getcount(1);
272 break;
273 case 'h':
274 left -= hstep * getcount(1);
275 break;
276 case 'H':
277 head = 0;
278 break;
279 case 'L':
280 head = MAX(0, prows - fb_rows());
281 break;
282 case 'M':
283 head = (prows - fb_rows()) / 2;
284 break;
285 case 'C':
286 left = (PDFCOLS - fb_cols()) / 2;
287 break;
288 case ' ':
289 case CTRL('d'):
290 head += fb_rows() * getcount(1) - step;
291 break;
292 case 127:
293 case CTRL('u'):
294 head -= fb_rows() * getcount(1) - step;
295 break;
296 case '[':
297 left = leftmost(0);
298 break;
299 case ']':
300 left = rightmost(0) - fb_cols();
301 break;
302 case '{':
303 left = leftmost(1) - hstep / 2;
304 break;
305 case '}':
306 left = rightmost(1) + hstep / 2 - fb_cols();
307 break;
308 case CTRLKEY('l'):
309 break;
310 default:
311 /* no need to redraw */
312 continue;
314 head = MAX(0, MIN(PDFROWS - fb_rows(), head));
315 left = MAX(0, MIN(PDFCOLS - fb_cols(), left));
316 draw();
318 term_cleanup();
321 static char *usage =
322 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename\n";
324 int main(int argc, char *argv[])
326 int i = 1;
327 if (argc < 2) {
328 printf(usage);
329 return 1;
331 strcpy(filename, argv[argc - 1]);
332 doc = doc_open(filename);
333 if (!doc) {
334 fprintf(stderr, "fbpdf: cannot open <%s>\n", filename);
335 return 1;
337 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
338 switch (argv[i][1]) {
339 case 'r':
340 rotate = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
341 break;
342 case 'z':
343 zoom = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
344 break;
345 case 'p':
346 num = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
347 break;
350 printinfo();
351 if (fb_init())
352 return 1;
353 left = (PDFCOLS - fb_cols()) / 2;
354 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
355 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
356 else
357 mainloop();
358 fb_free();
359 if (doc)
360 doc_close(doc);
361 return 0;