Do not track mouse outside of View/Birdseye
[llpp.git] / link.c
blobeef63cf9d79a966695797975f3c2a19ed9eb48af
1 /* lots 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 fionread_arg long
7 #define ssize_t int
8 #define FMT_ss "%d"
9 #ifdef _WIN64
10 #define FMT_s "%i64u"
11 #else
12 #define FMT_s "%u"
13 #endif
14 #pragma warning (disable:4244)
15 #pragma warning (disable:4996)
16 #pragma warning (disable:4995)
17 #endif
19 #ifdef _MSC_VER
20 #include <errno.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <stdlib.h>
24 static void __declspec (noreturn) err (int exitcode, const char *fmt, ...)
26 va_list ap;
27 int errcode;
29 errcode = errno;
30 va_start (ap, fmt);
31 vfprintf (stderr, fmt, ap);
32 va_end (ap);
33 fprintf (stderr, ": %s\n", strerror (errno));
34 exit (exitcode);
36 static void __declspec (noreturn) errx (int exitcode, const char *fmt, ...)
38 va_list ap;
40 va_start (ap, fmt);
41 vfprintf (stderr, fmt, ap);
42 va_end (ap);
43 fputc ('\n', stderr);
44 exit (exitcode);
46 static void __declspec (noreturn) sockerr (int exitcode, const char *fmt, ...)
48 va_list ap;
50 va_start (ap, fmt);
51 vfprintf (stderr, fmt, ap);
52 va_end (ap);
53 fprintf (stderr, ": wsaerror 0x%x\n", WSAGetLastError ());
54 exit (exitcode);
56 #else
57 #define FMT_ss "%zd"
58 #define FMT_s "%zu"
59 #define fionread_arg int
60 #define ioctlsocket ioctl
61 #define _GNU_SOURCE
62 #include <err.h>
63 #define sockerr err
64 #endif
65 #include <regex.h>
66 #include <errno.h>
67 #include <ctype.h>
68 #include <stdio.h>
69 #include <stdarg.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <limits.h>
73 #ifndef _WIN32
74 #include <pthread.h>
75 #include <sys/time.h>
76 #include <sys/types.h>
77 #include <sys/socket.h>
78 #include <sys/ioctl.h>
79 #endif
81 #ifdef __APPLE__
82 #include <OpenGL/gl.h>
83 #else
84 #include <GL/gl.h>
85 #endif
87 #ifndef GL_TEXTURE_RECTANGLE_ARB
88 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5
89 #endif
91 #include <caml/fail.h>
92 #include <caml/alloc.h>
93 #include <caml/memory.h>
94 #include <caml/unixsupport.h>
96 #include <fitz.h>
97 #include <mupdf.h>
99 #if 0
100 #define lprintf printf
101 #else
102 #define lprintf(...)
103 #endif
105 #ifdef FT_FREETYPE_H
106 #include FT_FREETYPE_H
107 #endif
109 #define ARSERT(cond) for (;;) { \
110 if (!(cond)) { \
111 errx (1, "%s:%d " #cond, __FILE__, __LINE__); \
113 break; \
116 struct slice {
117 int texindex;
118 int w, h;
121 struct pagedim {
122 int pageno;
123 int rotate;
124 int left;
125 fz_rect box;
126 fz_bbox bbox;
127 fz_matrix ctm, ctm1;
130 struct page {
131 int pageno;
132 int slicecount;
133 fz_text_span *text;
134 fz_pixmap *pixmap;
135 pdf_page *drawpage;
136 struct pagedim pagedim;
137 struct mark {
138 int i;
139 fz_text_span *span;
140 } fmark, lmark;
141 struct slice slices[];
144 #if !defined _WIN32 && !defined __APPLE__
145 #define USE_XSEL
146 #endif
148 struct {
149 int sock;
150 int sliceheight;
151 struct page *pig;
152 struct pagedim *pagedims;
153 int pagecount;
154 int pagedimcount;
155 pdf_xref *xref;
156 fz_glyph_cache *cache;
157 int w, h;
159 int texindex;
160 int texcount;
161 GLuint *texids;
163 GLenum texform;
164 GLenum texty;
166 struct {
167 int w, h;
168 struct slice *slice;
169 } *texowners;
171 int rotate;
172 int proportional;
173 int needoutline;
175 #ifdef _WIN32
176 HANDLE thread;
177 #else
178 pthread_t thread;
179 #endif
180 FILE *xselpipe;
181 } state;
183 #ifdef _WIN32
184 static CRITICAL_SECTION critsec;
186 static void lock (void *unused)
188 (void) unused;
189 EnterCriticalSection (&critsec);
192 static void unlock (void *unused)
194 (void) unused;
195 LeaveCriticalSection (&critsec);
198 static int trylock (void *unused)
200 return TryEnterCriticalSection (&critsec) == 0;
202 #else
203 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
205 static void lock (const char *cap)
207 int ret = pthread_mutex_lock (&mutex);
208 if (ret) {
209 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
213 static void unlock (const char *cap)
215 int ret = pthread_mutex_unlock (&mutex);
216 if (ret) {
217 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
221 static int trylock (const char *cap)
223 int ret = pthread_mutex_trylock (&mutex);
225 if (ret && ret != EBUSY) {
226 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
228 return ret == EBUSY;
230 #endif
232 static void *parse_pointer (const char *cap, const char *s)
234 int ret;
235 void *ptr;
237 ret = sscanf (s, "%p", &ptr);
238 if (ret != 1) {
239 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
241 return ptr;
244 static int hasdata (int sock)
246 int ret;
247 fionread_arg avail;
248 ret = ioctlsocket (sock, FIONREAD, &avail);
249 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
250 return avail > 0;
253 static double now (void)
255 struct timeval tv;
257 if (gettimeofday (&tv, NULL)) {
258 err (1, "gettimeofday");
260 return tv.tv_sec + tv.tv_usec*1e-6;
263 static void readdata (int fd, char *p, int size)
265 ssize_t n;
267 n = recv (fd, p, size, 0);
268 if (n - size) {
269 if (!n) errx (1, "EOF while reading");
270 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
274 static void writedata (int fd, char *p, int size)
276 char buf[4];
277 ssize_t n;
279 buf[0] = (size >> 24) & 0xff;
280 buf[1] = (size >> 16) & 0xff;
281 buf[2] = (size >> 8) & 0xff;
282 buf[3] = (size >> 0) & 0xff;
284 n = send (fd, buf, 4, 0);
285 if (n != 4) {
286 if (!n) errx (1, "EOF while writing length");
287 sockerr (1, "send " FMT_ss, n);
290 n = send (fd, p, size, 0);
291 if (n - size) {
292 if (!n) errx (1, "EOF while writing data");
293 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
297 static void
298 #ifdef __GNUC__
299 __attribute__ ((format (printf, 2, 3)))
300 #endif
301 printd (int fd, const char *fmt, ...)
303 int size = 200, len;
304 va_list ap;
305 char *buf;
307 buf = malloc (size);
308 for (;;) {
309 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
311 va_start (ap, fmt);
312 len = vsnprintf (buf, size, fmt, ap);
313 va_end (ap);
315 if (len > -1 && len < size) {
316 writedata (fd, buf, len);
317 break;
320 if (len > -1) {
321 size = len + 1;
323 else {
324 size *= 2;
326 buf = realloc (buf, size);
328 free (buf);
331 static void die (fz_error error)
333 fz_catch (error, "aborting");
334 if (state.xref)
335 pdf_free_xref (state.xref);
336 exit (1);
339 static void openxref (char *filename, char *password)
341 int i;
342 fz_error error;
344 for (i = 0; i < state.texcount; ++i) {
345 state.texowners[i].slice = NULL;
348 if (state.cache) {
349 fz_free_glyph_cache (state.cache);
352 state.cache = fz_new_glyph_cache ();
353 if (!state.cache) {
354 errx (1, "fz_newglyph_cache failed");
357 if (state.xref) {
358 if (state.xref->store) {
359 pdf_free_store (state.xref->store);
360 state.xref->store = NULL;
362 pdf_free_xref (state.xref);
363 state.xref = NULL;
366 if (state.pagedims) {
367 free (state.pagedims);
368 state.pagedims = NULL;
370 state.pagedimcount = 0;
372 error = pdf_open_xref (&state.xref, filename, password);
373 if (error) {
374 die (error);
377 error = pdf_load_page_tree (state.xref);
378 if (error) {
379 die (error);
382 state.pagecount = pdf_count_pages (state.xref);
385 static void pdfinfo (void)
387 fz_obj *obj;
389 printd (state.sock, "i PDF version %d.%d\n",
390 state.xref->version / 10, state.xref->version % 10);
392 #ifdef _GNU_SOURCE
394 FILE *f;
395 char *buf;
396 size_t size;
398 f = open_memstream (&buf, &size);
399 if (f) {
400 obj = fz_dict_gets (state.xref->trailer, "Info");
401 fz_fprint_obj (f, fz_resolve_indirect (obj), 0);
402 if (fclose (f)) err (1, "fclose on memstream failed");
403 printd (state.sock, "i %.*s", size, buf);
404 free (buf);
406 else {
407 printd (state.sock, "i error opening memstream: %s\n",
408 strerror (errno));
411 #endif
414 static int readlen (int fd)
416 ssize_t n;
417 unsigned char p[4];
419 n = recv (fd, p, 4, 0);
420 if (n != 4) {
421 if (!n) errx (1, "EOF while reading length");
422 sockerr (1, "recv " FMT_ss, n);
425 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
428 static void unlinkpage (struct page *page)
430 int i;
432 for (i = 0; i < page->slicecount; ++i) {
433 struct slice *s = &page->slices[i];
434 if (s->texindex != -1) {
435 if (state.texowners[s->texindex].slice == s) {
436 state.texowners[s->texindex].slice = NULL;
437 ARSERT (state.texowners[s->texindex].w == s->w);
438 ARSERT (state.texowners[s->texindex].h >= s->h);
444 static void freepage (struct page *page)
446 fz_drop_pixmap (page->pixmap);
448 unlinkpage (page);
450 if (page->text) {
451 fz_free_text_span (page->text);
453 if (page->drawpage) {
454 pdf_free_page (page->drawpage);
457 free (page);
460 static void subdivide (struct page *p)
462 int i;
463 int h = p->pixmap->h;
464 int th = MIN (h, state.sliceheight);
466 for (i = 0; i < p->slicecount; ++i) {
467 struct slice *s = &p->slices[i];
468 s->texindex = -1;
469 s->h = MIN (th, h);
470 s->w = p->pixmap->w;
471 h -= th;
475 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
477 return p1->rotate == p2->rotate
478 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
479 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
482 #ifdef __ALTIVEC__
483 #include <altivec.h>
485 static int cacheline32bytes;
487 static void __attribute__ ((constructor)) clcheck (void)
489 char **envp = environ;
490 unsigned long *auxv;
492 while (*envp++);
494 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
495 if (*auxv == 19) {
496 cacheline32bytes = auxv[1] == 32;
497 return;
502 static void __attribute__ ((optimize ("O"))) clearpixmap (fz_pixmap *pixmap)
504 if (cacheline32bytes) {
505 intptr_t a1, a2, diff;
506 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
507 vector unsigned char v = vec_splat_u8 (-1);
508 vector unsigned char *p;
510 a1 = a2 = (intptr_t) pixmap->samples;
511 a2 = (a1 + 31) & ~31;
512 diff = a2 - a1;
513 sizea = size - diff;
514 p = (void *) a2;
516 while (a1 != a2) *(char *) a1++ = 0xff;
517 for (i = 0; i < (sizea & ~31); i += 32) {
518 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
519 vec_st (v, i, p);
520 vec_st (v, i + 16, p);
522 while (i < sizea) *((char *) a1 + i++) = 0xff;
524 else fz_clear_pixmap_with_color (pixmap, 0xff);
526 #else
527 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
528 #endif
530 static void *render (int pageno, int pindex)
532 fz_error error;
533 int slicecount;
534 struct page *page = NULL;
535 double start, end;
536 pdf_page *drawpage;
537 fz_device *idev;
538 struct pagedim *pagedim;
540 start = now ();
541 printd (state.sock, "V rendering %d", pageno);
543 pagedim = &state.pagedims[pindex];
544 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
545 + state.sliceheight - 1) / state.sliceheight;
546 slicecount += slicecount == 0;
548 if (state.pig) {
549 if (compatpdims (&state.pig->pagedim, pagedim)) {
550 page = state.pig;
551 if (page->text) {
552 fz_free_text_span (page->text);
553 page->text = NULL;
555 if (page->drawpage) {
556 pdf_free_page (page->drawpage);
557 page->drawpage = NULL;
560 else {
561 freepage (state.pig);
564 if (!page) {
565 page = calloc (sizeof (*page)
566 + (slicecount * sizeof (struct slice)), 1);
567 if (!page) {
568 err (1, "calloc page %d\n", pageno);
570 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
573 page->slicecount = slicecount;
575 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
576 if (error)
577 die (error);
579 clearpixmap (page->pixmap);
581 idev = fz_new_draw_device (state.cache, page->pixmap);
582 if (!idev)
583 die (fz_throw ("fz_newdrawdevice failed"));
584 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
585 if (error)
586 die (fz_rethrow (error, "pdf_runpage failed"));
587 fz_free_device (idev);
589 page->drawpage = drawpage;
590 page->pagedim = *pagedim;
591 page->pageno = pageno;
592 subdivide (page);
593 end = now ();
595 pdf_age_store (state.xref->store, 3);
597 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
598 state.pig = NULL;
599 return page;
602 static void initpdims (void)
604 int pageno;
605 double start, end;
607 start = now ();
608 for (pageno = 0; pageno < state.pagecount; ++pageno) {
609 int rotate;
610 fz_rect box;
611 struct pagedim *p;
612 fz_obj *obj, *pageobj;
614 pageobj = state.xref->page_objs[pageno];
616 obj = fz_dict_gets (pageobj, "CropBox");
617 if (!fz_is_array (obj)) {
618 obj = fz_dict_gets (pageobj, "MediaBox");
619 if (!fz_is_array (obj)) {
620 die (fz_throw ("cannot find page bounds %d (%d Rd)",
621 fz_to_num (pageobj), fz_to_gen (pageobj)));
624 box = pdf_to_rect (obj);
626 obj = fz_dict_gets (pageobj, "Rotate");
627 if (fz_is_int (obj)) {
628 rotate = fz_to_int (obj);
630 else {
631 rotate = 0;
633 rotate += state.rotate;
635 p = &state.pagedims[state.pagedimcount - 1];
636 if ((state.pagedimcount == 0)
637 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
638 size_t size;
640 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
641 state.pagedims = realloc (state.pagedims, size);
642 if (!state.pagedims) {
643 err (1, "realloc pagedims to " FMT_s " (%d elems)",
644 size, state.pagedimcount + 1);
646 p = &state.pagedims[state.pagedimcount++];
647 p->rotate = rotate;
648 p->box = box;
649 p->pageno = pageno;
652 end = now ();
653 printd (state.sock, "T Processed %d pages in %f seconds",
654 state.pagecount, end - start);
657 static void layout (void)
659 int pindex;
660 fz_matrix ctm;
661 fz_rect box, box2;
662 double zoom, w, maxw = 0;
663 struct pagedim *p = state.pagedims;
665 if (state.proportional) {
666 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
667 box.x0 = MIN (p->box.x0, p->box.x1);
668 box.y0 = MIN (p->box.y0, p->box.y1);
669 box.x1 = MAX (p->box.x0, p->box.x1);
670 box.y1 = MAX (p->box.y0, p->box.y1);
672 ctm = fz_identity;
673 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
674 ctm = fz_concat (ctm, fz_rotate (p->rotate));
675 box2 = fz_transform_rect (ctm, box);
676 w = box2.x1 - box2.x0;
677 maxw = MAX (w, maxw);
681 p = state.pagedims;
682 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
683 box.x0 = MIN (p->box.x0, p->box.x1);
684 box.y0 = MIN (p->box.y0, p->box.y1);
685 box.x1 = MAX (p->box.x0, p->box.x1);
686 box.y1 = MAX (p->box.y0, p->box.y1);
688 ctm = fz_identity;
689 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
690 ctm = fz_concat (ctm, fz_rotate (p->rotate));
691 box2 = fz_transform_rect (ctm, box);
692 w = box2.x1 - box2.x0;
694 if (state.proportional) {
695 double scale = w / maxw;
696 zoom = (state.w / w) * scale;
698 else {
699 zoom = state.w / w;
701 ctm = fz_identity;
702 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
703 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
704 memcpy (&p->ctm1, &ctm, sizeof (ctm));
705 ctm = fz_concat (ctm, fz_rotate (p->rotate));
706 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
707 p->left = state.proportional ? ((maxw - w) * zoom) / 2.0 : 0;
708 memcpy (&p->ctm, &ctm, sizeof (ctm));
711 while (p-- != state.pagedims) {
712 printd (state.sock, "l %d %d %d %d",
713 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0,
714 p->left);
718 static void recurse_outline (pdf_outline *outline, int level)
720 while (outline) {
721 int i;
722 fz_obj *obj;
723 int pageno = -1;
724 int top = 0, h = 0;
726 if (!outline->link) goto next;
728 obj = outline->link->dest;
729 if (fz_is_indirect (obj)) {
730 obj = fz_resolve_indirect (obj);
732 if (fz_is_array (obj)) {
733 fz_obj *obj2;
735 obj2 = fz_array_get (obj, 0);
736 if (fz_is_int (obj2)) {
737 pageno = fz_to_int (obj2);
739 else {
740 pageno = pdf_find_page_number (state.xref, obj2);
743 if (fz_array_len (obj) > 3) {
744 fz_point p;
745 fz_obj *xo, *yo;
747 xo = fz_array_get (obj, 2);
748 yo = fz_array_get (obj, 3);
749 if (!fz_is_null (xo) && !fz_is_null (yo)) {
750 struct pagedim *pagedim = state.pagedims;
752 for (i = 0; i < state.pagedimcount; ++i) {
753 if (state.pagedims[i].pageno > pageno)
754 break;
755 pagedim = &state.pagedims[i];
757 p.x = fz_to_int (xo);
758 p.y = fz_to_int (yo);
759 p = fz_transform_point (pagedim->ctm, p);
760 h = pagedim->bbox.y1 - pagedim->bbox.y0;
761 top = p.y;
765 else {
766 pageno = pdf_find_page_number (state.xref, outline->link->dest);
769 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
770 if (pageno > 0) {
771 printd (state.sock, "o %d %d %d %d %s",
772 level, pageno, top, h, outline->title);
774 next:
775 if (outline->child) {
776 recurse_outline (outline->child, level + 1);
778 outline = outline->next;
782 static void process_outline (void)
784 pdf_outline *outline;
786 if (!state.needoutline) return;
788 state.needoutline = 0;
789 outline = pdf_load_outline (state.xref);
790 if (outline) {
791 recurse_outline (outline, 0);
792 pdf_free_outline (outline);
796 static int comparespans (const void *l, const void *r)
798 fz_text_span const *const*ls = l;
799 fz_text_span const *const*rs = r;
800 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
803 /* wishful thinking function */
804 static void search (regex_t *re, int pageno, int y, int forward)
806 int i, j;
807 int ret;
808 char *p;
809 char buf[256];
810 fz_matrix ctm;
811 fz_error error;
812 fz_device *tdev;
813 pdf_page *drawpage;
814 fz_text_span *text, *span, **pspan;
815 struct pagedim *pdim, *pdimprev;
816 int stop = 0;
817 int niters = 0;
818 int nspans;
819 double start, end;
821 start = now ();
822 while (pageno >= 0 && pageno < state.pagecount && !stop) {
823 if (niters++ == 5) {
824 pdf_age_store (state.xref->store, 3);
825 niters = 0;
826 if (hasdata (state.sock)) {
827 printd (state.sock, "T attention requested aborting search at %d",
828 pageno);
829 stop = 1;
831 else {
832 printd (state.sock, "T searching in page %d", pageno);
835 pdimprev = NULL;
836 for (i = 0; i < state.pagedimcount; ++i) {
837 pdim = &state.pagedims[i];
838 if (pdim->pageno == pageno) {
839 goto found;
841 if (pdim->pageno > pageno) {
842 pdim = pdimprev;
843 goto found;
845 pdimprev = pdim;
847 pdim = pdimprev;
848 found:
850 error = pdf_load_page (&drawpage, state.xref, pageno);
851 if (error)
852 die (error);
854 ctm = fz_rotate (pdim->rotate);
856 text = fz_new_text_span ();
857 tdev = fz_new_text_device (text);
858 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
859 if (error) die (error);
860 fz_free_device (tdev);
862 nspans = 0;
863 for (span = text; span; span = span->next) {
864 nspans++;
866 pspan = malloc (sizeof (void *) * nspans);
867 if (!pspan) {
868 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
870 for (i = 0, span = text; span; span = span->next, ++i) {
871 pspan[i] = span;
873 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
875 j = forward ? 0 : nspans - 1;
876 while (nspans--) {
877 regmatch_t rm;
879 span = pspan[j];
880 j += forward ? 1 : -1;
881 p = buf;
882 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
883 if (forward) {
884 if (span->text[i].bbox.y0 < y + 1) {
885 continue;
888 else {
889 if (span->text[i].bbox.y0 > y - 1) {
890 continue;
893 if (span->text[i].c < 256) {
894 *p++ = span->text[i].c;
896 else {
897 *p++ = '?';
900 if (p == buf) {
901 continue;
903 *p++ = 0;
905 ret = regexec (re, buf, 1, &rm, 0);
906 if (ret) {
907 if (ret != REG_NOMATCH) {
908 size_t size;
909 char errbuf[80];
910 size = regerror (ret, re, errbuf, sizeof (errbuf));
911 printd (state.sock,
912 "T regexec error `%.*s'",
913 (int) size, errbuf);
914 fz_free_text_span (text);
915 pdf_free_page (drawpage);
916 free (pspan);
917 return;
920 else {
921 int xoff, yoff;
922 fz_bbox *sb, *eb;
923 fz_point p1, p2, p3, p4;
925 xoff = pdim->left - pdim->bbox.x0;
926 yoff = -pdim->bbox.y0;
928 sb = &span->text[rm.rm_so].bbox;
929 eb = &span->text[rm.rm_eo - 1].bbox;
931 p1.x = sb->x0;
932 p1.y = sb->y0;
933 p2.x = eb->x1;
934 p2.y = sb->y0;
935 p3.x = eb->x1;
936 p3.y = eb->y1;
937 p4.x = sb->x0;
938 p4.y = eb->y1;
940 p1 = fz_transform_point (ctm, p1);
941 p2 = fz_transform_point (ctm, p2);
942 p3 = fz_transform_point (ctm, p3);
943 p4 = fz_transform_point (ctm, p4);
945 if (!stop) {
946 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
947 pageno, 1,
948 p1.x + xoff, p1.y + yoff,
949 p2.x + xoff, p2.y + yoff,
950 p3.x + xoff, p3.y + yoff,
951 p4.x + xoff, p4.y + yoff);
953 printd (state.sock, "T found at %d `%.*s' in %f sec",
954 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
955 now () - start);
957 else {
958 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
959 pageno, 2,
960 p1.x + xoff, p1.y + yoff,
961 p2.x + xoff, p2.y + yoff,
962 p3.x + xoff, p3.y + yoff,
963 p4.x + xoff, p4.y + yoff);
965 stop = 1;
968 if (forward) {
969 pageno += 1;
970 y = 0;
972 else {
973 pageno -= 1;
974 y = INT_MAX;
976 fz_free_text_span (text);
977 pdf_free_page (drawpage);
978 free (pspan);
980 end = now ();
981 if (!stop) {
982 printd (state.sock, "T no matches %f sec", end - start);
984 printd (state.sock, "D");
987 static
988 #ifdef _WIN32
989 DWORD _stdcall
990 #else
991 void *
992 #endif
993 mainloop (void *unused)
995 char *p = NULL;
996 int len, ret, oldlen = 0;
998 for (;;) {
999 len = readlen (state.sock);
1000 if (len == 0) {
1001 errx (1, "readlen returned 0");
1004 if (oldlen < len + 1) {
1005 p = realloc (p, len + 1);
1006 if (!p) {
1007 err (1, "realloc %d failed", len + 1);
1009 oldlen = len + 1;
1011 readdata (state.sock, p, len);
1012 p[len] = 0;
1014 if (!strncmp ("open", p, 4)) {
1015 fz_obj *obj;
1016 size_t filenamelen;
1017 char *password;
1018 char *filename = p + 5;
1020 filenamelen = strlen (filename);
1021 password = filename + filenamelen + 1;
1023 openxref (filename, password);
1024 initpdims ();
1026 obj = fz_dict_gets (state.xref->trailer, "Info");
1027 if (obj) {
1028 char *s;
1030 obj = fz_dict_gets (obj, "Title");
1031 s = pdf_to_utf8 (obj);
1032 if (*s) {
1033 printd (state.sock, "t %s", s);
1035 fz_free (s);
1038 state.needoutline = 1;
1040 else if (!strncmp ("free", p, 4)) {
1041 void *ptr;
1043 ret = sscanf (p + 4, " %p", &ptr);
1044 if (ret != 1) {
1045 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
1047 unlinkpage (ptr);
1048 state.pig = ptr;
1050 else if (!strncmp ("search", p, 6)) {
1051 int icase, pageno, y, ret, len2, forward;
1052 char *pattern;
1053 regex_t re;
1055 ret = sscanf (p + 6, " %d %d %d %d,%n",
1056 &icase, &pageno, &y, &forward, &len2);
1057 if (ret != 4) {
1058 errx (1, "malformed search `%s' ret=%d", p, ret);
1061 pattern = p + 6 + len2;
1062 ret = regcomp (&re, pattern,
1063 REG_EXTENDED | (icase ? REG_ICASE : 0));
1064 if (ret) {
1065 char errbuf[80];
1066 size_t size;
1068 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1069 printd (state.sock, "T regcomp failed `%.*s'",
1070 (int) size, errbuf);
1072 else {
1073 search (&re, pageno, y, forward);
1074 regfree (&re);
1077 else if (!strncmp ("geometry", p, 8)) {
1078 int w, h;
1080 printd (state.sock, "c");
1081 ret = sscanf (p + 8, " %d %d", &w, &h);
1082 if (ret != 2) {
1083 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1086 lock ("geometry");
1087 state.h = h;
1088 if (w != state.w) {
1089 int i;
1090 state.w = w;
1091 for (i = 0; i < state.texcount; ++i) {
1092 state.texowners[i].slice = NULL;
1095 layout ();
1096 process_outline ();
1097 unlock ("geometry");
1098 printd (state.sock, "C %d", state.pagecount);
1100 else if (!strncmp ("reinit", p, 6)) {
1101 float rotate;
1102 int proportional;
1104 printd (state.sock, "c");
1105 ret = sscanf (p + 6, " %f %d", &rotate, &proportional);
1106 if (ret != 2) {
1107 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1109 lock ("reinit");
1110 state.rotate = rotate;
1111 state.proportional = proportional;
1112 state.pagedimcount = 0;
1113 free (state.pagedims);
1114 state.pagedims = NULL;
1115 initpdims ();
1116 layout ();
1117 process_outline ();
1118 if (state.pig) {
1119 freepage (state.pig);
1120 state.pig = NULL;
1122 unlock ("reinit");
1123 printd (state.sock, "C %d", state.pagecount);
1125 else if (!strncmp ("render", p, 6)) {
1126 int pageno, pindex, w, h, ret;
1127 struct page *page;
1129 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1130 if (ret != 4) {
1131 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1134 lock ("render");
1135 page = render (pageno, pindex);
1136 unlock ("render");
1138 printd (state.sock, "r %d %d %d %d %d %d %p",
1139 pageno,
1140 state.w,
1141 state.h,
1142 state.rotate,
1143 state.proportional,
1144 w * h * 4,
1145 page);
1147 else if (!strncmp ("info", p, 4)) {
1148 pdfinfo ();
1150 else if (!strncmp ("interrupt", p, 9)) {
1151 printd (state.sock, "V interrupted");
1153 else {
1154 errx (1, "unknown command %.*s", len, p);
1157 return 0;
1160 static void showsel (struct page *page, int oy)
1162 int ox;
1163 fz_bbox bbox;
1164 fz_text_span *span;
1165 struct mark first, last;
1167 first = page->fmark;
1168 last = page->lmark;
1170 if (!first.span || !last.span) return;
1172 glEnable (GL_BLEND);
1173 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1174 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1176 ox = -page->pixmap->x + page->pagedim.left;
1177 oy = -page->pixmap->y + oy;
1178 for (span = first.span; span; span = span->next) {
1179 int i, j, k;
1181 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1183 j = 0;
1184 k = span->len - 1;
1186 if (span == page->fmark.span && span == page->lmark.span) {
1187 j = MIN (first.i, last.i);
1188 k = MAX (first.i, last.i);
1190 else if (span == first.span) {
1191 j = first.i;
1193 else if (span == last.span) {
1194 k = last.i;
1197 for (i = j; i <= k; ++i) {
1198 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1200 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1201 bbox.x0,
1202 bbox.y0,
1203 bbox.x1,
1204 bbox.y1,
1205 oy, ox);
1207 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1209 if (span == last.span) break;
1211 glDisable (GL_BLEND);
1214 static void highlightlinks (struct page *page, int yoff)
1216 pdf_link *link;
1217 int xoff;
1219 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1220 glEnable (GL_LINE_STIPPLE);
1221 glLineStipple (0.5, 0xcccc);
1223 xoff = page->pagedim.left - page->pixmap->x;
1224 yoff -= page->pixmap->y;
1226 glBegin (GL_QUADS);
1227 for (link = page->drawpage->links; link; link = link->next) {
1228 fz_point p1, p2, p3, p4;
1229 fz_matrix ctm = page->pagedim.ctm;
1231 p1.x = link->rect.x0;
1232 p1.y = link->rect.y0;
1234 p2.x = link->rect.x1;
1235 p2.y = link->rect.y0;
1237 p3.x = link->rect.x1;
1238 p3.y = link->rect.y1;
1240 p4.x = link->rect.x0;
1241 p4.y = link->rect.y1;
1243 p1 = fz_transform_point (ctm, p1);
1244 p2 = fz_transform_point (ctm, p2);
1245 p3 = fz_transform_point (ctm, p3);
1246 p4 = fz_transform_point (ctm, p4);
1248 switch (link->kind) {
1249 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1250 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1251 default: glColor3ub (0, 0, 0); break;
1254 glVertex2f (p1.x + xoff, p1.y + yoff);
1255 glVertex2f (p2.x + xoff, p2.y + yoff);
1256 glVertex2f (p3.x + xoff, p3.y + yoff);
1257 glVertex2f (p4.x + xoff, p4.y + yoff);
1259 glEnd ();
1261 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1262 glDisable (GL_LINE_STIPPLE);
1265 static void upload2 (struct page *page, int slicenum, const char *cap)
1267 int i;
1268 int w;
1269 double start, end;
1270 struct slice *slice = &page->slices[slicenum];
1272 w = page->pixmap->w;
1274 ARSERT (w == slice->w);
1275 if (slice->texindex != -1
1276 && state.texowners[slice->texindex].slice == slice) {
1277 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1279 else {
1280 int subimage = 0;
1281 int index = (state.texindex++ % state.texcount);
1282 size_t offset = 0;
1284 for (i = 0; i < slicenum; ++i) {
1285 offset += w * page->slices[i].h * 4;
1288 if (state.texowners[index].w == slice->w) {
1289 if (state.texowners[index].h >= slice->h ) {
1290 subimage = 1;
1292 else {
1293 state.texowners[index].h = slice->h;
1296 else {
1297 state.texowners[index].h = slice->h;
1300 state.texowners[index].slice = slice;
1301 state.texowners[index].w = slice->w;
1302 slice->texindex = index;
1304 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1305 start = now ();
1306 if (subimage) {
1308 GLenum err = glGetError ();
1309 if (err != GL_NO_ERROR) {
1310 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1311 abort ();
1314 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1319 slice->h,
1320 state.texform,
1321 state.texty,
1322 page->pixmap->samples + offset
1325 GLenum err = glGetError ();
1326 if (err != GL_NO_ERROR) {
1327 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1328 abort ();
1332 else {
1333 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1335 GL_RGBA8,
1337 slice->h,
1339 state.texform,
1340 state.texty,
1341 page->pixmap->samples + offset
1345 end = now ();
1346 (void) start;
1347 (void) end;
1348 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1349 subimage ? "sub" : "img",
1350 page->pageno, slicenum,
1351 slice->w, slice->h,
1352 state.texids[slice->texindex],
1353 end - start);
1357 CAMLprim value ml_draw (value args_v, value ptr_v)
1359 CAMLparam2 (args_v, ptr_v);
1360 int dispy = Int_val (Field (args_v, 0));
1361 int w = Int_val (Field (args_v, 1));
1362 int h = Int_val (Field (args_v, 2));
1363 int py = Int_val (Field (args_v, 3));
1364 int hlinks = Bool_val (Field (args_v, 4));
1365 char *s = String_val (ptr_v);
1366 int ret;
1367 void *ptr;
1368 struct page *page;
1369 int slicenum = 0;
1370 int yoff = dispy - py;
1372 ret = sscanf (s, "%p", &ptr);
1373 if (ret != 1) {
1374 errx (1, "cannot parse pointer `%s'", s);
1376 page = ptr;
1378 w = page->pixmap->w;
1380 ARSERT (h >= 0 && "ml_draw wrong h");
1381 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1383 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1385 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1386 struct slice *slice = &page->slices[slicenum];
1387 if (slice->h > py) {
1388 break;
1390 py -= slice->h;
1393 h = MIN (state.h, h);
1394 while (h) {
1395 int th, left;
1396 struct slice *slice = &page->slices[slicenum];
1398 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1400 th = MIN (h, slice->h - py);
1401 upload2 (page, slicenum, "upload");
1403 left = page->pagedim.left;
1404 glBegin (GL_QUADS);
1406 glTexCoord2i (0, py);
1407 glVertex2i (left, dispy);
1409 glTexCoord2i (w, py);
1410 glVertex2i (left+w, dispy);
1412 glTexCoord2i (w, py+th);
1413 glVertex2i (left+w, dispy + th);
1415 glTexCoord2i (0, py+th);
1416 glVertex2i (left, dispy + th);
1418 glEnd ();
1420 h -= th;
1421 py = 0;
1422 dispy += th;
1423 slicenum += 1;
1426 showsel (page, yoff);
1427 if (hlinks) highlightlinks (page, yoff);
1428 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1430 CAMLreturn (Val_unit);
1433 static pdf_link *getlink (struct page *page, int x, int y)
1435 fz_point p;
1436 fz_matrix ctm;
1437 pdf_link *link;
1439 p.x = x + page->pixmap->x;
1440 p.y = y + page->pixmap->y;
1442 ctm = fz_invert_matrix (page->pagedim.ctm);
1443 p = fz_transform_point (ctm, p);
1445 for (link = page->drawpage->links; link; link = link->next) {
1446 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1447 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1448 return link;
1452 return NULL;
1455 static void ensuretext (struct page *page)
1457 if (!page->text) {
1458 fz_error error;
1459 fz_device *tdev;
1461 page->text = fz_new_text_span ();
1462 tdev = fz_new_text_device (page->text);
1463 error = pdf_run_page (state.xref, page->drawpage, tdev,
1464 page->pagedim.ctm);
1465 if (error) die (error);
1466 fz_free_device (tdev);
1470 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1472 CAMLparam3 (ptr_v, x_v, y_v);
1473 CAMLlocal3 (ret_v, tup_v, str_v);
1474 pdf_link *link;
1475 struct page *page;
1476 char *s = String_val (ptr_v);
1478 ret_v = Val_int (0);
1479 if (trylock ("ml_whatsunder")) {
1480 goto done;
1483 page = parse_pointer ("ml_whatsunder", s);
1484 link = getlink (page, Int_val (x_v), Int_val (y_v));
1485 if (link) {
1486 switch (link->kind) {
1487 case PDF_LINK_GOTO:
1489 int pageno;
1490 fz_point p;
1491 fz_obj *obj;
1493 pageno = -1;
1494 p.x = 0;
1495 p.y = 0;
1497 if (fz_is_array (link->dest)) {
1498 obj = fz_array_get (link->dest, 0);
1499 if (fz_is_indirect (obj)) {
1500 pageno = pdf_find_page_number (state.xref, obj);
1502 else if (fz_is_int (obj)) {
1503 pageno = fz_to_int (obj);
1506 if (fz_array_len (link->dest) > 3) {
1507 fz_obj *xo, *yo;
1509 xo = fz_array_get (link->dest, 2);
1510 yo = fz_array_get (link->dest, 3);
1511 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1512 p.x = fz_to_int (xo);
1513 p.y = fz_to_int (yo);
1514 p = fz_transform_point (page->pagedim.ctm, p);
1518 else {
1519 pageno = pdf_find_page_number (state.xref, link->dest);
1521 tup_v = caml_alloc_tuple (2);
1522 ret_v = caml_alloc_small (1, 1);
1523 Field (tup_v, 0) = Val_int (pageno);
1524 Field (tup_v, 1) = Val_int (p.y);
1525 Field (ret_v, 0) = tup_v;
1527 break;
1529 case PDF_LINK_URI:
1530 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1531 ret_v = caml_alloc_small (1, 0);
1532 Field (ret_v, 0) = str_v;
1533 break;
1535 default:
1536 printd (state.sock, "T unhandled link kind %d", link->kind);
1537 break;
1540 else {
1541 int i, x, y;
1542 fz_text_span *span;
1544 ensuretext (page);
1545 x = Int_val (x_v) + page->pixmap->x;
1546 y = Int_val (y_v) + page->pixmap->y;
1548 for (span = page->text; span; span = span->next) {
1549 for (i = 0; i < span->len; ++i) {
1550 fz_bbox *b;
1551 b = &span->text[i].bbox;
1552 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1553 const char *n2 =
1554 span->font && span->font->name
1555 ? span->font->name
1556 : "Span has no font name"
1558 #ifdef FT_FREETYPE_H
1559 FT_FaceRec *face = span->font->ft_face;
1560 if (face && face->family_name) {
1561 char *s;
1562 char *n1 = face->family_name;
1563 size_t l1 = strlen (n1);
1564 size_t l2 = strlen (n2);
1566 if (l1 != l2 || memcmp (n1, n2, l1)) {
1567 s = malloc (l1 + l2 + 2);
1568 if (s) {
1569 memcpy (s, n2, l2);
1570 s[l2] = '=';
1571 memcpy (s + l2 + 1, n1, l1 + 1);
1572 str_v = caml_copy_string (s);
1573 free (s);
1577 if (str_v == 0) {
1578 str_v = caml_copy_string (n2);
1580 #else
1581 str_v = caml_copy_string (n2);
1582 #endif
1583 ret_v = caml_alloc_small (1, 2);
1584 Field (ret_v, 0) = str_v;
1585 goto unlock;
1590 unlock:
1591 unlock ("ml_whatsunder");
1593 done:
1594 CAMLreturn (ret_v);
1597 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1599 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1600 fz_bbox *b;
1601 struct page *page;
1602 fz_text_span *span;
1603 struct mark first, last;
1604 int i, x0, x1, y0, y1, oy, left;
1605 char *s = String_val (ptr_v);
1607 if (trylock ("ml_seltext")) {
1608 goto done;
1611 page = parse_pointer ("ml_seltext", s);
1612 ensuretext (page);
1614 oy = Int_val (oy_v);
1615 x0 = Int_val (Field (rect_v, 0));
1616 y0 = Int_val (Field (rect_v, 1));
1617 x1 = Int_val (Field (rect_v, 2));
1618 y1 = Int_val (Field (rect_v, 3));
1620 left = page->pagedim.left;
1621 if (0) {
1622 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1623 glColor3ub (128, 128, 128);
1624 glRecti (x0, y0, x1, y1);
1625 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1628 x0 += page->pixmap->x - left;
1629 y0 += page->pixmap->y - oy;
1630 x1 += page->pixmap->x - left;
1631 y1 += page->pixmap->y - oy;
1633 first.span = NULL;
1634 last.span = NULL;
1636 last.i = first.i = 0;
1637 first.span = page->text;
1638 for (span = page->text; span; span = span->next) {
1639 for (i = 0; i < span->len; ++i) {
1640 b = &span->text[i].bbox;
1641 int selected = 0;
1643 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1644 first.i = i;
1645 first.span = span;
1646 selected = 1;
1648 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1649 last.i = i;
1650 last.span = span;
1651 selected = 1;
1653 if (0 && selected) {
1654 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1655 glColor3ub (128, 128, 128);
1656 glRecti (b->x0+left, b->y0, b->x1+left, b->y1);
1657 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1662 if (y1 < y0 || x1 < x0) {
1663 int swap = 0;
1665 if (first.span == last.span) {
1666 swap = 1;
1668 else {
1669 if (y1 < y0) {
1670 for (span = first.span; span && span != last.span;
1671 span = span->next) {
1672 if (span->eol) {
1673 swap = 1;
1674 break;
1680 if (swap) {
1681 i = first.i;
1682 span = first.span;
1683 first.i = last.i;
1684 first.span = last.span;
1685 last.i = i;
1686 last.span = span;
1690 page->fmark = first;
1691 page->lmark = last;
1693 unlock ("ml_seltext");
1695 done:
1696 CAMLreturn (Val_unit);
1699 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1701 char buf[4];
1702 int i, len, ret;
1704 for (i = a; i <= b; ++i) {
1705 len = runetochar (buf, &span->text[i].c);
1706 ret = fwrite (buf, len, 1, f);
1708 if (ret != 1) {
1709 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1710 len, ret, strerror (errno));
1711 return -1;
1714 return 0;
1717 CAMLprim value ml_copysel (value ptr_v)
1719 CAMLparam1 (ptr_v);
1720 FILE *f;
1721 struct page *page;
1722 char *s = String_val (ptr_v);
1724 if (trylock ("ml_copysel")) {
1725 goto done;
1728 if (!*s) {
1729 close:
1730 #ifdef USE_XSEL
1731 if (state.xselpipe) {
1732 int ret = pclose (state.xselpipe);
1733 if (ret) {
1734 printd (state.sock, "T failed to close xsel pipe `%s'",
1735 strerror (errno));
1737 state.xselpipe = NULL;
1739 #else
1740 printf ("========================================\n");
1741 #endif
1743 else {
1744 fz_text_span *span;
1746 page = parse_pointer ("ml_sopysel", s);
1748 if (!page->fmark.span || !page->lmark.span) {
1749 printd (state.sock, "T nothing to copy");
1750 goto unlock;
1753 f = stdout;
1754 #ifdef USE_XSEL
1755 if (!state.xselpipe) {
1756 state.xselpipe = popen ("xsel -i", "w");
1757 if (!state.xselpipe) {
1758 printd (state.sock, "T failed to open xsel pipe `%s'",
1759 strerror (errno));
1761 else {
1762 f = state.xselpipe;
1765 else {
1766 f = state.xselpipe;
1768 #endif
1770 for (span = page->fmark.span;
1771 span && span != page->lmark.span->next;
1772 span = span->next) {
1773 int a = span == page->fmark.span ? page->fmark.i : 0;
1774 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1775 if (pipespan (f, span, a, b)) {
1776 goto close;
1778 if (span->eol) {
1779 if (putc ('\n', f) == EOF) {
1780 printd (state.sock, "T failed break line on xsel pipe `%s'",
1781 strerror (errno));
1782 goto close;
1786 page->lmark.span = NULL;
1787 page->fmark.span = NULL;
1790 unlock:
1791 unlock ("ml_copysel");
1793 done:
1794 CAMLreturn (Val_unit);
1797 CAMLprim value ml_getpdimrect (value pagedimno_v)
1799 CAMLparam1 (pagedimno_v);
1800 CAMLlocal1 (ret_v);
1801 int pagedimno = Int_val (pagedimno_v);
1802 fz_rect box;
1804 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1805 if (trylock ("ml_getpdimrect")) {
1806 box = fz_empty_rect;
1808 else {
1809 box = state.pagedims[pagedimno].box;
1810 unlock ("ml_getpdimrect");
1813 Store_double_field (ret_v, 0, box.x0);
1814 Store_double_field (ret_v, 1, box.x1);
1815 Store_double_field (ret_v, 2, box.y0);
1816 Store_double_field (ret_v, 3, box.y1);
1818 CAMLreturn (ret_v);
1821 CAMLprim value ml_zoom_for_height (value winw_v, value winh_v, value dw_v)
1823 CAMLparam3 (winw_v, winh_v, dw_v);
1824 CAMLlocal1 (ret_v);
1825 int i;
1826 double zoom = 1.0;
1827 double maxw = 0.0, maxh = 0.0;
1828 struct pagedim *p;
1829 double winw = Int_val (winw_v);
1830 double winh = Int_val (winh_v);
1831 double dw = Int_val (dw_v);
1832 double pw = 1.0, ph = 1.0, num, den;
1834 if (trylock ("ml_zoom_for_height")) {
1835 goto done;
1838 if (state.proportional) {
1839 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1840 double x0, x1, w;
1842 x0 = MIN (p->box.x0, p->box.x1);
1843 x1 = MAX (p->box.x0, p->box.x1);
1845 w = x1 - x0;
1846 maxw = MAX (w, maxw);
1850 for (i = 0, p = state.pagedims; i < state.pagedimcount; ++i, ++p) {
1851 double x0, x1, y0, y1, w, h, scaledh, scale;
1853 x0 = MIN (p->box.x0, p->box.x1);
1854 y0 = MIN (p->box.y0, p->box.y1);
1855 x1 = MAX (p->box.x0, p->box.x1);
1856 y1 = MAX (p->box.y0, p->box.y1);
1858 w = x1 - x0;
1859 h = y1 - y0;
1861 if (state.proportional) {
1862 scale = w / maxw;
1863 scaledh = h * scale;
1865 else {
1866 scale = 1.0;
1867 scaledh = h;
1870 if (scaledh > maxh) {
1871 maxh = scaledh;
1872 ph = scaledh;
1873 pw = w * scale;
1877 num = (winh * pw) + (ph * dw);
1878 den = ph * winw;
1879 zoom = num / den;
1881 unlock ("ml_zoom_for_height");
1882 done:
1883 ret_v = caml_copy_double (zoom);
1884 CAMLreturn (ret_v);
1887 CAMLprim value ml_init (value sock_v, value params_v)
1889 #ifndef _WIN32
1890 int ret;
1891 #endif
1892 CAMLparam2 (sock_v, params_v);
1894 state.rotate = Int_val (Field (params_v, 0));
1895 state.proportional = Bool_val (Field (params_v, 1));
1896 state.texcount = Int_val (Field (params_v, 2));
1897 state.sliceheight = Int_val (Field (params_v, 3));
1898 state.texform = GL_RGBA;
1899 state.texty = GL_UNSIGNED_BYTE;
1901 fz_accelerate ();
1902 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1903 if (!state.texids) {
1904 err (1, "calloc texids " FMT_s,
1905 state.texcount * sizeof (*state.texids));
1908 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1909 if (!state.texowners) {
1910 err (1, "calloc texowners " FMT_s,
1911 state.texcount * sizeof (*state.texowners));
1914 glGenTextures (state.texcount, state.texids);
1916 #ifdef _WIN32
1917 state.sock = Socket_val (sock_v);
1918 #else
1919 state.sock = Int_val (sock_v);
1920 #endif
1922 #ifdef _WIN32
1923 InitializeCriticalSection (&critsec);
1924 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1925 if (state.thread == INVALID_HANDLE_VALUE) {
1926 errx (1, "CreateThread failed: %lx", GetLastError ());
1928 #else
1929 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1930 if (ret) {
1931 errx (1, "pthread_create: %s", strerror (ret));
1933 #endif
1935 CAMLreturn (Val_unit);