Stop panning once whole page can be visible
[llpp.git] / link.c
blob08e69a16dee85f7cb6848e22a106a5f6104e5998
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 fz_rect box;
125 fz_bbox bbox;
126 fz_matrix ctm, ctm1;
129 struct page {
130 int pageno;
131 int slicecount;
132 fz_text_span *text;
133 fz_pixmap *pixmap;
134 pdf_page *drawpage;
135 struct pagedim pagedim;
136 struct mark {
137 int i;
138 fz_text_span *span;
139 } fmark, lmark;
140 struct slice slices[];
143 #if !defined _WIN32 && !defined __APPLE__
144 #define USE_XSEL
145 #endif
147 struct {
148 int sock;
149 int sliceheight;
150 struct page *pig;
151 struct pagedim *pagedims;
152 int pagecount;
153 int pagedimcount;
154 pdf_xref *xref;
155 fz_glyph_cache *cache;
156 int w, h;
158 int texindex;
159 int texcount;
160 GLuint *texids;
162 GLenum texform;
163 GLenum texty;
165 struct {
166 int w, h;
167 struct slice *slice;
168 } *texowners;
170 int rotate;
171 int needoutline;
173 #ifdef _WIN32
174 HANDLE thread;
175 #else
176 pthread_t thread;
177 #endif
178 FILE *xselpipe;
179 } state;
181 #ifdef _WIN32
182 static CRITICAL_SECTION critsec;
184 static void lock (void *unused)
186 (void) unused;
187 EnterCriticalSection (&critsec);
190 static void unlock (void *unused)
192 (void) unused;
193 LeaveCriticalSection (&critsec);
196 static int trylock (void *unused)
198 return TryEnterCriticalSection (&critsec) == 0;
200 #else
201 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
203 static void lock (const char *cap)
205 int ret = pthread_mutex_lock (&mutex);
206 if (ret) {
207 errx (1, "%s: pthread_mutex_lock: %s", cap, strerror (ret));
211 static void unlock (const char *cap)
213 int ret = pthread_mutex_unlock (&mutex);
214 if (ret) {
215 errx (1, "%s: pthread_mutex_unlock: %s", cap, strerror (ret));
219 static int trylock (const char *cap)
221 int ret = pthread_mutex_trylock (&mutex);
223 if (ret && ret != EBUSY) {
224 errx (1, "%s: pthread_mutex_trylock: %s", cap, strerror (ret));
226 return ret == EBUSY;
228 #endif
230 static void *parse_pointer (const char *cap, const char *s)
232 int ret;
233 void *ptr;
235 ret = sscanf (s, "%p", &ptr);
236 if (ret != 1) {
237 errx (1, "%s: cannot parse pointer in `%s'", cap, s);
239 return ptr;
242 static int hasdata (int sock)
244 int ret;
245 fionread_arg avail;
246 ret = ioctlsocket (sock, FIONREAD, &avail);
247 if (ret) sockerr (1, "hasdata: FIONREAD error ret=%d", ret);
248 return avail > 0;
251 static double now (void)
253 struct timeval tv;
255 if (gettimeofday (&tv, NULL)) {
256 err (1, "gettimeofday");
258 return tv.tv_sec + tv.tv_usec*1e-6;
261 static void readdata (int fd, char *p, int size)
263 ssize_t n;
265 n = recv (fd, p, size, 0);
266 if (n - size) {
267 if (!n) errx (1, "EOF while reading");
268 sockerr (1, "recv (req %d, ret " FMT_ss ")", size, n);
272 static void writedata (int fd, char *p, int size)
274 char buf[4];
275 ssize_t n;
277 buf[0] = (size >> 24) & 0xff;
278 buf[1] = (size >> 16) & 0xff;
279 buf[2] = (size >> 8) & 0xff;
280 buf[3] = (size >> 0) & 0xff;
282 n = send (fd, buf, 4, 0);
283 if (n != 4) {
284 if (!n) errx (1, "EOF while writing length");
285 sockerr (1, "send " FMT_ss, n);
288 n = send (fd, p, size, 0);
289 if (n - size) {
290 if (!n) errx (1, "EOF while writing data");
291 sockerr (1, "send (req %d, ret " FMT_ss ")", size, n);
295 static void
296 #ifdef __GNUC__
297 __attribute__ ((format (printf, 2, 3)))
298 #endif
299 printd (int fd, const char *fmt, ...)
301 int size = 200, len;
302 va_list ap;
303 char *buf;
305 buf = malloc (size);
306 for (;;) {
307 if (!buf) err (errno, "malloc for temp buf (%d bytes) failed", size);
309 va_start (ap, fmt);
310 len = vsnprintf (buf, size, fmt, ap);
311 va_end (ap);
313 if (len > -1 && len < size) {
314 writedata (fd, buf, len);
315 break;
318 if (len > -1) {
319 size = len + 1;
321 else {
322 size *= 2;
324 buf = realloc (buf, size);
326 free (buf);
329 static void die (fz_error error)
331 fz_catch (error, "aborting");
332 if (state.xref)
333 pdf_free_xref (state.xref);
334 exit (1);
337 static void openxref (char *filename, char *password)
339 int i;
340 fz_error error;
342 for (i = 0; i < state.texcount; ++i) {
343 state.texowners[i].slice = NULL;
346 if (state.cache) {
347 fz_free_glyph_cache (state.cache);
350 state.cache = fz_new_glyph_cache ();
351 if (!state.cache) {
352 errx (1, "fz_newglyph_cache failed");
355 if (state.xref) {
356 if (state.xref->store) {
357 pdf_free_store (state.xref->store);
358 state.xref->store = NULL;
360 pdf_free_xref (state.xref);
361 state.xref = NULL;
364 if (state.pagedims) {
365 free (state.pagedims);
366 state.pagedims = NULL;
368 state.pagedimcount = 0;
370 error = pdf_open_xref (&state.xref, filename, password);
371 if (error) {
372 die (error);
375 error = pdf_load_page_tree (state.xref);
376 if (error) {
377 die (error);
380 state.pagecount = pdf_count_pages (state.xref);
383 static int readlen (int fd)
385 ssize_t n;
386 unsigned char p[4];
388 n = recv (fd, p, 4, 0);
389 if (n != 4) {
390 if (!n) errx (1, "EOF while reading length");
391 sockerr (1, "recv " FMT_ss, n);
394 return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
397 static void unlinkpage (struct page *page)
399 int i;
401 for (i = 0; i < page->slicecount; ++i) {
402 struct slice *s = &page->slices[i];
403 if (s->texindex != -1) {
404 if (state.texowners[s->texindex].slice == s) {
405 state.texowners[s->texindex].slice = NULL;
406 ARSERT (state.texowners[s->texindex].w == s->w);
407 ARSERT (state.texowners[s->texindex].h >= s->h);
413 static void freepage (struct page *page)
415 fz_drop_pixmap (page->pixmap);
417 unlinkpage (page);
419 if (page->text) {
420 fz_free_text_span (page->text);
422 if (page->drawpage) {
423 pdf_free_page (page->drawpage);
426 free (page);
429 static void subdivide (struct page *p)
431 int i;
432 int h = p->pixmap->h;
433 int th = MIN (h, state.sliceheight);
435 for (i = 0; i < p->slicecount; ++i) {
436 struct slice *s = &p->slices[i];
437 s->texindex = -1;
438 s->h = MIN (th, h);
439 s->w = p->pixmap->w;
440 h -= th;
444 static int compatpdims (struct pagedim *p1, struct pagedim *p2)
446 return p1->rotate == p2->rotate
447 && !memcmp (&p1->bbox, &p2->bbox, sizeof (p1->bbox))
448 && !memcmp (&p1->ctm, &p2->ctm, sizeof (p1->ctm));
451 #ifdef __ALTIVEC__
452 #include <altivec.h>
454 static int cacheline32bytes;
456 static void __attribute__ ((constructor)) clcheck (void)
458 char **envp = environ;
459 unsigned long *auxv;
461 while (*envp++);
463 for (auxv = (unsigned long *) envp; *auxv != 0; auxv += 2) {
464 if (*auxv == 19) {
465 cacheline32bytes = auxv[1] == 32;
466 return;
471 static void __attribute__ ((optimize ("O"))) clearpixmap (fz_pixmap *pixmap)
473 if (cacheline32bytes) {
474 intptr_t a1, a2, diff;
475 size_t sizea, i, size = pixmap->w * pixmap->h * pixmap->n;
476 vector unsigned char v = vec_splat_u8 (-1);
477 vector unsigned char *p;
479 a1 = a2 = (intptr_t) pixmap->samples;
480 a2 = (a1 + 31) & ~31;
481 diff = a2 - a1;
482 sizea = size - diff;
483 p = (void *) a2;
485 while (a1 != a2) *(char *) a1++ = 0xff;
486 for (i = 0; i < (sizea & ~31); i += 32) {
487 __asm volatile ("dcbz %0, %1"::"b"(a2),"r"(i));
488 vec_st (v, i, p);
489 vec_st (v, i + 16, p);
491 while (i < sizea) *((char *) a1 + i++) = 0xff;
493 else fz_clear_pixmap_with_color (pixmap, 0xff);
495 #else
496 #define clearpixmap(p) fz_clear_pixmap_with_color (p, 0xff)
497 #endif
499 static void *render (int pageno, int pindex)
501 fz_error error;
502 int slicecount;
503 struct page *page = NULL;
504 double start, end;
505 pdf_page *drawpage;
506 fz_device *idev;
507 struct pagedim *pagedim;
509 start = now ();
510 printd (state.sock, "V rendering %d", pageno);
512 pagedim = &state.pagedims[pindex];
513 slicecount = (pagedim->bbox.y1 - pagedim->bbox.y0
514 + state.sliceheight - 1) / state.sliceheight;
515 slicecount += slicecount == 0;
517 if (state.pig) {
518 if (compatpdims (&state.pig->pagedim, pagedim)) {
519 page = state.pig;
520 if (page->text) {
521 fz_free_text_span (page->text);
522 page->text = NULL;
524 if (page->drawpage) {
525 pdf_free_page (page->drawpage);
526 page->drawpage = NULL;
529 else {
530 freepage (state.pig);
533 if (!page) {
534 page = calloc (sizeof (*page)
535 + (slicecount * sizeof (struct slice)), 1);
536 if (!page) {
537 err (1, "calloc page %d\n", pageno);
539 page->pixmap = fz_new_pixmap_with_rect (fz_device_rgb, pagedim->bbox);
542 page->slicecount = slicecount;
544 error = pdf_load_page (&drawpage, state.xref, pageno - 1);
545 if (error)
546 die (error);
548 clearpixmap (page->pixmap);
550 idev = fz_new_draw_device (state.cache, page->pixmap);
551 if (!idev)
552 die (fz_throw ("fz_newdrawdevice failed"));
553 error = pdf_run_page (state.xref, drawpage, idev, pagedim->ctm);
554 if (error)
555 die (fz_rethrow (error, "pdf_runpage failed"));
556 fz_free_device (idev);
558 page->drawpage = drawpage;
559 page->pagedim = *pagedim;
560 page->pageno = pageno;
561 subdivide (page);
562 end = now ();
564 pdf_age_store (state.xref->store, 3);
566 printd (state.sock, "V rendering %d took %f sec", pageno, end - start);
567 state.pig = NULL;
568 return page;
571 static void initpdims (void)
573 int pageno;
574 double start, end;
576 start = now ();
577 for (pageno = 0; pageno < state.pagecount; ++pageno) {
578 int rotate;
579 fz_rect box;
580 struct pagedim *p;
581 fz_obj *obj, *pageobj;
583 pageobj = state.xref->page_objs[pageno];
585 obj = fz_dict_gets (pageobj, "CropBox");
586 if (!fz_is_array (obj)) {
587 obj = fz_dict_gets (pageobj, "MediaBox");
588 if (!fz_is_array (obj)) {
589 die (fz_throw ("cannot find page bounds %d (%d Rd)",
590 fz_to_num (pageobj), fz_to_gen (pageobj)));
593 box = pdf_to_rect (obj);
595 obj = fz_dict_gets (pageobj, "Rotate");
596 if (fz_is_int (obj)) {
597 rotate = fz_to_int (obj);
599 else {
600 rotate = 0;
602 rotate += state.rotate;
604 p = &state.pagedims[state.pagedimcount - 1];
605 if ((state.pagedimcount == 0)
606 || (p->rotate != rotate || memcmp (&p->box, &box, sizeof (box)))) {
607 size_t size;
609 size = (state.pagedimcount + 1) * sizeof (*state.pagedims);
610 state.pagedims = realloc (state.pagedims, size);
611 if (!state.pagedims) {
612 err (1, "realloc pagedims to " FMT_s " (%d elems)",
613 size, state.pagedimcount + 1);
615 p = &state.pagedims[state.pagedimcount++];
616 p->rotate = rotate;
617 p->box = box;
618 p->pageno = pageno;
621 end = now ();
622 printd (state.sock, "T Processed %d pages in %f seconds",
623 state.pagecount, end - start);
626 static void layout (void)
628 int pindex;
629 fz_matrix ctm;
630 fz_rect box, box2;
631 double zoom, w;
632 struct pagedim *p = state.pagedims;
634 pindex = 0;
635 for (pindex = 0; pindex < state.pagedimcount; ++pindex, ++p) {
636 box.x0 = MIN (p->box.x0, p->box.x1);
637 box.y0 = MIN (p->box.y0, p->box.y1);
638 box.x1 = MAX (p->box.x0, p->box.x1);
639 box.y1 = MAX (p->box.y0, p->box.y1);
641 ctm = fz_identity;
642 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
643 ctm = fz_concat (ctm, fz_rotate (p->rotate));
644 box2 = fz_transform_rect (ctm, box);
645 w = box2.x1 - box2.x0;
647 zoom = (state.w / w);
648 ctm = fz_identity;
649 ctm = fz_concat (ctm, fz_translate (0, -box.y1));
650 ctm = fz_concat (ctm, fz_scale (zoom, -zoom));
651 memcpy (&p->ctm1, &ctm, sizeof (ctm));
652 ctm = fz_concat (ctm, fz_rotate (p->rotate));
653 p->bbox = fz_round_rect (fz_transform_rect (ctm, box));
654 memcpy (&p->ctm, &ctm, sizeof (ctm));
657 while (p-- != state.pagedims) {
658 printd (state.sock, "l %d %d %d",
659 p->pageno, p->bbox.x1 - p->bbox.x0, p->bbox.y1 - p->bbox.y0);
663 static void recurse_outline (pdf_outline *outline, int level)
665 while (outline) {
666 int i;
667 fz_obj *obj;
668 int pageno = -1;
669 int top = 0, h = 0;
671 if (!outline->link) goto next;
673 obj = outline->link->dest;
674 if (fz_is_indirect (obj)) {
675 obj = fz_resolve_indirect (obj);
677 if (fz_is_array (obj)) {
678 fz_obj *obj2;
680 obj2 = fz_array_get (obj, 0);
681 if (fz_is_int (obj2)) {
682 pageno = fz_to_int (obj2);
684 else {
685 pageno = pdf_find_page_number (state.xref, obj2);
688 if (fz_array_len (obj) > 3) {
689 fz_point p;
690 fz_obj *xo, *yo;
692 xo = fz_array_get (obj, 2);
693 yo = fz_array_get (obj, 3);
694 if (!fz_is_null (xo) && !fz_is_null (yo)) {
695 struct pagedim *pagedim = state.pagedims;
697 for (i = 0; i < state.pagedimcount; ++i) {
698 if (state.pagedims[i].pageno > pageno)
699 break;
700 pagedim = &state.pagedims[i];
702 p.x = fz_to_int (xo);
703 p.y = fz_to_int (yo);
704 p = fz_transform_point (pagedim->ctm, p);
705 h = pagedim->bbox.y1 - pagedim->bbox.y0;
706 top = p.y;
710 else {
711 pageno = pdf_find_page_number (state.xref, outline->link->dest);
714 lprintf ("%*c%s %d\n", level, ' ', outline->title, pageno);
715 printd (state.sock, "o %d %d %d %d %s",
716 level, pageno, top, h, outline->title);
717 next:
718 if (outline->child) {
719 recurse_outline (outline->child, level + 1);
721 outline = outline->next;
725 static void process_outline (void)
727 pdf_outline *outline;
729 if (!state.needoutline) return;
731 state.needoutline = 0;
732 outline = pdf_load_outline (state.xref);
733 if (outline) {
734 recurse_outline (outline, 0);
735 pdf_free_outline (outline);
739 static int comparespans (const void *l, const void *r)
741 fz_text_span const *const*ls = l;
742 fz_text_span const *const*rs = r;
743 return (*ls)->text->bbox.y0 - (*rs)->text->bbox.y0;
746 /* wishful thinking function */
747 static void search (regex_t *re, int pageno, int y, int forward)
749 int i, j;
750 int ret;
751 char *p;
752 char buf[256];
753 fz_matrix ctm;
754 fz_error error;
755 fz_device *tdev;
756 pdf_page *drawpage;
757 fz_text_span *text, *span, **pspan;
758 struct pagedim *pdim, *pdimprev;
759 int stop = 0;
760 int niters = 0;
761 int nspans;
762 double start, end;
764 start = now ();
765 while (pageno >= 0 && pageno < state.pagecount && !stop) {
766 if (niters++ == 5) {
767 pdf_age_store (state.xref->store, 3);
768 niters = 0;
769 if (hasdata (state.sock)) {
770 printd (state.sock, "T attention requested aborting search at %d",
771 pageno);
772 stop = 1;
774 else {
775 printd (state.sock, "T searching in page %d", pageno);
778 pdimprev = NULL;
779 for (i = 0; i < state.pagedimcount; ++i) {
780 pdim = &state.pagedims[i];
781 if (pdim->pageno == pageno) {
782 goto found;
784 if (pdim->pageno > pageno) {
785 pdim = pdimprev;
786 goto found;
788 pdimprev = pdim;
790 pdim = pdimprev;
791 found:
793 error = pdf_load_page (&drawpage, state.xref, pageno);
794 if (error)
795 die (error);
797 ctm = fz_rotate (pdim->rotate);
799 text = fz_new_text_span ();
800 tdev = fz_new_text_device (text);
801 error = pdf_run_page (state.xref, drawpage, tdev, pdim->ctm1);
802 if (error) die (error);
803 fz_free_device (tdev);
805 nspans = 0;
806 for (span = text; span; span = span->next) {
807 nspans++;
809 pspan = malloc (sizeof (void *) * nspans);
810 if (!pspan) {
811 err (1, "malloc span pointers " FMT_s, sizeof (void *) * nspans);
813 for (i = 0, span = text; span; span = span->next, ++i) {
814 pspan[i] = span;
816 qsort (pspan, nspans, sizeof (fz_text_span *), comparespans);
818 j = forward ? 0 : nspans - 1;
819 while (nspans--) {
820 regmatch_t rm;
822 span = pspan[j];
823 j += forward ? 1 : -1;
824 p = buf;
825 for (i = 0; i < MIN (span->len, (int) sizeof (buf) - 1); ++i) {
826 if (forward) {
827 if (span->text[i].bbox.y0 < y + 1) {
828 continue;
831 else {
832 if (span->text[i].bbox.y0 > y - 1) {
833 continue;
836 if (span->text[i].c < 256) {
837 *p++ = span->text[i].c;
839 else {
840 *p++ = '?';
843 if (p == buf) {
844 continue;
846 *p++ = 0;
848 ret = regexec (re, buf, 1, &rm, 0);
849 if (ret) {
850 if (ret != REG_NOMATCH) {
851 size_t size;
852 char errbuf[80];
853 size = regerror (ret, re, errbuf, sizeof (errbuf));
854 printd (state.sock,
855 "T regexec error `%.*s'",
856 (int) size, errbuf);
857 fz_free_text_span (text);
858 pdf_free_page (drawpage);
859 free (pspan);
860 return;
863 else {
864 int xoff, yoff;
865 fz_bbox *sb, *eb;
866 fz_point p1, p2, p3, p4;
868 xoff = -pdim->bbox.x0;
869 yoff = -pdim->bbox.y0;
871 sb = &span->text[rm.rm_so].bbox;
872 eb = &span->text[rm.rm_eo - 1].bbox;
874 p1.x = sb->x0;
875 p1.y = sb->y0;
876 p2.x = eb->x1;
877 p2.y = sb->y0;
878 p3.x = eb->x1;
879 p3.y = eb->y1;
880 p4.x = sb->x0;
881 p4.y = eb->y1;
883 p1 = fz_transform_point (ctm, p1);
884 p2 = fz_transform_point (ctm, p2);
885 p3 = fz_transform_point (ctm, p3);
886 p4 = fz_transform_point (ctm, p4);
888 if (!stop) {
889 printd (state.sock, "F %d %d %f %f %f %f %f %f %f %f",
890 pageno, 1,
891 p1.x + xoff, p1.y + yoff,
892 p2.x + xoff, p2.y + yoff,
893 p3.x + xoff, p3.y + yoff,
894 p4.x + xoff, p4.y + yoff);
896 printd (state.sock, "T found at %d `%.*s' in %f sec",
897 pageno, rm.rm_eo - rm.rm_so, &buf[rm.rm_so],
898 now () - start);
900 else {
901 printd (state.sock, "R %d %d %f %f %f %f %f %f %f %f",
902 pageno, 2,
903 p1.x + xoff, p1.y + yoff,
904 p2.x + xoff, p2.y + yoff,
905 p3.x + xoff, p3.y + yoff,
906 p4.x + xoff, p4.y + yoff);
908 stop = 1;
911 if (forward) {
912 pageno += 1;
913 y = 0;
915 else {
916 pageno -= 1;
917 y = INT_MAX;
919 fz_free_text_span (text);
920 pdf_free_page (drawpage);
921 free (pspan);
923 end = now ();
924 if (!stop) {
925 printd (state.sock, "T no matches %f sec", end - start);
927 printd (state.sock, "D");
930 static
931 #ifdef _WIN32
932 DWORD _stdcall
933 #else
934 void *
935 #endif
936 mainloop (void *unused)
938 char *p = NULL;
939 int len, ret, oldlen = 0;
941 for (;;) {
942 len = readlen (state.sock);
943 if (len == 0) {
944 errx (1, "readlen returned 0");
947 if (oldlen < len + 1) {
948 p = realloc (p, len + 1);
949 if (!p) {
950 err (1, "realloc %d failed", len + 1);
952 oldlen = len + 1;
954 readdata (state.sock, p, len);
955 p[len] = 0;
957 if (!strncmp ("open", p, 4)) {
958 fz_obj *obj;
959 size_t filenamelen;
960 char *password;
961 char *filename = p + 5;
963 filenamelen = strlen (filename);
964 password = filename + filenamelen + 1;
966 openxref (filename, password);
967 initpdims ();
969 obj = fz_dict_gets (state.xref->trailer, "Info");
970 if (obj) {
971 char *s;
973 obj = fz_dict_gets (obj, "Title");
974 s = pdf_to_utf8 (obj);
975 if (*s) {
976 printd (state.sock, "t %s", s);
978 fz_free (s);
980 state.needoutline = 1;
982 else if (!strncmp ("free", p, 4)) {
983 void *ptr;
985 ret = sscanf (p + 4, " %p", &ptr);
986 if (ret != 1) {
987 errx (1, "malformed free `%.*s' ret=%d", len, p, ret);
989 unlinkpage (ptr);
990 state.pig = ptr;
992 else if (!strncmp ("search", p, 6)) {
993 int icase, pageno, y, ret, len2, forward;
994 char *pattern;
995 regex_t re;
997 ret = sscanf (p + 6, " %d %d %d %d,%n",
998 &icase, &pageno, &y, &forward, &len2);
999 if (ret != 4) {
1000 errx (1, "malformed search `%s' ret=%d", p, ret);
1003 pattern = p + 6 + len2;
1004 ret = regcomp (&re, pattern,
1005 REG_EXTENDED | (icase ? REG_ICASE : 0));
1006 if (ret) {
1007 char errbuf[80];
1008 size_t size;
1010 size = regerror (ret, &re, errbuf, sizeof (errbuf));
1011 printd (state.sock, "T regcomp failed `%.*s'",
1012 (int) size, errbuf);
1014 else {
1015 search (&re, pageno, y, forward);
1016 regfree (&re);
1019 else if (!strncmp ("geometry", p, 8)) {
1020 int w, h;
1022 printd (state.sock, "c");
1023 ret = sscanf (p + 8, " %d %d", &w, &h);
1024 if (ret != 2) {
1025 errx (1, "malformed geometry `%.*s' ret=%d", len, p, ret);
1028 lock ("geometry");
1029 state.h = h;
1030 if (w != state.w) {
1031 int i;
1032 state.w = w;
1033 for (i = 0; i < state.texcount; ++i) {
1034 state.texowners[i].slice = NULL;
1037 layout ();
1038 process_outline ();
1039 unlock ("geometry");
1040 printd (state.sock, "C %d", state.pagecount);
1042 else if (!strncmp ("rotate", p, 6)) {
1043 float rotate;
1045 printd (state.sock, "c");
1046 ret = sscanf (p + 6, " %f", &rotate);
1047 if (ret != 1) {
1048 errx (1, "bad rotate line `%.*s' ret=%d", len, p, ret);
1050 lock ("rotate");
1051 state.rotate = rotate;
1052 state.pagedimcount = 0;
1053 free (state.pagedims);
1054 state.pagedims = NULL;
1055 initpdims ();
1056 layout ();
1057 process_outline ();
1058 if (state.pig) {
1059 freepage (state.pig);
1060 state.pig = NULL;
1062 unlock ("rotate");
1063 printd (state.sock, "C %d", state.pagecount);
1065 else if (!strncmp ("render", p, 6)) {
1066 int pageno, pindex, w, h, ret;
1067 struct page *page;
1069 ret = sscanf (p + 6, " %d %d %d %d", &pageno, &pindex, &w, &h);
1070 if (ret != 4) {
1071 errx (1, "bad render line `%.*s' ret=%d", len, p, ret);
1074 lock ("render");
1075 page = render (pageno, pindex);
1076 unlock ("render");
1078 printd (state.sock, "r %d %d %d %d %p",
1079 pageno,
1080 state.w,
1081 state.h,
1082 state.rotate,
1083 page);
1085 else if (!strncmp ("interrupt", p, 9)) {
1086 printd (state.sock, "V interrupted");
1088 else {
1089 errx (1, "unknown command %.*s", len, p);
1092 return 0;
1095 static void showsel (struct page *page, int oy)
1097 int ox;
1098 fz_bbox bbox;
1099 fz_text_span *span;
1100 struct mark first, last;
1102 first = page->fmark;
1103 last = page->lmark;
1105 if (!first.span || !last.span) return;
1107 glEnable (GL_BLEND);
1108 glBlendFunc (GL_SRC_ALPHA, GL_SRC_ALPHA);
1109 glColor4f (0.5f, 0.5f, 0.0f, 0.6f);
1111 ox = -page->pixmap->x;
1112 oy = -page->pixmap->y + oy;
1113 for (span = first.span; span; span = span->next) {
1114 int i, j, k;
1116 bbox.x0 = bbox.y0 = bbox.x1 = bbox.y1 = 0;
1118 j = 0;
1119 k = span->len - 1;
1121 if (span == page->fmark.span && span == page->lmark.span) {
1122 j = MIN (first.i, last.i);
1123 k = MAX (first.i, last.i);
1125 else if (span == first.span) {
1126 j = first.i;
1128 else if (span == last.span) {
1129 k = last.i;
1132 for (i = j; i <= k; ++i) {
1133 bbox = fz_union_bbox (bbox, span->text[i].bbox);
1135 lprintf ("%d %d %d %d oy=%d ox=%d\n",
1136 bbox.x0,
1137 bbox.y0,
1138 bbox.x1,
1139 bbox.y1,
1140 oy, ox);
1142 glRecti (bbox.x0 + ox, bbox.y0 + oy, bbox.x1 + ox, bbox.y1 + oy);
1144 if (span == last.span) break;
1146 glDisable (GL_BLEND);
1149 static void highlightlinks (struct page *page, int yoff)
1151 pdf_link *link;
1152 int xoff;
1154 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1155 glEnable (GL_LINE_STIPPLE);
1156 glLineStipple (0.5, 0xcccc);
1158 xoff = -page->pixmap->x;
1159 yoff -= page->pixmap->y;
1161 glBegin (GL_QUADS);
1162 for (link = page->drawpage->links; link; link = link->next) {
1163 fz_point p1, p2, p3, p4;
1164 fz_matrix ctm = page->pagedim.ctm;
1166 p1.x = link->rect.x0;
1167 p1.y = link->rect.y0;
1169 p2.x = link->rect.x1;
1170 p2.y = link->rect.y0;
1172 p3.x = link->rect.x1;
1173 p3.y = link->rect.y1;
1175 p4.x = link->rect.x0;
1176 p4.y = link->rect.y1;
1178 p1 = fz_transform_point (ctm, p1);
1179 p2 = fz_transform_point (ctm, p2);
1180 p3 = fz_transform_point (ctm, p3);
1181 p4 = fz_transform_point (ctm, p4);
1183 switch (link->kind) {
1184 case PDF_LINK_GOTO: glColor3ub (255, 0, 0); break;
1185 case PDF_LINK_URI: glColor3ub (0, 0, 255); break;
1186 default: glColor3ub (0, 0, 0); break;
1189 glVertex2f (p1.x + xoff, p1.y + yoff);
1190 glVertex2f (p2.x + xoff, p2.y + yoff);
1191 glVertex2f (p3.x + xoff, p3.y + yoff);
1192 glVertex2f (p4.x + xoff, p4.y + yoff);
1194 glEnd ();
1196 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1197 glDisable (GL_LINE_STIPPLE);
1200 static void upload2 (struct page *page, int slicenum, const char *cap)
1202 int i;
1203 int w, h;
1204 double start, end;
1205 struct slice *slice = &page->slices[slicenum];
1207 w = page->pixmap->w;
1208 h = page->pixmap->h;
1210 ARSERT (w == slice->w);
1211 if (slice->texindex != -1
1212 && state.texowners[slice->texindex].slice == slice) {
1213 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1215 else {
1216 int subimage = 0;
1217 int index = (state.texindex++ % state.texcount);
1218 size_t offset = 0;
1220 for (i = 0; i < slicenum; ++i) {
1221 offset += w * page->slices[i].h * 4;
1224 if (state.texowners[index].w == slice->w) {
1225 if (state.texowners[index].h >= slice->h ) {
1226 subimage = 1;
1228 else {
1229 state.texowners[index].h = slice->h;
1232 else {
1233 state.texowners[index].h = slice->h;
1236 state.texowners[index].slice = slice;
1237 state.texowners[index].w = slice->w;
1238 slice->texindex = index;
1240 glBindTexture (GL_TEXTURE_RECTANGLE_ARB, state.texids[slice->texindex]);
1241 start = now ();
1242 if (subimage) {
1244 GLenum err = glGetError ();
1245 if (err != GL_NO_ERROR) {
1246 printf ("\033[0;31mERROR1 %d %d %#x\033[0m\n", w, slice->h, err);
1247 abort ();
1250 glTexSubImage2D (GL_TEXTURE_RECTANGLE_ARB,
1255 slice->h,
1256 state.texform,
1257 state.texty,
1258 page->pixmap->samples + offset
1261 GLenum err = glGetError ();
1262 if (err != GL_NO_ERROR) {
1263 printf ("\033[0;31mERROR %d %d %#x\033[0m\n", w, slice->h, err);
1264 abort ();
1268 else {
1269 glTexImage2D (GL_TEXTURE_RECTANGLE_ARB,
1271 GL_RGBA8,
1273 slice->h,
1275 state.texform,
1276 state.texty,
1277 page->pixmap->samples + offset
1281 end = now ();
1282 lprintf ("%s[%d] slice=%d(%d,%d) texid=%d %f sec\n",
1283 subimage ? "sub" : "img",
1284 page->pageno, slicenum,
1285 slice->w, slice->h,
1286 state.texids[slice->texindex],
1287 end - start);
1291 CAMLprim value ml_draw (value args_v, value ptr_v)
1293 CAMLparam2 (args_v, ptr_v);
1294 int dispy = Int_val (Field (args_v, 0));
1295 int w = Int_val (Field (args_v, 1));
1296 int h = Int_val (Field (args_v, 2));
1297 int py = Int_val (Field (args_v, 3));
1298 int hlinks = Bool_val (Field (args_v, 4));
1299 char *s = String_val (ptr_v);
1300 int ret;
1301 void *ptr;
1302 struct page *page;
1303 int slicenum = 0;
1304 int yoff = dispy - py;
1306 ret = sscanf (s, "%p", &ptr);
1307 if (ret != 1) {
1308 errx (1, "cannot parse pointer `%s'", s);
1310 page = ptr;
1312 w = page->pixmap->w;
1314 ARSERT (h >= 0 && "ml_draw wrong h");
1315 ARSERT (py <= page->pixmap->h && "ml_draw wrong py");
1317 glEnable (GL_TEXTURE_RECTANGLE_ARB);
1319 for (slicenum = 0; slicenum < page->slicecount; ++slicenum) {
1320 struct slice *slice = &page->slices[slicenum];
1321 if (slice->h > py) {
1322 break;
1324 py -= slice->h;
1327 h = MIN (state.h, h);
1328 while (h) {
1329 int th;
1330 struct slice *slice = &page->slices[slicenum];
1332 ARSERT (slicenum < page->slicecount && "ml_draw wrong slicenum");
1334 th = MIN (h, slice->h - py);
1335 upload2 (page, slicenum, "upload");
1337 glBegin (GL_QUADS);
1339 glTexCoord2i (0, py);
1340 glVertex2i (0, dispy);
1342 glTexCoord2i (w, py);
1343 glVertex2i (w, dispy);
1345 glTexCoord2i (w, py+th);
1346 glVertex2i (w, dispy + th);
1348 glTexCoord2i (0, py+th);
1349 glVertex2i (0, dispy + th);
1351 glEnd ();
1353 h -= th;
1354 py = 0;
1355 dispy += th;
1356 slicenum += 1;
1359 showsel (page, yoff);
1360 if (hlinks) highlightlinks (page, yoff);
1361 glDisable (GL_TEXTURE_RECTANGLE_ARB);
1363 CAMLreturn (Val_unit);
1366 static pdf_link *getlink (struct page *page, int x, int y)
1368 fz_point p;
1369 fz_matrix ctm;
1370 pdf_link *link;
1372 p.x = x + page->pixmap->x;
1373 p.y = y + page->pixmap->y;
1375 ctm = fz_invert_matrix (page->pagedim.ctm);
1376 p = fz_transform_point (ctm, p);
1378 for (link = page->drawpage->links; link; link = link->next) {
1379 if (p.x >= link->rect.x0 && p.x <= link->rect.x1) {
1380 if (p.y >= link->rect.y0 && p.y <= link->rect.y1) {
1381 return link;
1385 return NULL;
1388 static void ensuretext (struct page *page)
1390 if (!page->text) {
1391 fz_error error;
1392 fz_device *tdev;
1394 page->text = fz_new_text_span ();
1395 tdev = fz_new_text_device (page->text);
1396 error = pdf_run_page (state.xref, page->drawpage, tdev,
1397 page->pagedim.ctm);
1398 if (error) die (error);
1399 fz_free_device (tdev);
1403 CAMLprim value ml_whatsunder (value ptr_v, value x_v, value y_v)
1405 CAMLparam3 (ptr_v, x_v, y_v);
1406 CAMLlocal3 (ret_v, tup_v, str_v);
1407 pdf_link *link;
1408 struct page *page;
1409 char *s = String_val (ptr_v);
1411 ret_v = Val_int (0);
1412 if (trylock ("ml_whatsunder")) {
1413 goto done;
1416 page = parse_pointer ("ml_whatsunder", s);
1417 link = getlink (page, Int_val (x_v), Int_val (y_v));
1418 if (link) {
1419 switch (link->kind) {
1420 case PDF_LINK_GOTO:
1422 int pageno;
1423 fz_point p;
1424 fz_obj *obj;
1426 pageno = -1;
1427 p.x = 0;
1428 p.y = 0;
1430 if (fz_is_array (link->dest)) {
1431 obj = fz_array_get (link->dest, 0);
1432 if (fz_is_indirect (obj)) {
1433 pageno = pdf_find_page_number (state.xref, obj);
1435 else if (fz_is_int (obj)) {
1436 pageno = fz_to_int (obj);
1439 if (fz_array_len (link->dest) > 3) {
1440 fz_obj *xo, *yo;
1442 xo = fz_array_get (link->dest, 2);
1443 yo = fz_array_get (link->dest, 3);
1444 if (!fz_is_null (xo) && !fz_is_null (yo)) {
1445 p.x = fz_to_int (xo);
1446 p.y = fz_to_int (yo);
1447 p = fz_transform_point (page->pagedim.ctm, p);
1451 else {
1452 pageno = pdf_find_page_number (state.xref, link->dest);
1454 tup_v = caml_alloc_tuple (2);
1455 ret_v = caml_alloc_small (1, 1);
1456 Field (tup_v, 0) = Val_int (pageno);
1457 Field (tup_v, 1) = Val_int (p.y);
1458 Field (ret_v, 0) = tup_v;
1460 break;
1462 case PDF_LINK_URI:
1463 str_v = caml_copy_string (fz_to_str_buf (link->dest));
1464 ret_v = caml_alloc_small (1, 0);
1465 Field (ret_v, 0) = str_v;
1466 break;
1468 default:
1469 printd (state.sock, "T unhandled link kind %d", link->kind);
1470 break;
1473 else {
1474 int i, x, y;
1475 fz_text_span *span;
1477 ensuretext (page);
1478 x = Int_val (x_v) + page->pixmap->x;
1479 y = Int_val (y_v) + page->pixmap->y;
1481 for (span = page->text; span; span = span->next) {
1482 for (i = 0; i < span->len; ++i) {
1483 fz_bbox *b;
1484 b = &span->text[i].bbox;
1485 if ((x >= b->x0 && x <= b->x1 && y >= b->y0 && y <= b->y1)) {
1486 const char *n2 =
1487 span->font && span->font->name
1488 ? span->font->name
1489 : "Span has no font name"
1491 #ifdef FT_FREETYPE_H
1492 FT_FaceRec *face = span->font->ft_face;
1493 if (face && face->family_name) {
1494 char *s;
1495 char *n1 = face->family_name;
1496 size_t l1 = strlen (n1);
1497 size_t l2 = strlen (n2);
1499 if (l1 != l2 || memcmp (n1, n2, l1)) {
1500 s = malloc (l1 + l2 + 2);
1501 if (s) {
1502 memcpy (s, n2, l2);
1503 s[l2] = '=';
1504 memcpy (s + l2 + 1, n1, l1 + 1);
1505 str_v = caml_copy_string (s);
1506 free (s);
1510 if (str_v == 0) {
1511 str_v = caml_copy_string (n2);
1513 #else
1514 str_v = caml_copy_string (n2);
1515 #endif
1516 ret_v = caml_alloc_small (1, 2);
1517 Field (ret_v, 0) = str_v;
1518 goto unlock;
1523 unlock:
1524 unlock ("ml_whatsunder");
1526 done:
1527 CAMLreturn (ret_v);
1530 CAMLprim value ml_seltext (value ptr_v, value rect_v, value oy_v)
1532 CAMLparam4 (ptr_v, rect_v, oy_v, rect_v);
1533 fz_bbox *b;
1534 struct page *page;
1535 fz_text_span *span;
1536 struct mark first, last;
1537 int i, x0, x1, y0, y1, oy;
1538 char *s = String_val (ptr_v);
1540 if (trylock ("ml_seltext")) {
1541 goto done;
1544 page = parse_pointer ("ml_seltext", s);
1545 ensuretext (page);
1547 oy = Int_val (oy_v);
1548 x0 = Int_val (Field (rect_v, 0));
1549 y0 = Int_val (Field (rect_v, 1));
1550 x1 = Int_val (Field (rect_v, 2));
1551 y1 = Int_val (Field (rect_v, 3));
1553 if (0) {
1554 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
1555 glColor3ub (128, 128, 128);
1556 glRecti (x0, y0, x1, y1);
1557 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1560 x0 += page->pixmap->x;
1561 y0 += page->pixmap->y - oy;
1562 x1 += page->pixmap->x;
1563 y1 += page->pixmap->y - oy;
1565 first.span = NULL;
1566 last.span = NULL;
1568 last.i = first.i = 0;
1569 first.span = page->text;
1570 for (span = page->text; span; span = span->next) {
1571 for (i = 0; i < span->len; ++i) {
1572 b = &span->text[i].bbox;
1573 if (x0 >= b->x0 && x0 <= b->x1 && y0 >= b->y0 && y0 <= b->y1) {
1574 first.i = i;
1575 first.span = span;
1577 if (x1 >= b->x0 && x1 <= b->x1 && y1 >= b->y0 && y1 <= b->y1) {
1578 last.i = i;
1579 last.span = span;
1584 if (y1 < y0 || x1 < x0) {
1585 int swap = 0;
1587 if (first.span == last.span) {
1588 swap = 1;
1590 else {
1591 if (y1 < y0) {
1592 for (span = first.span; span && span != last.span;
1593 span = span->next) {
1594 if (span->eol) {
1595 swap = 1;
1596 break;
1602 if (swap) {
1603 i = first.i;
1604 span = first.span;
1605 first.i = last.i;
1606 first.span = last.span;
1607 last.i = i;
1608 last.span = span;
1612 page->fmark = first;
1613 page->lmark = last;
1615 unlock ("ml_seltext");
1617 done:
1618 CAMLreturn (Val_unit);
1621 static int pipespan (FILE *f, fz_text_span *span, int a, int b)
1623 char buf[4];
1624 int i, len, ret;
1626 for (i = a; i <= b; ++i) {
1627 len = runetochar (buf, &span->text[i].c);
1628 ret = fwrite (buf, len, 1, f);
1630 if (ret != 1) {
1631 printd (state.sock, "T failed to write %d bytes ret=%d: %s",
1632 len, ret, strerror (errno));
1633 return -1;
1636 return 0;
1639 CAMLprim value ml_copysel (value ptr_v)
1641 CAMLparam1 (ptr_v);
1642 FILE *f;
1643 struct page *page;
1644 char *s = String_val (ptr_v);
1646 if (trylock ("ml_copysel")) {
1647 goto done;
1650 if (!*s) {
1651 close:
1652 #ifdef USE_XSEL
1653 if (state.xselpipe) {
1654 int ret = pclose (state.xselpipe);
1655 if (ret) {
1656 printd (state.sock, "T failed to close xsel pipe `%s'",
1657 strerror (errno));
1659 state.xselpipe = NULL;
1661 #else
1662 printf ("========================================\n");
1663 #endif
1665 else {
1666 fz_text_span *span;
1668 page = parse_pointer ("ml_sopysel", s);
1670 if (!page->fmark.span || !page->lmark.span) {
1671 printd (state.sock, "T nothing to copy");
1672 goto unlock;
1675 f = stdout;
1676 #ifdef USE_XSEL
1677 if (!state.xselpipe) {
1678 state.xselpipe = popen ("xsel -i", "w");
1679 if (!state.xselpipe) {
1680 printd (state.sock, "T failed to open xsel pipe `%s'",
1681 strerror (errno));
1683 else {
1684 f = state.xselpipe;
1687 else {
1688 f = state.xselpipe;
1690 #endif
1692 for (span = page->fmark.span;
1693 span && span != page->lmark.span->next;
1694 span = span->next) {
1695 int a = span == page->fmark.span ? page->fmark.i : 0;
1696 int b = span == page->lmark.span ? page->lmark.i : span->len - 1;
1697 if (pipespan (f, span, a, b)) {
1698 goto close;
1700 if (span->eol) {
1701 if (putc ('\n', f) == EOF) {
1702 printd (state.sock, "T failed break line on xsel pipe `%s'",
1703 strerror (errno));
1704 goto close;
1708 page->lmark.span = NULL;
1709 page->fmark.span = NULL;
1712 unlock:
1713 unlock ("ml_copysel");
1715 done:
1716 CAMLreturn (Val_unit);
1719 CAMLprim value ml_getpagewh (value pagedimno_v)
1721 CAMLparam1 (pagedimno_v);
1722 CAMLlocal1 (ret_v);
1723 int pagedimno = Int_val (pagedimno_v);
1725 ret_v = caml_alloc_small (4 * Double_wosize, Double_array_tag);
1726 Store_double_field (ret_v, 0, state.pagedims[pagedimno].box.x0);
1727 Store_double_field (ret_v, 1, state.pagedims[pagedimno].box.x1);
1728 Store_double_field (ret_v, 2, state.pagedims[pagedimno].box.y0);
1729 Store_double_field (ret_v, 3, state.pagedims[pagedimno].box.y1);
1730 CAMLreturn (ret_v);
1733 CAMLprim value ml_init (value sock_v)
1735 #ifndef _WIN32
1736 int ret;
1737 #endif
1738 CAMLparam1 (sock_v);
1740 state.texcount = 256;
1741 state.sliceheight = 24;
1742 state.texform = GL_RGBA;
1743 state.texty = GL_UNSIGNED_BYTE;
1745 fz_accelerate ();
1746 state.texids = calloc (state.texcount * sizeof (*state.texids), 1);
1747 if (!state.texids) {
1748 err (1, "calloc texids " FMT_s,
1749 state.texcount * sizeof (*state.texids));
1752 state.texowners = calloc (state.texcount * sizeof (*state.texowners), 1);
1753 if (!state.texowners) {
1754 err (1, "calloc texowners " FMT_s,
1755 state.texcount * sizeof (*state.texowners));
1758 glGenTextures (state.texcount, state.texids);
1760 #ifdef _WIN32
1761 state.sock = Socket_val (sock_v);
1762 #else
1763 state.sock = Int_val (sock_v);
1764 #endif
1766 #ifdef _WIN32
1767 InitializeCriticalSection (&critsec);
1768 state.thread = CreateThread (NULL, 0, mainloop, NULL, 0, NULL);
1769 if (state.thread == INVALID_HANDLE_VALUE) {
1770 errx (1, "CreateThread failed: %lx", GetLastError ());
1772 #else
1773 ret = pthread_create (&state.thread, NULL, mainloop, NULL);
1774 if (ret) {
1775 errx (1, "pthread_create: %s", strerror (ret));
1777 #endif
1779 CAMLreturn (Val_unit);