a6725e14e97dd365e9ae08bd6de5adbf7395780c
[fbpdf.git] / fbpdf.c
bloba6725e14e97dd365e9ae08bd6de5adbf7395780c
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)
28 static struct doc *doc;
29 static fbval_t pbuf[PDFROWS * PDFCOLS]; /* current page */
30 static int prows, pcols; /* the dimensions of current page */
32 static int num = 1;
33 static struct termios termios;
34 static char filename[256];
35 static int mark[128]; /* mark page number */
36 static int mark_head[128]; /* mark head position */
37 static int zoom = 15;
38 static int rotate;
39 static int head;
40 static int left;
41 static int count;
43 static void draw(void)
45 int i;
46 for (i = head; i < MIN(head + fb_rows(), PDFROWS); i++)
47 fb_set(i - head, 0, pbuf + i * PDFCOLS + left, fb_cols());
50 static int showpage(int p, int h)
52 if (p < 1 || p > doc_pages(doc))
53 return 0;
54 memset(pbuf, 0x00, sizeof(pbuf));
55 prows = PDFROWS;
56 pcols = PDFCOLS;
57 doc_draw(doc, p, zoom, rotate, pbuf, &prows, &pcols);
58 num = p;
59 head = h;
60 draw();
61 return 0;
64 static void zoom_page(int z)
66 int _zoom = zoom;
67 zoom = z;
68 showpage(num, MIN(PDFROWS - fb_rows(), head * zoom / _zoom));
71 static int readkey(void)
73 unsigned char b;
74 if (read(STDIN_FILENO, &b, 1) <= 0)
75 return -1;
76 return b;
79 static int getcount(int def)
81 int result = count ? count : def;
82 count = 0;
83 return result;
86 static void printinfo(void)
88 printf("\x1b[H");
89 printf("FBPDF: file:%s page:%d(%d) zoom:%d%% \x1b[K",
90 filename, num, doc_pages(doc), zoom * 10);
91 fflush(stdout);
94 static void term_setup(void)
96 struct termios newtermios;
97 tcgetattr(STDIN_FILENO, &termios);
98 newtermios = termios;
99 newtermios.c_lflag &= ~ICANON;
100 newtermios.c_lflag &= ~ECHO;
101 tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtermios);
104 static void term_cleanup(void)
106 tcsetattr(STDIN_FILENO, 0, &termios);
109 static void sigcont(int sig)
111 term_setup();
114 static void reload(void)
116 doc_close(doc);
117 doc = doc_open(filename);
118 showpage(num, head);
121 static int rightmost(void)
123 int ret = 0;
124 int i, j;
125 for (i = 0; i < prows; i++) {
126 j = PDFCOLS - 1;
127 while (j > ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
128 j--;
129 while (j > ret && pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
130 j--;
131 if (ret < j)
132 ret = j;
134 return ret;
137 static int leftmost(void)
139 int ret = PDFCOLS;
140 int i, j;
141 for (i = 0; i < prows; i++) {
142 j = 0;
143 while (j < ret && pbuf[i * PDFCOLS + j] == FB_VAL(0, 0, 0))
144 j++;
145 while (j < ret && pbuf[i * PDFCOLS + j] == FB_VAL(255, 255, 255))
146 j++;
147 if (ret > j)
148 ret = j;
150 return ret;
153 static void mainloop(void)
155 int step = fb_rows() / PAGESTEPS;
156 int hstep = fb_cols() / PAGESTEPS;
157 int c, c2;
158 term_setup();
159 signal(SIGCONT, sigcont);
160 showpage(num, 0);
161 while ((c = readkey()) != -1) {
162 switch (c) {
163 case CTRLKEY('f'):
164 case 'J':
165 showpage(num + getcount(1), 0);
166 break;
167 case CTRLKEY('b'):
168 case 'K':
169 showpage(num - getcount(1), 0);
170 break;
171 case 'G':
172 showpage(getcount(doc_pages(doc)), 0);
173 break;
174 case 'z':
175 zoom_page(getcount(15));
176 break;
177 case 'w':
178 zoom_page(zoom * fb_cols() / pcols);
179 break;
180 case 'f':
181 zoom_page(zoom * fb_rows() / prows);
182 break;
183 case 'r':
184 rotate = getcount(0);
185 showpage(num, 0);
186 break;
187 case 'i':
188 printinfo();
189 break;
190 case 'q':
191 term_cleanup();
192 return;
193 case 27:
194 count = 0;
195 break;
196 case 'm':
197 c2 = readkey();
198 if (isalpha(c2)) {
199 mark[c2] = num;
200 mark_head[c2] = head / zoom;
202 break;
203 case 'e':
204 reload();
205 break;
206 case '`':
207 case '\'':
208 c2 = readkey();
209 if (isalpha(c2) && mark[c2])
210 showpage(mark[c2], c == '`' ? mark_head[c2] * zoom : 0);
211 break;
212 default:
213 if (isdigit(c))
214 count = count * 10 + c - '0';
216 switch (c) {
217 case 'j':
218 head += step * getcount(1);
219 break;
220 case 'k':
221 head -= step * getcount(1);
222 break;
223 case 'l':
224 left += hstep * getcount(1);
225 break;
226 case 'h':
227 left -= hstep * getcount(1);
228 break;
229 case 'H':
230 head = 0;
231 break;
232 case 'L':
233 head = MAX(0, prows - fb_rows());
234 break;
235 case 'M':
236 head = prows / 2;
237 break;
238 case ' ':
239 case CTRL('d'):
240 head += fb_rows() * getcount(1) - step;
241 break;
242 case 127:
243 case CTRL('u'):
244 head -= fb_rows() * getcount(1) - step;
245 break;
246 case '[':
247 left = leftmost() - hstep / 2;
248 break;
249 case ']':
250 left = rightmost() + hstep / 2 - fb_cols();
251 break;
252 case CTRLKEY('l'):
253 break;
254 default:
255 /* no need to redraw */
256 continue;
258 head = MAX(0, MIN(PDFROWS - fb_rows(), head));
259 left = MAX(0, MIN(PDFCOLS - fb_cols(), left));
260 draw();
264 static char *usage =
265 "usage: fbpdf [-r rotation] [-z zoom x10] [-p page] filename\n";
267 int main(int argc, char *argv[])
269 char *hide = "\x1b[?25l";
270 char *show = "\x1b[?25h";
271 char *clear = "\x1b[2J";
272 int i = 1;
273 if (argc < 2) {
274 printf(usage);
275 return 1;
277 strcpy(filename, argv[argc - 1]);
278 doc = doc_open(filename);
279 if (!doc) {
280 fprintf(stderr, "cannot open <%s>\n", filename);
281 return 1;
283 while (i + 2 < argc && argv[i][0] == '-') {
284 if (argv[i][1] == 'r')
285 rotate = atoi(argv[i + 1]);
286 if (argv[i][1] == 'z')
287 zoom = atoi(argv[i + 1]);
288 if (argv[i][1] == 'p')
289 num = atoi(argv[i + 1]);
290 i += 2;
293 write(STDIN_FILENO, hide, strlen(hide));
294 write(STDOUT_FILENO, clear, strlen(clear));
295 printinfo();
296 if (fb_init())
297 return 1;
298 left = (PDFCOLS - fb_cols()) / 2;
299 if (FBM_BPP(fb_mode()) != sizeof(fbval_t))
300 fprintf(stderr, "fbpdf: fbval_t doesn't match fb depth\n");
301 else
302 mainloop();
303 fb_free();
304 write(STDIN_FILENO, show, strlen(show));
305 printf("\n");
306 doc_close(doc);
307 return 0;