chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / dox / dox.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2007-2008 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "dox.h"
25
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/Xosdefs.h>
29 #include <X11/Xproto.h>
30 #include <X11/keysym.h>
31 #include <X11/Xatom.h>
32
33 #ifdef USE_XINERAMA
34 #include <X11/extensions/Xinerama.h>
35 #endif
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40
41 #if defined(__alpha__) && defined(__GNUC__) && ((__GNUC__ == 2) && (__GNUC_MINOR__ < 96))       /* gets rid of some misalignment in GCC */
42 #pragma 2
43 #endif
44
45 #define EDOX_DEFAULT_W  512
46 #define EDOX_DEFAULT_H  400
47
48 Display            *disp;
49 Root                VRoot;
50
51 Window              win_main, win_title, win_exit, win_next, win_prev, win_text,
52    win_cover;
53 Imlib_Image         im_text;
54 Imlib_Image         im_title;
55 Imlib_Image         im_prev1, im_prev2;
56 Imlib_Image         im_next1, im_next2;
57 Imlib_Image         im_exit1, im_exit2;
58
59 static const char   doxdir[] = ENLIGHTENMENT_ROOT "/E-docs";
60 char               *docdir = NULL;
61
62 static Atom         ATOM_WM_DELETE_WINDOW = None;
63 static Atom         ATOM_WM_PROTOCOLS = None;
64
65 static              Window
66 FindRootWindow(Display * dpy)
67 {
68    Window              win1, win2, win3, root_win;
69    char               *str;
70    Atom                a, at;
71    int                 format_ret;
72    unsigned long       bytes_after, num_ret;
73    unsigned char      *retval;
74
75    root_win = DefaultRootWindow(dpy);
76
77    str = getenv("ENL_WM_ROOT");
78    if (!str)
79       goto done;
80    win1 = strtoul(str, NULL, 0);
81
82    a = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", True);
83
84    XGetWindowProperty(dpy, win1, a, 0, 1, False, XA_WINDOW, &at,
85                       &format_ret, &num_ret, &bytes_after, &retval);
86    if (!retval)
87       goto done;
88    win2 = *((Window *) retval);
89    XFree(retval);
90
91    XGetWindowProperty(dpy, win2, a, 0, 1, False, XA_WINDOW, &at,
92                       &format_ret, &num_ret, &bytes_after, &retval);
93    if (!retval)
94       goto done;
95    win3 = *((Window *) retval);
96    XFree(retval);
97
98    if (win2 != win3)
99       goto done;
100
101    root_win = win1;
102
103  done:
104    return root_win;
105 }
106
107 static void
108 VRootInit(void)
109 {
110    Window              root_return;
111    int                 x_return, y_return;
112    unsigned int        border_width_return;
113
114    VRoot.scr = DefaultScreen(disp);
115    VRoot.win = FindRootWindow(disp);
116    XGetGeometry(disp, VRoot.win, &root_return, &x_return, &y_return,
117                 &VRoot.w, &VRoot.h, &border_width_return, &VRoot.depth);
118    VRoot.vis = DefaultVisual(disp, VRoot.scr);
119    VRoot.depth = DefaultDepth(disp, VRoot.scr);
120    VRoot.cmap = DefaultColormap(disp, VRoot.scr);
121
122    imlib_set_color_usage(128);
123
124    imlib_context_set_display(disp);
125    imlib_context_set_visual(VRoot.vis);
126    imlib_context_set_colormap(VRoot.cmap);
127    imlib_context_set_dither(1);
128    imlib_context_set_dither_mask(0);
129 }
130
131 static              Window
132 CreateWindow(Window parent, int x, int y, int ww, int hh)
133 {
134    Window              win;
135    XSetWindowAttributes attr;
136    XSizeHints          hnt;
137
138    attr.backing_store = NotUseful;
139    attr.override_redirect = False;
140    attr.colormap = VRoot.cmap;
141    attr.border_pixel = 0;
142    attr.background_pixel = 0;
143    attr.save_under = False;
144    win = XCreateWindow(disp, parent, x, y, ww, hh, 0, VRoot.depth,
145                        InputOutput, VRoot.vis,
146                        CWOverrideRedirect | CWSaveUnder | CWBackingStore |
147                        CWColormap | CWBackPixel | CWBorderPixel, &attr);
148    XSetWindowBackground(disp, win, 0);
149    XStoreName(disp, win, "DOX: Enlightenment Document Viewer");
150    hnt.flags = USPosition | USSize | PPosition | PSize | PMinSize | PMaxSize;
151    hnt.x = x;
152    hnt.y = y;
153    hnt.width = ww;
154    hnt.height = hh;
155    hnt.min_width = ww;
156    hnt.max_width = ww;
157    hnt.min_height = hh;
158    hnt.max_height = hh;
159    XSetWMNormalHints(disp, win, &hnt);
160
161    ATOM_WM_PROTOCOLS = XInternAtom(disp, "WM_PROTOCOLS", False);
162    ATOM_WM_DELETE_WINDOW = XInternAtom(disp, "WM_DELETE_WINDOW", False);
163    XSetWMProtocols(disp, win, &ATOM_WM_DELETE_WINDOW, 1);
164
165    return win;
166 }
167
168 static              Imlib_Image
169 ImageLoad(const char *dir, const char *file)
170 {
171    char                tmp[4096];
172
173    Esnprintf(tmp, sizeof(tmp), "%s/%s", dir, file);
174    if (!exists(tmp))
175       return NULL;
176
177    return imlib_load_image(tmp);
178 }
179
180 static              Imlib_Image
181 ImageLoadDox(const char *file)
182 {
183    return ImageLoad(doxdir, file);
184 }
185
186 Imlib_Image
187 ImageLoadDoc(const char *file)
188 {
189    Imlib_Image         im;
190
191    im = ImageLoad(docdir, file);
192    if (im)
193       return im;
194
195    im = ImageLoadDox(file);
196
197    return im;
198 }
199
200 static void
201 ApplyImage1(Window win, Imlib_Image im)
202 {
203    Pixmap              pmap = 0, mask = 0;
204
205    imlib_context_set_image(im);
206    imlib_context_set_drawable(win);
207    imlib_render_pixmaps_for_whole_image(&pmap, &mask);
208    XSetWindowBackgroundPixmap(disp, win, pmap);
209    imlib_free_pixmap_and_mask(pmap);
210 }
211
212 static void
213 ApplyImage2(Window win, Imlib_Image im)
214 {
215    imlib_context_set_image(im);
216    imlib_context_set_drawable(win);
217    imlib_render_image_on_drawable(0, 0);
218 }
219
220 static void
221 LoadFile(const char *file, const char *docfile)
222 {
223    char                s[4096];
224    FILE               *f;
225
226    f = fopen(file, "r");
227    if (!f)
228      {
229         strcpy(s, docdir);
230         strcat(s, "/");
231         strcat(s, docfile);
232         f = fopen(s, "r");
233         if (!f)
234           {
235              printf("Edoc_dir %s does not contain a %s file\n", docdir,
236                     docfile);
237              exit(1);
238           }
239      }
240
241    GetObjects(f);
242    fclose(f);
243 }
244
245 #define ApplyImage3(win, im) \
246         XClearWindow(disp, win)
247
248 #define FREE_LINKS \
249 ll = l; \
250 while (ll) \
251 { \
252   l = ll; \
253   ll = ll->next; \
254   Efree(l->name); \
255   Efree(l); \
256 }
257
258 #define UPDATE_NOW \
259 { \
260 XSetWindowBackgroundPixmap(disp, win_text, draw); \
261 XClearWindow(disp, win_text); \
262 }
263
264 #define UPDATE \
265 { \
266   int up_i, up_j; \
267   int up_lut[16] = { 0, 8, 4, 12, 2, 6, 10, 14, \
268                      3, 11, 1, 9, 7, 13, 5, 15}; \
269   XSetWindowBackgroundPixmap(disp, win_text, draw); \
270   for (up_j = 0; up_j < 16; up_j++) \
271     { \
272       for (up_i = 0; up_i < h; up_i += 16) \
273         { \
274           XClearArea(disp, win_text, 0, up_i + up_lut[up_j], w, 1, False); \
275         } \
276       XSync(disp, False); \
277     } \
278 }
279
280 int
281 main(int argc, char **argv)
282 {
283    int                 pagenum, prev_pagenum;
284    int                 i, w, h, t, x, y;
285    int                 wx, wy;
286    char               *s;
287    const char         *docfile;
288    Pixmap              draw = 0;
289    Link               *l = NULL, *ll = NULL;
290    Imlib_Border        ibd;
291    int                *page_hist = NULL;
292    int                 page_hist_len = 1;
293    int                 page_hist_pos = 0;
294
295    w = EDOX_DEFAULT_W;
296    h = EDOX_DEFAULT_H;
297    x = 0;
298    y = 0;
299    pagenum = 0;
300
301    if (argc < 1)
302      {
303         printf("usage:\n"
304                "%s [-page page_number] [-file Edoc_fname] [-size width height] [Edoc_dir]\n",
305                argv[0]);
306         exit(1);
307      }
308
309    if ((disp = XOpenDisplay(NULL)) == NULL)
310      {
311         printf("%s: Cannot open X display.\n", argv[0]);
312         exit(0);
313      }
314
315    /* now we'll set the locale */
316    setlocale(LC_ALL, "");
317    if (!XSupportsLocale())
318       setlocale(LC_ALL, "C");
319    XSetLocaleModifiers("");
320    setlocale(LC_ALL, NULL);
321
322    /* I dont want any internationalisation of my numeric input & output */
323    setlocale(LC_NUMERIC, "C");
324
325    VRootInit();
326
327    docfile = "MAIN";
328    for (i = 1; i < argc; i++)
329      {
330         if ((!strcmp(argv[i], "-page")) && (i < (argc - 1)))
331            pagenum = atoi(argv[++i]);
332         else if ((!strcmp(argv[i], "-file")) && (i < (argc - 1)))
333            docfile = argv[++i];
334         else if ((!strcmp(argv[i], "-size")) && (i < (argc - 2)))
335           {
336              w = atoi(argv[++i]);
337              h = atoi(argv[++i]);
338           }
339         else
340            docdir = strdup(argv[i]);
341      }
342    if (docdir == NULL)
343       docdir = strdup(doxdir);
344    s = EMALLOC(char, strlen(docdir) + strlen(docfile) + 2 + 20);
345
346    sprintf(s, "%s/%s", docdir, docfile);
347    findLocalizedFile(s);
348
349    im_title = ImageLoadDox("title.png");
350    imlib_context_set_image(im_title);
351    ibd.left = 50;
352    ibd.right = 2;
353    ibd.top = 2;
354    ibd.bottom = 2;
355    imlib_image_set_border(&ibd);
356
357    im_prev1 = ImageLoadDox("prev1.png");
358    im_prev2 = ImageLoadDox("prev2.png");
359    im_next1 = ImageLoadDox("next1.png");
360    im_next2 = ImageLoadDox("next2.png");
361    im_exit1 = ImageLoadDox("exit1.png");
362    im_exit2 = ImageLoadDox("exit2.png");
363
364    t = 16;
365    wx = (VRoot.w - w) / 2;
366    wy = (VRoot.h - (h + t)) / 2;
367 #ifdef USE_XINERAMA
368    if (VRoot.win == DefaultRootWindow(disp) && XineramaIsActive(disp))
369      {
370         Window              rt, ch;
371         int                 d;
372         unsigned int        ud;
373         int                 pointer_x, pointer_y;
374         int                 num;
375         XineramaScreenInfo *screens;
376
377         XQueryPointer(disp, VRoot.win, &rt, &ch, &pointer_x, &pointer_y,
378                       &d, &d, &ud);
379
380         screens = XineramaQueryScreens(disp, &num);
381         for (i = 0; i < num; i++)
382           {
383              if (pointer_x >= screens[i].x_org &&
384                  pointer_x <= (screens[i].width + screens[i].x_org) &&
385                  pointer_y >= screens[i].y_org &&
386                  pointer_y <= (screens[i].height + screens[i].y_org))
387                {
388                   wx = ((screens[i].width - w) / 2) + screens[i].x_org;
389                   wy = ((screens[i].height - (h + t)) / 2) + screens[i].y_org;
390                }
391           }
392
393         XFree(screens);
394      }
395 #endif
396    win_main = CreateWindow(VRoot.win, wx, wy, w, h + t);
397    win_title =
398       XCreateSimpleWindow(disp, win_main, 0, 0, (w - 64 - 64 - t), t, 0, 0, 0);
399    win_prev =
400       XCreateSimpleWindow(disp, win_main, (w - 64 - 64 - t), 0, 64, t, 0, 0, 0);
401    win_next =
402       XCreateSimpleWindow(disp, win_main, (w - 64 - 64 - t) + 64, 0, 64, t, 0,
403                           0, 0);
404    win_exit =
405       XCreateSimpleWindow(disp, win_main, (w - 64 - 64 - t) + 64 + 64, 0, t, t,
406                           0, 0, 0);
407    win_text = XCreateSimpleWindow(disp, win_main, 0, t, w, h, 0, 0, 0);
408
409    XSelectInput(disp, win_main, KeyPressMask | KeyReleaseMask);
410    XSelectInput(disp, win_prev, ButtonPressMask | ButtonReleaseMask);
411    XSelectInput(disp, win_next, ButtonPressMask | ButtonReleaseMask);
412    XSelectInput(disp, win_exit, ButtonPressMask | ButtonReleaseMask);
413    XSelectInput(disp, win_text, ButtonPressMask | ButtonReleaseMask |
414                 PointerMotionMask);
415
416    draw = XCreatePixmap(disp, win_text, w, h, VRoot.depth);
417
418    ApplyImage1(win_title, im_title);
419    ApplyImage1(win_prev, im_prev1);
420    ApplyImage1(win_next, im_next1);
421    ApplyImage1(win_exit, im_exit1);
422
423    LoadFile(s, docfile);
424
425    l = RenderPage(draw, pagenum, w, h);
426    UPDATE_NOW;
427
428    XMapWindow(disp, win_title);
429    XMapWindow(disp, win_prev);
430    XMapWindow(disp, win_next);
431    XMapWindow(disp, win_exit);
432    XMapWindow(disp, win_text);
433    XMapWindow(disp, win_main);
434
435    XSync(disp, False);
436
437    page_hist = EMALLOC(int, 1);
438
439    page_hist[0] = 0;
440
441    for (;;)
442      {
443         KeySym              key;
444         XEvent              ev;
445
446         prev_pagenum = pagenum;
447
448         XNextEvent(disp, &ev);
449         switch (ev.type)
450           {
451           case KeyPress:
452              key = XLookupKeysym(&ev.xkey, 0);
453              switch (key)
454                {
455                case XK_Escape:
456                   exit(0);
457                   break;
458                case XK_Down:
459                   goto do_next;
460                case XK_Up:
461                   goto do_prev;
462                case XK_Home:
463                   pagenum = 0;
464                   page_hist_pos = 0;
465                   goto do_page;
466                case XK_End:
467                   pagenum = 99999;
468                   goto do_page;
469                case XK_Left:
470                case XK_Prior:
471                   pagenum--;
472                   page_hist_len = page_hist_pos + 1;
473                   goto do_page;
474                case XK_Right:
475                case XK_Next:
476                   pagenum++;
477                   page_hist_len = page_hist_pos + 1;
478                   goto do_page;
479                case XK_r:
480                   LoadFile(s, docfile);
481                   goto do_page1;
482                }
483              break;
484
485            do_next:
486              if (page_hist_pos >= page_hist_len - 1)
487                 break;
488              page_hist_pos++;
489              pagenum = page_hist[page_hist_pos];
490              goto do_page;
491
492            do_prev:
493              if (pagenum == page_hist[page_hist_pos])
494                {
495                   page_hist_pos--;
496                   if (page_hist_pos < 0)
497                      page_hist_pos = 0;
498                }
499              pagenum = page_hist[page_hist_pos];
500              goto do_page;
501
502            do_page_save:
503              pagenum = FixPage(pagenum);
504              if (pagenum == prev_pagenum)
505                 break;
506              page_hist_pos++;
507              if (page_hist_pos >= page_hist_len)
508                {
509                   page_hist_len++;
510                   page_hist = EREALLOC(int, page_hist, page_hist_len);
511                }
512              page_hist_len = page_hist_pos + 1;
513              page_hist[page_hist_pos] = pagenum;
514              goto do_page;
515
516            do_page:
517              pagenum = FixPage(pagenum);
518              if (pagenum == prev_pagenum)
519                 break;
520            do_page1:
521              FREE_LINKS;
522              l = RenderPage(draw, pagenum, w, h);
523              UPDATE;
524              break;
525
526           case ButtonPress:
527              if (ev.xbutton.window == win_prev)
528                 ApplyImage2(win_prev, im_prev2);
529              else if (ev.xbutton.window == win_next)
530                 ApplyImage2(win_next, im_next2);
531              else if (ev.xbutton.window == win_exit)
532                 ApplyImage2(win_exit, im_exit2);
533              else
534                {
535                   x = ev.xbutton.x;
536                   y = ev.xbutton.y;
537                   ll = l;
538                   while (ll)
539                     {
540                        if ((x >= ll->x) && (y >= ll->y) &&
541                            (x < (ll->x + ll->w)) && (y < (ll->y + ll->h)))
542                          {
543                             int                 pg;
544
545                             if (!strncmp("EXEC.", ll->name, 5))
546                               {
547                                  if (!fork())
548                                    {
549                                       char               *exe;
550
551                                       exe = &(ll->name[5]);
552                                       execl("/bin/sh", "/bin/sh", "-c", exe,
553                                             NULL);
554                                       exit(0);
555                                    }
556                               }
557                             else if (!strncmp("INPUT.", ll->name, 6))
558                               {
559                                  FILE               *p;
560                                  char               *exe, tmp[1024];
561
562                                  exe = &(ll->name[6]);
563                                  if (exe[0] != '/')
564                                    {
565                                       sprintf(tmp, "%s/%s", docdir, exe);
566                                       findLocalizedFile(tmp);
567                                       exe = tmp;
568                                    }
569                                  p = popen(exe, "r");
570                                  if (p)
571                                    {
572                                       int                 dirlen = 0;
573                                       char               *sp;
574
575                                       sp = exe;
576                                       while ((*sp) && (*sp != ' '))
577                                          sp++;
578                                       while ((*sp != '/') && (sp != exe))
579                                          sp--;
580                                       dirlen = sp - exe;
581                                       if (dirlen > 1)
582                                         {
583                                            Efree(docdir);
584                                            docdir = EMALLOC(char, dirlen + 1);
585
586                                            memcpy(docdir, exe, dirlen);
587                                            docdir[dirlen] = 0;
588                                         }
589                                       GetObjects(p);
590                                       pclose(p);
591                                       Efree(page_hist);
592                                       page_hist = EMALLOC(int, 1);
593
594                                       page_hist[0] = 0;
595                                       page_hist_len = 1;
596                                       pagenum = 0;
597                                       page_hist_pos = 0;
598                                       FREE_LINKS;
599                                       l = RenderPage(draw, pagenum, w, h);
600                                       UPDATE;
601                                    }
602                               }
603                             else
604                               {
605                                  pg = GetPage(ll->name);
606                                  if (pg >= 0)
607                                    {
608                                       pagenum = pg;
609                                       goto do_page_save;
610                                    }
611                               }
612                             break;
613                          }
614                        ll = ll->next;
615                     }
616                }
617              break;
618
619           case ButtonRelease:
620              if (ev.xbutton.window == win_prev)
621                {
622                   ApplyImage3(win_prev, im_prev1);
623                   goto do_prev;
624                }
625              else if (ev.xbutton.window == win_next)
626                {
627                   ApplyImage3(win_next, im_next1);
628                   pagenum++;
629                   goto do_page_save;
630                }
631              else if (ev.xbutton.window == win_exit)
632                {
633                   ApplyImage3(win_exit, im_exit1);
634                   exit(0);
635                }
636              break;
637
638           case EnterNotify:
639           case LeaveNotify:
640              break;
641
642           case MotionNotify:
643              while (XCheckTypedEvent(disp, ev.type, &ev))
644                 ;
645              {
646                 static Link        *pl = NULL;
647                 char                found = 0;
648
649                 x = ev.xmotion.x;
650                 y = ev.xmotion.y;
651                 ll = l;
652                 while (ll)
653                   {
654                      if ((x >= ll->x) && (y >= ll->y) &&
655                          (x < (ll->x + ll->w)) && (y < (ll->y + ll->h)))
656                        {
657                           GC                  gc;
658                           XGCValues           gcv;
659                           int                 r, g, b;
660                           XColor              xclr;
661
662                           if (pl != ll)
663                             {
664                                if (pl)
665                                  {
666                                     UPDATE_NOW;
667                                  }
668                                GetLinkColors(pagenum, &r, &g, &b);
669                                ESetColor(&xclr, r, g, b);
670                                EAllocColor(&xclr);
671                                gc = XCreateGC(disp, win_text, 0, &gcv);
672                                XSetForeground(disp, gc, xclr.pixel);
673                                XDrawRectangle(disp, win_text, gc, ll->x, ll->y,
674                                               ll->w, ll->h);
675                                XFreeGC(disp, gc);
676                                pl = ll;
677                             }
678                           found = 1;
679                           ll = NULL;
680                        }
681                      if (ll)
682                         ll = ll->next;
683                   }
684                 if (!found)
685                   {
686                      UPDATE_NOW;
687                      pl = NULL;
688                   }
689              }
690              break;
691
692           case ClientMessage:
693              if (ev.xclient.message_type == ATOM_WM_PROTOCOLS &&
694                  (Atom) ev.xclient.data.l[0] == ATOM_WM_DELETE_WINDOW)
695                 goto done;
696              break;
697
698           default:
699              break;
700           }
701      }
702  done:
703    return 0;
704 }
705
706 void
707 ESetColor(XColor * pxc, int r, int g, int b)
708 {
709    pxc->red = (r << 8) | r;
710    pxc->green = (g << 8) | g;
711    pxc->blue = (b << 8) | b;
712 }
713
714 void
715 EGetColor(XColor * pxc, int *pr, int *pg, int *pb)
716 {
717    *pr = pxc->red >> 8;
718    *pg = pxc->green >> 8;
719    *pb = pxc->blue >> 8;
720 }