2854f723df5b6f09ccc728d902bfaad2b1c7e019
[fbpdf.git] / fbpdf.c
blob2854f723df5b6f09ccc728d902bfaad2b1c7e019
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 MAXWIDTH 2
24 #define MAXHEIGHT 3
25 #define PDFCOLS (1 << 11)
26 #define PDFROWS (1 << 12)
27 #define MAXZOOM (100)
29 static struct doc *doc;
30 static fbval_t pbuf[PDFROWS * PDFCOLS]; /* current page */
31 static int prows, pcols; /* the dimensions of current page */
33 static int num = 1;
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 zoom = 15;
39 static int rotate;
40 static int head;
41 static int left;
42 static int count;
44 static void draw(void)
46 int i;
47 for (i = head; i < MIN(head + fb_rows(), PDFROWS); i++)
48 fb_set(i - head, 0, pbuf + i * PDFCOLS + left, fb_cols());
51 static int showpage(int p, int h)
53 if (p < 1 || p > doc_pages(doc))
54 return 0;
55 memset(pbuf, 0x00, sizeof(pbuf));
56 prows = PDFROWS;
57 pcols = PDFCOLS;
58 doc_draw(doc, p, zoom, rotate, pbuf, &prows, &pcols);
59 num = p;
60 head = h;
61 draw();
62 return 0;
65 static void zoom_page(int z)
67 int _zoom = zoom;
68 zoom = MIN(MAXZOOM, MAX(1, z));
69 showpage(num, MIN(PDFROWS - fb_rows(), head * zoom / _zoom));
72 static int readkey(void)
74 unsigned char b;
75 if (read(STDIN_FILENO, &b, 1) <= 0)
76 return -1;
77 return b;
80 static int getcount(int def)
82 int result = count ? count : def;
83 count = 0;
84 return result;
87 static void printinfo(void)
89 printf("\x1b[H");
90 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K",
91 filename, num, doc_pages(doc), zoom * 10);
92 fflush(stdout);
95 static void term_setup(void)
97 struct termios newtermios;
98 tcgetattr(STDIN_FILENO, &termios);
99 newtermios = termios;
100 newtermios.c_lflag &= ~ICANON;
101 newtermios.c_lflag &= ~ECHO;
102 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
105 static void term_cleanup(void)
107 tcsetattr(STDIN_FILENO, 0, &termios);
110 static void sigcont(int sig)
112 term_setup();
115 static void reload(void)
117 doc_close(doc);
118 doc = doc_open(filename);
119 showpage(num, head);
122 static int rightmost(int cont)
124 int ret = 0;
125 int i, j;
126 for (i = 0; i < prows; i++) {
127 j = PDFCOLS - 1;
128 while (j > ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
129 j--;
130 while (cont && j > ret &&
131 pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
132 j--;
133 if (ret < j)
134 ret = j;
136 return ret;
139 static int leftmost(int cont)
141 int ret = PDFCOLS;
142 int i, j;
143 for (i = 0; i < prows; i++) {
144 j = 0;
145 while (j < ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
146 j++;
147 while (cont && j < ret &&
148 pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
149 j++;
150 if (ret > j)
151 ret = j;
153 return ret;
156 static void mainloop(void)
158 int step = fb_rows() / PAGESTEPS;
159 int hstep = fb_cols() / PAGESTEPS;
160 int c, c2;
161 term_setup();
162 signal(SIGCONT, sigcont);
163 showpage(num, 0);
164 while ((c = readkey()) != -1) {
165 switch (c) {
166 case CTRLKEY('f'):
167 case 'J':
168 showpage(num + getcount(1), 0);
169 break;
170 case CTRLKEY('b'):
171 case 'K':
172 showpage(num - getcount(1), 0);
173 break;
174 case 'G':
175 showpage(getcount(doc_pages(doc)), 0);
176 break;
177 case 'z':
178 zoom_page(getcount(15));
179 break;
180 case 'w':
181 zoom_page(zoom * fb_cols() / pcols);
182 break;
183 case 'W':
184 if (leftmost(1) < rightmost(1))
185 zoom_page(zoom * (fb_cols() - hstep) /
186 (rightmost(1) - leftmost(1)));
187 break;
188 case 'f':
189 zoom_page(zoom * fb_rows() / prows);
190 break;
191 case 'r':
192 rotate = getcount(0);
193 showpage(num, 0);
194 break;
195 case 'i':
196 printinfo();
197 break;
198 case 'q':
199 term_cleanup();
200 return;
201 case 27:
202 count = 0;
203 break;
204 case 'm':
205 c2 = readkey();
206 if (isalpha(c2)) {
207 mark[c2] = num;
208 mark_head[c2] = head / zoom;
210 break;
211 case 'e':
212 reload();
213 break;
214 case '`':
215 case '\'':
216 c2 = readkey();
217 if (isalpha(c2) && mark[c2])
218 showpage(mark[c2], c == '`' ? mark_head[c2] * zoom : 0);
219 break;
220 default:
221 if (isdigit(c))
222 count = count * 10 + c - '0';
224 switch (c) {
225 case 'j':
226 head += step * getcount(1);
227 break;
228 case 'k':
229 head -= step * getcount(1);
230 break;
231 case 'l':
232 left += hstep * getcount(1);
233 break;
234 case 'h':
235 left -= hstep * getcount(1);
236 break;
237 case 'H':
238 head = 0;
239 break;
240 case 'L':
241 head = MAX(0, prows - fb_rows());
242 break;
243 case 'M':
244 head = (prows - fb_rows()) / 2;
245 break;
246 case ' ':
247 case CTRL('d'):
248 head += fb_rows() * getcount(1) - step;
249 break;
250 case 127:
251 case CTRL('u'):
252 head -= fb_rows() * getcount(1) - step;
253 break;
254 case '{':
255 left = leftmost(0);
256 break;
257 case '}':
258 left = rightmost(0) - fb_cols();
259 break;
260 case '[':
261 left = leftmost(1) - hstep / 2;
262 break;
263 case ']':
264 left = rightmost(1) + hstep / 2 - fb_cols();
265 break;
266 case CTRLKEY('l'):
267 break;
268 default:
269 /* no need to redraw */
270 continue;
272 head = MAX(0, MIN(PDFROWS - fb_rows(), head));
273 left = MAX(0, MIN(PDFCOLS - fb_cols(), left));
274 draw();
278 static char *usage =
279 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename\n";
281 int main(int argc, char *argv[])
283 char *hide = "\x1b[?25l";
284 char *show = "\x1b[?25h";
285 char *clear = "\x1b[2J";
286 int i = 1;
287 if (argc < 2) {
288 printf(usage);
289 return 1;
291 strcpy(filename, argv[argc - 1]);
292 doc = doc_open(filename);
293 if (!doc) {
294 fprintf(stderr, "cannot open <%s>\n", filename);
295 return 1;
297 while (i + 2 < argc && argv[i][0] == '-') {
298 if (argv[i][1] == 'r')
299 rotate = atoi(argv[i + 1]);
300 if (argv[i][1] == 'z')
301 zoom = atoi(argv[i + 1]);
302 if (argv[i][1] == 'p')
303 num = atoi(argv[i + 1]);
304 i += 2;
307 write(STDIN_FILENO, hide, strlen(hide));
308 write(STDOUT_FILENO, clear, strlen(clear));
309 printinfo();
310 if (fb_init())
311 return 1;
312 left = (PDFCOLS - fb_cols()) / 2;
313 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
314 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
315 else
316 mainloop();
317 fb_free();
318 write(STDIN_FILENO, show, strlen(show));
319 printf("\n");
320 doc_close(doc);
321 return 0;