fbpdf: o command now sets current page number
[fbpdf.git] / fbpdf.c
blob8ea7a8e3ad946eb835eec0f52985cb31f92ee27f
1 /*
2 * fbpdf - a small framebuffer pdf viewer using mupdf
4 * Copyright (C) 2009-2013 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 rotate;
42 static int head;
43 static int left;
44 static int count;
46 static void draw(void)
48 int i;
49 for (i = head; i < MIN(head + fb_rows(), PDFROWS); i++)
50 fb_set(i - head, 0, pbuf + i * PDFCOLS + left, fb_cols());
53 static int showpage(int p, int h)
55 if (p < 1 || p > doc_pages(doc))
56 return 0;
57 memset(pbuf, 0x00, sizeof(pbuf));
58 prows = PDFROWS;
59 pcols = PDFCOLS;
60 doc_draw(doc, p, zoom, rotate, pbuf, &prows, &pcols);
61 num = p;
62 head = h;
63 draw();
64 return 0;
67 static void zoom_page(int z)
69 int _zoom = zoom;
70 zoom = MIN(MAXZOOM, MAX(1, z));
71 showpage(num, MIN(PDFROWS - fb_rows(), head * zoom / _zoom));
74 static void setmark(int c)
76 if (ISMARK(c)) {
77 mark[c] = num;
78 mark_head[c] = head / zoom;
82 static void jmpmark(int c, int offset)
84 if (c == '`')
85 c = '\'';
86 if (ISMARK(c) && mark[c]) {
87 int dst = mark[c];
88 int dst_head = offset ? mark_head[c] * zoom : 0;
89 setmark('\'');
90 showpage(dst, dst_head);
94 static int readkey(void)
96 unsigned char b;
97 if (read(0, &b, 1) <= 0)
98 return -1;
99 return b;
102 static int getcount(int def)
104 int result = count ? count : def;
105 count = 0;
106 return result;
109 static void printinfo(void)
111 printf("\x1b[H");
112 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K",
113 filename, num, doc_pages(doc), zoom * 10);
114 fflush(stdout);
117 static void term_setup(void)
119 struct termios newtermios;
120 tcgetattr(0, &termios);
121 newtermios = termios;
122 newtermios.c_lflag &= ~ICANON;
123 newtermios.c_lflag &= ~ECHO;
124 tcsetattr(0, TCSAFLUSH, &newtermios);
127 static void term_cleanup(void)
129 tcsetattr(0, 0, &termios);
132 static void sigcont(int sig)
134 term_setup();
137 static void reload(void)
139 doc_close(doc);
140 doc = doc_open(filename);
141 showpage(num, head);
144 static int rightmost(int cont)
146 int ret = 0;
147 int i, j;
148 for (i = 0; i < prows; i++) {
149 j = PDFCOLS - 1;
150 while (j > ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
151 j--;
152 while (cont && j > ret &&
153 pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
154 j--;
155 if (ret < j)
156 ret = j;
158 return ret;
161 static int leftmost(int cont)
163 int ret = PDFCOLS;
164 int i, j;
165 for (i = 0; i < prows; i++) {
166 j = 0;
167 while (j < ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
168 j++;
169 while (cont && j < ret &&
170 pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
171 j++;
172 if (ret > j)
173 ret = j;
175 return ret;
178 static void mainloop(void)
180 int step = fb_rows() / PAGESTEPS;
181 int hstep = fb_cols() / PAGESTEPS;
182 int c;
183 term_setup();
184 signal(SIGCONT, sigcont);
185 showpage(num, 0);
186 while ((c = readkey()) != -1) {
187 switch (c) {
188 case CTRLKEY('f'):
189 case 'J':
190 showpage(num + getcount(1), 0);
191 break;
192 case CTRLKEY('b'):
193 case 'K':
194 showpage(num - getcount(1), 0);
195 break;
196 case 'G':
197 setmark('\'');
198 showpage(getcount(doc_pages(doc) - numdiff) + numdiff, 0);
199 break;
200 case 'z':
201 zoom_page(getcount(15));
202 break;
203 case 'w':
204 zoom_page(zoom * fb_cols() / pcols);
205 break;
206 case 'W':
207 if (leftmost(1) < rightmost(1))
208 zoom_page(zoom * (fb_cols() - hstep) /
209 (rightmost(1) - leftmost(1)));
210 break;
211 case 'f':
212 zoom_page(zoom * fb_rows() / prows);
213 break;
214 case 'r':
215 rotate = getcount(0);
216 showpage(num, 0);
217 break;
218 case 'i':
219 printinfo();
220 break;
221 case 'q':
222 term_cleanup();
223 return;
224 case 27:
225 count = 0;
226 break;
227 case 'm':
228 setmark(readkey());
229 break;
230 case 'e':
231 reload();
232 break;
233 case '`':
234 case '\'':
235 jmpmark(readkey(), c == '`');
236 break;
237 case 'o':
238 numdiff = num - getcount(num);
239 break;
240 default:
241 if (isdigit(c))
242 count = count * 10 + c - '0';
244 switch (c) {
245 case 'j':
246 head += step * getcount(1);
247 break;
248 case 'k':
249 head -= step * getcount(1);
250 break;
251 case 'l':
252 left += hstep * getcount(1);
253 break;
254 case 'h':
255 left -= hstep * getcount(1);
256 break;
257 case 'H':
258 head = 0;
259 break;
260 case 'L':
261 head = MAX(0, prows - fb_rows());
262 break;
263 case 'M':
264 head = (prows - fb_rows()) / 2;
265 break;
266 case ' ':
267 case CTRL('d'):
268 head += fb_rows() * getcount(1) - step;
269 break;
270 case 127:
271 case CTRL('u'):
272 head -= fb_rows() * getcount(1) - step;
273 break;
274 case '[':
275 left = leftmost(0);
276 break;
277 case ']':
278 left = rightmost(0) - fb_cols();
279 break;
280 case '{':
281 left = leftmost(1) - hstep / 2;
282 break;
283 case '}':
284 left = rightmost(1) + hstep / 2 - fb_cols();
285 break;
286 case CTRLKEY('l'):
287 break;
288 default:
289 /* no need to redraw */
290 continue;
292 head = MAX(0, MIN(PDFROWS - fb_rows(), head));
293 left = MAX(0, MIN(PDFCOLS - fb_cols(), left));
294 draw();
298 static char *usage =
299 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename\n";
301 int main(int argc, char *argv[])
303 int i = 1;
304 if (argc < 2) {
305 printf(usage);
306 return 1;
308 strcpy(filename, argv[argc - 1]);
309 doc = doc_open(filename);
310 if (!doc) {
311 fprintf(stderr, "cannot open <%s>\n", filename);
312 return 1;
314 for (i = 1; i < argc && argv[i][0] == '-'; i++) {
315 switch (argv[i][1]) {
316 case 'r':
317 rotate = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
318 break;
319 case 'z':
320 zoom = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
321 break;
322 case 'p':
323 num = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]);
324 break;
327 printf("\x1b[?25l"); /* hide the cursor */
328 printf("\x1b[2J"); /* clear the screen */
329 printinfo();
330 if (fb_init())
331 return 1;
332 left = (PDFCOLS - fb_cols()) / 2;
333 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
334 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
335 else
336 mainloop();
337 fb_free();
338 printf("\x1b[?25h\n"); /* show the cursor */
339 doc_close(doc);
340 return 0;