Use select instead of poll
[llpp.git] / link.c
blob32dbefb17cf153ccd02366d07d4f13a698916531
1 /* lot's of code c&p-ed directly from mupdf */
2 #ifdef _WIN32
3 #define WIN32_LEAN_AND_MEAN
4 #include <windows.h>
5 #include <winsock2.h>
6 #define ssize_t int
7 #pragma warning (disable:4244)
8 #pragma warning (disable:4996)
9 #pragma warning (disable:4995)
10 #endif
12 #ifdef _MSC_VER
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdlib.h>
17 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
19 va_list ap;
20 int errcode;
22 errcode = errno;
23 va_start (ap, fmt);
24 vfprintf (stderr, fmt, ap);
25 va_end (ap);
26 fprintf (stderr, ": %s\n", strerror (errno));
27 exit (exitcode);
29 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
31 va_list ap;
32 int errcode;
34 errcode = errno;
35 va_start (ap, fmt);
36 vfprintf (stderr, fmt, ap);
37 va_end (ap);
38 fputc ('\n', stderr);
39 exit (exitcode);
41 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
43 va_list ap;
44 int errcode;
46 errcode = errno;
47 va_start (ap, fmt);
48 vfprintf (stderr, fmt, ap);
49 va_end (ap);
50 fprintf (stderr, ": wsaerror %lx\n", WSAGetLastError ());
51 exit (exitcode);
53 #else
54 #define _GNU_SOURCE
55 #include <err.h>
56 #define sockerr err
57 #endif
58 #include <regex.h>
59 #include <errno.h>
60 #include <ctype.h>
61 #include <stdio.h>
62 #include <stdarg.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #ifndef _WIN32
66 #include <pthread.h>
67 #include <sys/time.h>
68 #include <sys/types.h>
69 #include <sys/socket.h>
70 #include <sys/select.h>
71 #endif
73 /* fugly as hell and GCC specific but... */
74 #ifdef _BIG_ENDIAN
75 #define GL_GLEXT_PROTOTYPES
76 #endif
78 #include <GL/gl.h>
79 #ifndef _WIN32
80 #include <GL/glext.h>
81 #else
82 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
83 #define GL_FRAGMENT_SHADER_ATI 0x8920
84 #define GL_UNSIGNED_INT_8_8_8_8 0x8035
85 #endif
87 #include <caml/fail.h>
88 #include <caml/alloc.h>
89 #include <caml/memory.h>
90 #include <caml/unixsupport.h>
92 #include <fitz.h>
93 #include <mupdf.h>
95 #if 0
96 #define lprintf printf
97 #else
98 #define lprintf(...)
99 #endif
101 #define ARSERT(cond) for (;;) { \
102 if (!(cond)) { \
103 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
105 break; \
108 struct slice {
109 int texindex;
110 int w, h;
113 struct page {
114 int pageno;
115 int slicecount;
116 fz_textspan *text;
117 fz_pixmap *pixmap;
118 pdf_page *drawpage;
119 struct pagedim *pagedim;
120 struct page *prev;
121 struct slice slices[];
124 struct pagedim {
125 int pageno;
126 int rotate;
127 fz_rect box;
128 fz_bbox bbox;
129 fz_matrix ctm;
132 struct {
133 int sock;
134 int sliceheight;
135 struct page *pages;
136 struct pagedim *pagedims;
137 int pagecount;
138 int pagedimcount;
139 pdf_xref *xref;
140 fz_glyphcache *cache;
141 int w, h;
143 int useatifs;
145 int texindex;
146 int texcount;
147 GLuint *texids;
149 GLenum texform;
150 GLenum texty;
152 int lotsamemory;
154 int *pagetbl;
155 struct {
156 int w, h;
157 struct slice *slice;
158 } *texowners;
160 #ifdef _WIN32
161 HANDLE thread;
162 #else
163 pthread_t thread;
164 #endif
165 } state;
167 #ifdef _WIN32
168 static CRITICAL_SECTION critsec;
170 static void lock (void *unused)
172 (void) unused;
173 EnterCriticalSection (&critsec);
176 static void unlock (void *unused)
178 (void) unused;
179 LeaveCriticalSection (&critsec);
182 static int trylock (void *unused)
184 return TryEnterCriticalSection (&critsec) == 0;
186 #else
187 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
189 static void lock (const char *cap)
191 int ret = pthread_mutex_lock (&mutex);
192 if (ret) {
193 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
197 static void unlock (const char *cap)
199 int ret = pthread_mutex_unlock (&mutex);
200 if (ret) {
201 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
205 static int trylock (const char *cap)
207 int ret = pthread_mutex_trylock (&mutex);
209 if (ret && ret != EBUSY) {
210 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
212 return ret == EBUSY;
214 #endif
216 static void *parse_pointer (const char *cap, const char *s)
218 int ret;
219 void *ptr;
221 ret = sscanf (s, "%p", &ptr);
222 if (ret != 1) {
223 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
225 return ptr;
228 static int hasdata (int sock)
230 int n;
231 fd_set s;
232 struct timeval tv;
233 FD_ZERO (&s);
234 FD_SET (sock, &s);
235 tv.tv_sec = 0;
236 tv.tv_usec = 0;
237 n = select (sock + 1, &s, NULL, NULL, &tv);
238 if (n == 0) return 0;
239 if (n == 1) return 1;
240 sockerr (1, "hasdata: select error ret=%d", n);
243 static double now (void)
245 struct timeval tv;
247 if (gettimeofday (&tv, NULL)) {
248 err (1, "gettimeofday");
250 return tv.tv_sec + tv.tv_usec*1e-6;
253 static void readdata (int fd, char *p, int size)
255 ssize_t n;
257 n = recv (fd, p, size, 0);
258 if (n - size) {
259 if (!n) errx (1, "EOF while reading");
260 sockerr (1, "recv (req %d, ret %zd)", size, n);
264 static void writedata (int fd, char *p, int size)
266 char buf[4];
267 ssize_t n;
269 buf[0] = (size >> 24) & 0xff;
270 buf[1] = (size >> 16) & 0xff;
271 buf[2] = (size >> 8) & 0xff;
272 buf[3] = (size >> 0) & 0xff;
274 n = send (fd, buf, 4, 0);
275 if (n != 4) {
276 if (!n) errx (1, "EOF while writing length");
277 sockerr (1, "send %zd", n);
280 n = send (fd, p, size, 0);
281 if (n - size) {
282 if (!n) errx (1, "EOF while writing data");
283 sockerr (1, "send (req %d, ret %zd)", size, n);
287 static void
288 #ifdef __GNUC__
289 __attribute__ ((format (printf, 2, 3)))
290 #endif
291 printd (int fd, const char *fmt, ...)
293 int len;
294 va_list ap;
295 char buf[200];
297 va_start (ap, fmt);
298 len = vsnprintf (buf, sizeof (buf), fmt, ap);
299 va_end (ap);
300 writedata (fd, buf, len);
303 static void die (fz_error error)
305 fz_catch (error, "aborting");
306 if (state.xref)
307 pdf_closexref (state.xref);
308 exit (1);
311 static void openxref (char *filename)
313 int fd;
314 fz_error error;
315 fz_stream *file;
317 fd = open (filename, O_BINARY | O_RDONLY, 0666);
318 if (fd < 0)
319 die (fz_throw ("cannot open file '%s'", filename));
321 file = fz_openfile (fd);
322 state.xref = pdf_openxref (file);
323 if (!state.xref)
324 die (fz_throw ("cannot open PDF file '%s'", filename));
325 fz_dropstream (file);
327 if (pdf_needspassword (state.xref)) {
328 die (fz_throw ("password protected"));
331 error = pdf_loadpagetree (state.xref);
332 if (error) {
333 die (fz_throw ("cannot load page tree"));
336 state.pagecount = pdf_getpagecount (state.xref);
337 state.pagetbl = stat_alloc (state.pagecount * sizeof (*state.pagetbl));
340 static int readlen (int fd)
342 ssize_t n;
343 char p[4];
345 n = recv (fd, p, 4, 0);
346 if (n != 4) {
347 if (!n) errx (1, "EOF while reading length");
348 sockerr (1, "read %zd", n);
351 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
354 static void freepage (struct page *page)
356 int i;
357 struct page *p;
359 fz_droppixmap (page->pixmap);
360 for (p = state.pages; p; p = p->prev) {
361 if (p->prev == page) {
362 p->prev = page->prev;
363 break;
366 for (i = 0; i < page->slicecount; ++i) {
367 struct slice *s = &page->slices[i];
368 if (s->texindex != -1) {
369 if (state.texowners[s->texindex].slice == s) {
370 state.texowners[s->texindex].slice = NULL;
371 ARSERT (state.texowners[s->texindex].w == s->w);
372 ARSERT (state.texowners[s->texindex].h >= s->h);
376 if (page->text) {
377 fz_freetextspan (page->text);
379 if (page->drawpage) {
380 pdf_freepage (page->drawpage);
383 free (page);
386 static void subdivide (struct page *p)
388 int i;
389 int h = p->pixmap->h;
390 int th = MIN (h, state.sliceheight);
392 for (i = 0; i < p->slicecount; ++i) {
393 struct slice *s = &p->slices[i];
394 s->texindex = -1;
395 s->h = MIN (th, h);
396 s->w = p->pixmap->w;
397 h -= th;
401 static void *render (int pageno, int pindex)
403 fz_error error;
404 int slicecount;
405 fz_obj *pageobj;
406 struct page *page;
407 double start, end;
408 pdf_page *drawpage;
409 fz_device *idev;
410 struct pagedim *pagedim;
412 start = now ();
413 printd (state.sock, "V rendering %d", pageno);
414 pdf_flushxref (state.xref, 0);
416 pagedim = &state.pagedims[pindex];
417 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
418 + state.sliceheight - 1) / state.sliceheight;
419 slicecount += slicecount == 0;
421 page = calloc (sizeof (*page)
422 + (slicecount * sizeof (struct slice)), 1);
423 if (!page) {
424 err (1, "calloc page %d\n", pageno);
426 page->slicecount = slicecount;
427 page->prev = state.pages;
428 state.pages = page;
430 pageobj = pdf_getpageobject (state.xref, pageno);
431 if (!pageobj)
432 die (fz_throw ("cannot retrieve info from page %d", pageno));
434 error = pdf_loadpage (&drawpage, state.xref, pageobj);
435 if (error)
436 die (error);
438 page->pixmap = fz_newpixmapwithrect (pdf_devicergb, pagedim->bbox);
439 if (error)
440 die (error);
441 fz_clearpixmap (page->pixmap, 0xFF);
443 idev = fz_newdrawdevice (state.cache, page->pixmap);
444 if (!idev)
445 die (fz_throw ("fz_newdrawdevice failed"));
446 error = pdf_runcontentstream (idev, pagedim->ctm, state.xref,
447 drawpage->resources,
448 drawpage->contents);
449 fz_freedevice (idev);
451 page->drawpage = drawpage;
452 page->pagedim = pagedim;
453 page->pageno = pageno;
454 subdivide (page);
455 end = now ();
457 if (!state.lotsamemory) {
458 pdf_agestoreditems (state.xref->store);
459 pdf_evictageditems (state.xref->store);
462 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
463 return page;
466 static void initpdims (void)
468 int pageno;
469 double start, end;
471 start = now ();
472 for (pageno = 0; pageno < state.pagecount; ++pageno) {
473 int rotate;
474 fz_rect box;
475 struct pagedim *p;
476 fz_obj *obj, *pageobj;
478 pageobj = pdf_getpageobject (state.xref, pageno + 1);
480 obj = fz_dictgets (pageobj, "CropBox");
481 if (!fz_isarray (obj)) {
482 obj = fz_dictgets (pageobj, "MediaBox");
483 if (!fz_isarray (obj)) {
484 die (fz_throw ("cannot find page bounds %d (%d Rd)",
485 fz_tonum (pageobj), fz_togen (pageobj)));
488 box = pdf_torect (obj);
490 obj = fz_dictgets (pageobj, "Rotate");
491 if (fz_isint (obj)) {
492 rotate = fz_toint (obj);
494 else {
495 rotate = 0;
498 state.pagetbl[pageno] = fz_tonum (state.xref->pagerefs[pageno]);
499 p = &state.pagedims[state.pagedimcount - 1];
500 if ((state.pagedimcount == 0)
501 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
502 size_t size;
504 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
505 state.pagedims = realloc (state.pagedims, size);
506 if (!state.pagedims) {
507 err (1, "realloc pagedims to %zu (%d elems)",
508 size, state.pagedimcount + 1);
510 p = &state.pagedims[state.pagedimcount++];
511 p->rotate = rotate;
512 p->box = box;
513 p->pageno = pageno;
516 end = now ();
517 printd (state.sock, "T Processed %d pages in %f seconds",
518 state.pagecount, end - start);
521 static void layout (void)
523 int pindex;
524 fz_matrix ctm;
525 fz_rect box, box2;
526 double zoom, w;
527 struct pagedim *p = state.pagedims;
529 pindex = 0;
530 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
531 box.x0 = MIN (p->box.x0, p->box.x1);
532 box.y0 = MIN (p->box.y0, p->box.y1);
533 box.x1 = MAX (p->box.x0, p->box.x1);
534 box.y1 = MAX (p->box.y0, p->box.y1);
536 ctm = fz_identity ();
537 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
538 ctm = fz_concat (ctm, fz_rotate (p->rotate));
539 box2 = fz_transformrect (ctm, box);
540 w = box2.x1 - box2.x0;
542 zoom = (state.w / w);
543 ctm = fz_identity ();
544 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
545 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
546 ctm = fz_concat (ctm, fz_rotate (p->rotate));
547 p->bbox = fz_roundrect (fz_transformrect (ctm, box));
548 memcpy (&p->ctm, &ctm, sizeof (ctm));
551 while (p-- != state.pagedims) {
552 printd (state.sock, "l %d %d %d",
553 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
557 static void recurse_outline (pdf_outline *outline, int level)
559 while (outline) {
560 int i, num;
561 fz_obj *obj;
562 int top = 0;
563 int pageno = -1;
565 if (!outline->link) goto next;
567 obj = outline->link->dest;
568 if (fz_isindirect (obj)) {
569 num = fz_tonum (obj);
571 for (i = 0; i < state.pagecount; ++i) {
572 if (state.pagetbl[i] == num) {
573 pageno = i;
574 break;
578 else if (fz_isarray (obj)) {
579 fz_obj *obj2;
581 obj2 = fz_arrayget (obj, 0);
582 if (fz_isint (obj2)) {
583 pageno = fz_toint (obj2);
585 else {
586 num = fz_tonum (obj2);
587 for (i = 0; i < state.pagecount; ++i) {
588 if (state.pagetbl[i] == num) {
589 pageno = i;
590 break;
595 if (fz_arraylen (obj) > 3) {
596 fz_point p;
597 struct pagedim *pagedim = state.pagedims;
599 for (i = 0; i < state.pagedimcount; ++i) {
600 if (state.pagedims[i].pageno > pageno)
601 break;
602 pagedim = &state.pagedims[i];
605 p.x = fz_toint (fz_arrayget (obj, 2));
606 p.y = fz_toint (fz_arrayget (obj, 3));
607 p = fz_transformpoint (pagedim->ctm, p);
608 top = p.y;
612 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
613 printd (state.sock, "o %d %d %d %s",
614 level, pageno, top, outline->title);
615 next:
616 if (outline->child) {
617 recurse_outline (outline->child, level + 1);
619 outline = outline->next;
623 static void process_outline (void)
625 pdf_outline *outline;
627 outline = pdf_loadoutline (state.xref);
628 if (outline) {
629 recurse_outline (outline, 0);
630 pdf_freeoutline (outline);
634 static int comparespans (const void *l, const void *r)
636 #ifdef _MSC_VER
637 fz_textspan const**ls = l;
638 fz_textspan const**rs = r;
639 #else
640 fz_textspan *const*ls = l;
641 fz_textspan *const*rs = r;
642 #endif
643 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
646 /* wishful thinking function */
647 static void search (regex_t *re, int pageno, int y, int forward)
649 int i, j;
650 int ret;
651 char *p;
652 char buf[256];
653 fz_error error;
654 fz_obj *pageobj;
655 fz_device *tdev;
656 pdf_page *drawpage;
657 fz_textspan *text, *span, **pspan;
658 struct pagedim *pdim, *pdimprev;
659 int stop = 0;
660 int niters = 0;
661 int nspans;
662 double start, end;
664 start = now ();
665 while (pageno >= 0 && pageno < state.pagecount && !stop) {
666 if (niters++ == 5) {
667 if (!state.lotsamemory) {
668 pdf_agestoreditems (state.xref->store);
669 pdf_evictageditems (state.xref->store);
671 niters = 0;
672 if (hasdata (state.sock)) {
673 printd (state.sock, "T attention requested aborting search at %d",
674 pageno);
675 stop = 1;
677 else {
678 printd (state.sock, "T searching in page %d", pageno);
681 pdimprev = NULL;
682 for (i = 0; i < state.pagedimcount; ++i) {
683 pdim = &state.pagedims[i];
684 if (pdim->pageno == pageno) {
685 goto found;
687 if (pdim->pageno > pageno) {
688 pdim = pdimprev;
689 goto found;
691 pdimprev = pdim;
693 pdim = pdimprev;
694 found:
696 pageobj = pdf_getpageobject (state.xref, pageno + 1);
697 if (!pageobj)
698 die (fz_throw ("cannot retrieve info from page %d", pageno));
700 error = pdf_loadpage (&drawpage, state.xref, pageobj);
701 if (error)
702 die (error);
704 text = fz_newtextspan ();
705 tdev = fz_newtextdevice (text);
706 error = pdf_runcontentstream (tdev, pdim->ctm, state.xref,
707 drawpage->resources,
708 drawpage->contents);
709 if (error) die (error);
710 fz_freedevice (tdev);
712 nspans = 0;
713 for (span = text; span; span = span->next) {
714 nspans++;
716 pspan = malloc (sizeof (void *) * nspans);
717 if (!pspan) {
718 err (1, "malloc span pointers %zu", sizeof (void *) * nspans);
720 for (i = 0, span = text; span; span = span->next, ++i) {
721 pspan[i] = span;
723 qsort (pspan, nspans, sizeof (fz_textspan *), comparespans);
725 j = forward ? 0 : nspans - 1;
726 while (nspans--) {
727 regmatch_t rm;
729 span = pspan[j];
730 j += forward ? 1 : -1;
731 p = buf;
732 /* XXX: spans are not sorted "visually" */
733 for (i = 0; i < MIN (span->len, sizeof (buf) - 1); ++i) {
734 if (forward) {
735 if (span->text[i].bbox.y0 < y + 1) {
736 continue;
739 else {
740 if (span->text[i].bbox.y0 > y - 1) {
741 continue;
744 if (span->text[i].c < 256) {
745 *p++ = span->text[i].c;
747 else {
748 *p++ = '?';
751 if (p == buf) {
752 continue;
754 *p++ = 0;
756 ret = regexec (re, buf, 1, &rm, 0);
757 if (ret) {
758 if (ret != REG_NOMATCH) {
759 size_t size;
760 char errbuf[80];
761 size = regerror (ret, re, errbuf, sizeof (errbuf));
762 printd (state.sock,
763 "T regexec error `%.*s'",
764 (int) size, errbuf);
765 fz_freetextspan (text);
766 pdf_freepage (drawpage);
767 free (pspan);
768 return;
771 else {
772 fz_rect r;
774 r.x0 = span->text[rm.rm_so].bbox.x0 - pdim->bbox.x0;
775 r.y0 = span->text[rm.rm_so].bbox.y0;
776 r.x1 = span->text[rm.rm_eo - 1].bbox.x1 - pdim->bbox.x0;
777 r.y1 = span->text[rm.rm_eo - 1].bbox.y1;
779 if (!stop) {
780 printd (state.sock, "F %d %d %f %f %f %f",
781 pageno, 1,
782 r.x0, r.y0,
783 r.x1, r.y1);
785 else {
786 printd (state.sock, "R %d %d %f %f %f %f",
787 pageno, 2,
788 r.x0, r.y0,
789 r.x1, r.y1);
791 printd (state.sock, "T found at %d `%.*s' %f in %f sec",
792 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
793 span->text[0].bbox.y0 - drawpage->mediabox.y0,
794 now () - start);
795 stop = 1;
798 if (forward) {
799 pageno += 1;
800 y = 0;
802 else {
803 pageno -= 1;
804 y = INT_MAX;
806 fz_freetextspan (text);
807 pdf_freepage (drawpage);
808 free (pspan);
810 end = now ();
811 if (!stop) {
812 printd (state.sock, "T no matches %f sec", end - start);
814 printd (state.sock, "D");
817 static
818 #ifdef _WIN32
819 DWORD _stdcall
820 #else
821 void *
822 #endif
823 mainloop (void *unused)
825 char *p = NULL;
826 int len, ret, oldlen = 0;
828 for (;;) {
829 len = readlen (state.sock);
830 if (len == 0) {
831 errx (1, "readlen returned 0");
834 if (oldlen < len + 1) {
835 p = realloc (p, len + 1);
836 if (!p) {
837 err (1, "realloc %d failed", len + 1);
839 oldlen = len + 1;
841 readdata (state.sock, p, len);
842 p[len] = 0;
844 if (!strncmp ("open", p, 4)) {
845 fz_obj *obj;
846 char *filename = p + 5;
848 openxref (filename);
849 initpdims ();
851 obj = fz_dictgets (state.xref->trailer, "Info");
852 if (obj) {
853 obj = fz_dictgets (obj, "Title");
854 if (obj) {
855 printd (state.sock, "t %s", pdf_toutf8 (obj));
859 else if (!strncmp ("free", p, 4)) {
860 void *ptr;
862 ret = sscanf (p + 4, " %p", &ptr);
863 if (ret != 1) {
864 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
866 lock ("free");
867 freepage (ptr);
868 unlock ("free");
869 printd (state.sock, "d");
871 else if (!strncmp ("search", p, 6)) {
872 int icase, pageno, y, ret, len2, forward;
873 char *pattern;
874 regex_t re;
876 ret = sscanf (p + 6, " %d %d %d %d,%n",
877 &icase, &pageno, &y, &forward, &len2);
878 if (ret != 4) {
879 errx (1, "malformed search `%s' ret=%d", p, ret);
882 pattern = p + 6 + len2;
883 ret = regcomp (&re, pattern,
884 REG_EXTENDED | (icase ? REG_ICASE : 0));
885 if (ret) {
886 char errbuf[80];
887 size_t size;
889 size = regerror (ret, &re, errbuf, sizeof (errbuf));
890 printd (state.sock, "T regcomp failed `%.*s'",
891 (int) size, errbuf);
893 else {
894 search (&re, pageno, y, forward);
895 regfree (&re);
898 else if (!strncmp ("geometry", p, 8)) {
899 int w, h;
901 printd (state.sock, "c");
902 ret = sscanf (p + 8, " %d %d", &w, &h);
903 if (ret != 2) {
904 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
906 state.h = h;
907 if (w != state.w) {
908 int i;
909 state.w = w;
910 for (i = 0; i < state.texcount; ++i) {
911 state.texowners[i].slice = NULL;
914 lock ("geometry");
915 layout ();
916 process_outline ();
917 unlock ("geometry");
918 printd (state.sock, "C %d", state.pagecount);
920 else if (!strncmp ("render", p, 6)) {
921 int pageno, pindex, w, h, ret;
922 struct page *page;
924 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
925 if (ret != 4) {
926 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
929 page = render (pageno, pindex);
930 printd (state.sock, "r %d %d %d %p\n",
931 pageno,
932 state.w,
933 state.h,
934 page);
936 else {
937 errx (1, "unknown command %.*s", len, p);
940 return 0;
943 static void upload2 (struct page *page, int slicenum, const char *cap)
945 int i;
946 int w, h;
947 double start, end;
948 struct slice *slice = &page->slices[slicenum];
950 w = page->pixmap->w;
951 h = page->pixmap->h;
953 ARSERT (w == slice->w);
954 if (slice->texindex != -1
955 && state.texowners[slice->texindex].slice == slice) {
956 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
958 else {
959 int subimage = 0;
960 int index = (state.texindex++ % state.texcount);
961 size_t offset = 0;
963 for (i = 0; i < slicenum; ++i) {
964 offset += w * page->slices[i].h * 4;
967 if (state.texowners[index].w == slice->w) {
968 if (state.texowners[index].h >= slice->h ) {
969 subimage = 1;
971 else {
972 state.texowners[index].h = slice->h;
975 else {
976 state.texowners[index].h = slice->h;
979 state.texowners[index].slice = slice;
980 state.texowners[index].w = slice->w;
981 slice->texindex = index;
983 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
984 start = now ();
985 if (subimage) {
987 GLenum err = glGetError ();
988 if (err != GL_NO_ERROR) {
989 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
990 abort ();
993 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
998 slice->h,
999 state.texform,
1000 state.texty,
1001 page->pixmap->samples + offset
1004 GLenum err = glGetError ();
1005 if (err != GL_NO_ERROR) {
1006 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1007 abort ();
1011 else {
1012 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1014 GL_RGBA8,
1016 slice->h,
1018 state.texform,
1019 state.texty,
1020 page->pixmap->samples + offset
1024 end = now ();
1025 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1026 subimage ? "sub" : "img",
1027 page->pageno, slicenum,
1028 slice->w, slice->h,
1029 state.texids[slice->texindex],
1030 end - start);
1034 CAMLprim value ml_draw (value dispy_v, value w_v, value h_v,
1035 value py_v, value ptr_v)
1037 CAMLparam5 (dispy_v, w_v, h_v, py_v, ptr_v);
1038 int dispy = Int_val (dispy_v);
1039 int w = Int_val (w_v);
1040 int h = Int_val (h_v);
1041 int py = Int_val (py_v);
1042 char *s = String_val (ptr_v);
1043 int ret;
1044 void *ptr;
1045 struct page *page;
1046 int slicenum = 0;
1048 if (trylock ("ml_draw")) {
1049 goto done;
1052 ret = sscanf (s, "%p", &ptr);
1053 if (ret != 1) {
1054 errx (1, "cannot parse pointer `%s'", s);
1056 page = ptr;
1058 w = page->pixmap->w;
1060 ARSERT (h >= 0 && "ml_draw wrong h");
1062 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1063 if (state.useatifs) {
1064 glEnable (GL_FRAGMENT_SHADER_ATI);
1067 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1068 struct slice *slice = &page->slices[slicenum];
1069 if (slice->h > py) {
1070 break;
1072 py -= slice->h;
1075 h = MIN (state.h, h);
1076 while (h) {
1077 int th;
1078 struct slice *slice = &page->slices[slicenum];
1080 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1082 th = MIN (h, slice->h - py);
1083 upload2 (page, slicenum, "upload");
1085 glBegin (GL_QUADS);
1087 glTexCoord2i (0, py);
1088 glVertex2i (0, dispy);
1090 glTexCoord2i (w, py);
1091 glVertex2i (w, dispy);
1093 glTexCoord2i (w, py+th);
1094 glVertex2i (w, dispy + th);
1096 glTexCoord2i (0, py+th);
1097 glVertex2i (0, dispy + th);
1099 glEnd ();
1101 h -= th;
1102 py = 0;
1103 dispy += th;
1104 slicenum += 1;
1107 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1108 if (state.useatifs) {
1109 glDisable (GL_FRAGMENT_SHADER_ATI);
1112 unlock ("ml_draw");
1113 done:
1114 CAMLreturn (Val_unit);
1117 static pdf_link *getlink (struct page *page, int x, int y)
1119 fz_point p;
1120 fz_matrix ctm;
1121 pdf_link *link;
1123 p.x = x;
1124 p.y = y;
1126 ctm = fz_invertmatrix (page->pagedim->ctm);
1127 p = fz_transformpoint (ctm, p);
1129 for (link = page->drawpage->links; link; link = link->next) {
1130 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1131 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1132 if (link->kind == PDF_LGOTO) {
1133 return link;
1138 return NULL;
1141 CAMLprim value ml_checklink (value ptr_v, value x_v, value y_v)
1143 CAMLparam3 (ptr_v, x_v, y_v);
1144 char *s = String_val (ptr_v);
1145 int ret;
1147 if (trylock ("ml_checklink")) {
1148 ret = 0;
1150 else {
1151 ret = NULL != getlink (parse_pointer ("ml_checklink", s),
1152 Int_val (x_v), Int_val (y_v));
1153 unlock ("ml_checklink");
1155 CAMLreturn (Val_bool (ret));
1158 CAMLprim value ml_getlink (value ptr_v, value x_v, value y_v)
1160 CAMLparam3 (ptr_v, x_v, y_v);
1161 CAMLlocal2 (ret_v, tup_v);
1162 pdf_link *link;
1163 struct page *page;
1164 char *s = String_val (ptr_v);
1166 if (trylock ("ml_getlink")) {
1167 ret_v = Val_int (0);
1168 goto done;
1171 page = parse_pointer ("ml_getlink", s);
1173 link = getlink (page, Int_val (x_v), Int_val (y_v));
1174 if (link) {
1175 int pageno;
1176 fz_point p;
1177 fz_obj *obj;
1179 pageno = -1;
1180 obj = fz_arrayget (link->dest, 0);
1181 if (fz_isindirect (obj)) {
1182 pageno = pdf_findpageobject (state.xref, obj) - 1;
1184 else if (fz_isint (obj)) {
1185 pageno = fz_toint (obj);
1188 if (fz_arraylen (link->dest) > 3) {
1189 p.x = fz_toint (fz_arrayget (link->dest, 2));
1190 p.y = fz_toint (fz_arrayget (link->dest, 3));
1191 p = fz_transformpoint (page->pagedim->ctm, p);
1193 else {
1194 p.x = 0.0;
1195 p.y = 0.0;
1198 tup_v = caml_alloc_tuple (2);
1199 ret_v = caml_alloc_small (1, 1);
1200 Field (tup_v, 0) = Val_int (pageno);
1201 Field (tup_v, 1) = Val_int (p.y);
1202 Field (ret_v, 0) = tup_v;
1204 else {
1205 ret_v = Val_int (0);
1207 unlock ("ml_getlink");
1209 done:
1210 CAMLreturn (ret_v);
1213 CAMLprim value ml_gettext (value ptr_v, value rect_v, value oy_v, value rectsel_v)
1215 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1216 fz_matrix ctm;
1217 fz_point p1, p2;
1218 struct page *page;
1219 fz_textspan *span;
1220 char *s = String_val (ptr_v);
1221 int rectsel = Bool_val (rectsel_v);
1222 int i, bx0, bx1, by0, by1, x0, x1, y0, y1, oy;
1224 /* stop GCC from complaining about uninitialized variables */
1225 #ifdef __GNUC__
1226 int rx0 = rx0, rx1 = rx1, ry0 = ry0, ry1 = ry1;
1227 #else
1228 int rx0, rx1, ry0, ry1;
1229 #endif
1231 if (trylock ("ml_gettext")) {
1232 goto done;
1235 page = parse_pointer ("ml_gettext", s);
1237 oy = Int_val (oy_v);
1238 p1.x = Int_val (Field (rect_v, 0));
1239 p1.y = Int_val (Field (rect_v, 1));
1240 p2.x = Int_val (Field (rect_v, 2));
1241 p2.y = Int_val (Field (rect_v, 3));
1243 if (0) {
1244 glEnable (GL_BLEND);
1245 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1246 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1247 glColor4f (0, 0, 0, 0.2);
1248 glRecti (p1.x, p1.y, p2.x, p2.y);
1249 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1250 glDisable (GL_BLEND);
1253 ctm = page->pagedim->ctm;
1254 if (!page->text) {
1255 fz_error error;
1256 fz_device *tdev;
1258 page->text = fz_newtextspan ();
1259 tdev = fz_newtextdevice (page->text);
1260 error = pdf_runcontentstream (tdev, page->pagedim->ctm, state.xref,
1261 page->drawpage->resources,
1262 page->drawpage->contents);
1263 if (error) die (error);
1264 fz_freedevice (tdev);
1267 printf ("\033c");
1269 printf ("BBox %f %f %f %f\n", p1.x, p1.y, p2.x, p2.y);
1270 p1.x += page->pixmap->x;
1271 p1.y += page->pixmap->y;
1272 p2.x += page->pixmap->x;
1273 p2.y += page->pixmap->y;
1274 x0 = p1.x;
1275 y0 = p1.y;
1276 x1 = p2.x;
1277 y1 = p2.y;
1278 printf ("BBox %d %d %d %d %d %d\n", x0, y0, x1, y1, oy, page->pageno);
1280 for (span = page->text; span; span = span->next) {
1281 int seen = 0;
1283 /* fz_debugtextspanxml (span); */
1284 for (i = 0; i < span->len; ++i) {
1285 long c;
1287 bx0 = span->text[i].bbox.x0;
1288 bx1 = span->text[i].bbox.x1;
1289 by0 = span->text[i].bbox.y0 + oy;
1290 by1 = span->text[i].bbox.y1 + oy;
1292 if ((bx1 >= x0 && bx0 <= x1 && by1 >= y0 && by0 <= y1)) {
1293 if (!seen) {
1294 rx0 = bx0 - page->pixmap->x;
1295 rx1 = bx1 - page->pixmap->x;
1296 ry0 = by0;
1297 ry1 = by1;
1300 seen = 1;
1301 c = span->text[i].c;
1302 if (c < 256) {
1303 if ((isprint (c) && !isspace (c))) {
1304 if (!rectsel) {
1305 bx0 -= page->pixmap->x;
1306 bx1 -= page->pixmap->x;
1307 glEnable (GL_BLEND);
1308 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1309 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1310 glColor4f (0.5, 0.5, 0.0, 0.6);
1311 glRecti (bx0, by0, bx1, by1);
1312 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1313 glDisable (GL_BLEND);
1315 if (isprint (c) || c ==' ') {
1316 rx1 = bx1;
1317 ry1 = by1;
1320 putc (c, stdout);
1322 else {
1323 putc ('?', stdout);
1328 if (rectsel) {
1329 if (seen) {
1330 glEnable (GL_BLEND);
1331 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1332 glBlendFunc (GL_DST_ALPHA, GL_SRC_ALPHA);
1333 glColor4f (0.5, 0.5, 0.0, 0.6);
1334 glRecti (rx0, ry0, rx1, ry1);
1335 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1336 glDisable (GL_BLEND);
1340 if (seen && span->eol) {
1341 x0 = page->pixmap->x;
1342 putc ('\n', stdout);
1345 unlock ("ml_gettext");
1347 done:
1348 CAMLreturn (Val_unit);
1351 CAMLprim value ml_getpagewh (value pagedimno_v)
1353 CAMLparam1 (pagedimno_v);
1354 CAMLlocal1 (ret_v);
1355 int pagedimno = Int_val (pagedimno_v);
1357 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1358 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1359 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1360 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1361 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1362 CAMLreturn (ret_v);
1365 static void initgl (void)
1367 #ifdef _BIG_ENDIAN
1368 if (strstr ((char *) glGetString (GL_EXTENSIONS),
1369 "GL_ATI_fragment_shader")) {
1370 /* Here, with MESA, rv280, powerpc32: BGRA(rev) is slow while
1371 ABGR is fast, so fix things in the shader */
1372 state.texform = GL_ABGR_EXT;
1373 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1375 glBindFragmentShaderATI (1);
1376 glBeginFragmentShaderATI ();
1378 glSampleMapATI (GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI);
1380 glColorFragmentOp1ATI (GL_MOV_ATI,
1381 GL_REG_1_ATI, GL_RED_BIT_ATI, GL_NONE,
1382 GL_REG_0_ATI, GL_BLUE, GL_NONE);
1383 glColorFragmentOp1ATI (GL_MOV_ATI,
1384 GL_REG_1_ATI, GL_BLUE_BIT_ATI, GL_NONE,
1385 GL_REG_0_ATI, GL_RED, GL_NONE);
1386 glColorFragmentOp1ATI (
1387 GL_MOV_ATI,
1388 GL_REG_0_ATI, GL_RED_BIT_ATI | GL_BLUE_BIT_ATI, GL_NONE,
1389 GL_REG_1_ATI, GL_NONE, GL_NONE
1392 glEndFragmentShaderATI ();
1393 state.useatifs = 1;
1395 else {
1396 state.texform = GL_BGRA_EXT;
1397 state.texty = GL_UNSIGNED_INT_8_8_8_8_REV;
1399 #else
1400 state.texform = GL_BGRA_EXT;
1401 state.texty = GL_UNSIGNED_INT_8_8_8_8;
1402 #endif
1405 CAMLprim value ml_init (value sock_v)
1407 int ret;
1408 CAMLparam1 (sock_v);
1410 state.texcount = 256;
1411 state.sliceheight = 24;
1413 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1414 if (!state.texids) {
1415 err (1, "calloc texids %zu", state.texcount * sizeof (*state.texids));
1418 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1419 if (!state.texowners) {
1420 err (1, "calloc texowners %zu",
1421 state.texcount * sizeof (*state.texowners));
1424 glGenTextures (state.texcount, state.texids);
1426 #ifdef _WIN32
1427 state.sock = Socket_val (sock_v);
1428 #else
1429 state.sock = Int_val (sock_v);
1430 #endif
1431 initgl ();
1433 state.cache = fz_newglyphcache ();
1434 if (!state.cache) {
1435 errx (1, "fz_newglyphcache failed");
1438 #ifdef _WIN32
1439 InitializeCriticalSection (&critsec);
1440 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1441 if (state.thread == INVALID_HANDLE_VALUE) {
1442 errx (1, "CreateThread failed: %lx", GetLastError ());
1444 #else
1445 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1446 if (ret) {
1447 errx (1, "pthread_create: %s", strerror (errno));
1449 #endif
1451 CAMLreturn (Val_unit);