Merge release-4-6 into master
[alexxy/gromacs.git] / src / programs / view / x11.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2013, The GROMACS development team.
6  * Copyright (c) 2013, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <string.h>
42 #include "typedefs.h"
43 #include "sysstuff.h"
44 #include "smalloc.h"
45 #include "string2.h"
46 #include "Xstuff.h"
47 #include "x11.h"
48
49 /* These colours will be mapped to black on a monochrome screen */
50 unsigned long BLACK, BLUE, GREEN, CYAN, RED, BROWN, GREY, DARKGREY;
51
52 /* These colours will be mapped to white on a monochrome screen */
53 unsigned long LIGHTBLUE, LIGHTGREEN, LIGHTGREY, LIGHTCYAN, LIGHTRED, VIOLET, YELLOW, WHITE;
54
55 static XFontStruct *XLQF(FILE gmx_unused *err, Display *disp, const char *name)
56 {
57     XFontStruct *font = XLoadQueryFont(disp, name);
58 #ifdef DEBUG
59     if (font != NULL)
60     {
61         fprintf(err, "Loaded font %s\n", name);
62     }
63 #endif
64     return font;
65 }
66
67 static XFontStruct *GetFont(FILE *err, Display *disp, char *name)
68 {
69     static const char *fontnames[] = {
70         "sansb12", "8x13bold", "8x13",
71         "9x15", "6x13", "fixed"
72     };
73 #define MAXNAMES (sizeof(fontnames)/sizeof(fontnames[0]))
74     unsigned int       i;
75     XFontStruct       *font;
76     int                count;
77     char             **fontlist;
78     bool               bFont = false;
79
80     if (name)
81     {
82         bFont = ((font = XLQF(err, disp, name)) != NULL);
83     }
84     else
85     {
86         font = NULL;
87     }
88
89     for (i = 0; (!bFont && (i < MAXNAMES)); i++)
90     {
91         bFont = ((font = XLQF(err, disp, fontnames[i])) != NULL);
92     }
93
94     if (!bFont)
95     {
96         fontlist = XListFonts(disp, "?", 1, &count);
97         if (count != 0)
98         {
99             bFont = ((font = XLQF(err, disp, fontlist[0])) != NULL);
100         }
101     }
102     if (!bFont)
103     {
104         fprintf (err, "Cannot load any suitable font\n");
105     }
106     return font;
107 }
108
109 static GC GetGC(Display *disp, XFontStruct *font)
110 {
111     XGCValues     values;
112
113     values.font       = font->fid;
114     values.foreground = WhitePixel(disp, DefaultScreen(disp));
115
116     return XCreateGC(disp, DefaultRootWindow(disp), GCForeground|GCFont, &values);
117 }
118
119 void GetNamedColor(t_x11 *x11, const char *name, unsigned long *col)
120 {
121     /* If name is found than col set to that colour else col is unchanged */
122     XColor exact, clr;
123
124     if (XAllocNamedColor(x11->disp, x11->cmap, name, &clr, &exact))
125     {
126         *col = clr.pixel;
127     }
128     else
129     {
130         fprintf(x11->console, "No colour %s\n", name);
131     }
132 }
133
134 static t_wlist *GetWList(t_x11 *x11, Window w)
135 {
136     t_wlist *curs;
137
138     curs = x11->wlist;
139     while (curs && (curs->w != w))
140     {
141         curs = curs->next;
142     }
143
144     return curs;
145 }
146
147 static void MainLoop(t_x11 *x11)
148 {
149     bool        bReturn;
150     XEvent      event;
151     t_wlist    *curs;
152     Window      w;
153
154     for (bReturn = false; (!bReturn); )
155     {
156         if (x11->wlist)
157         {
158             XNextEvent(x11->disp, &event);
159             w    = event.xany.window;
160             curs = GetWList(x11, w);
161             if (!curs)
162             {
163                 bReturn = true;
164             }
165             if (!bReturn)
166             {
167                 switch (event.type)
168                 {
169                     case Expose:
170                         /* Filter out expose events with non-zero count field */
171                         if (event.xexpose.count != 0)
172                         {
173                             curs = NULL;
174                         }
175                         break;
176                     case ConfigureNotify:
177                         /* Check if more are coming...
178                            if (XCheckTypedWindowEvent(x11->disp,w,ConfigureNotify,&config))
179                            curs=NULL; */
180                         break;
181                     default:
182                         break;
183                 }
184                 if (curs)
185                 {
186                     bReturn = (*curs->cb)(x11, &event, w, curs->data);
187                 }
188             }
189         }
190     }
191 }
192
193 static void RegisterCallback(t_x11 *x11, Window w, Window Parent,
194                              CallBack cb, void *data)
195 {
196     t_wlist *curs, *item;
197
198     snew(item, 1);
199     item->w      = w;
200     item->Parent = Parent;
201     item->cb     = cb;
202     item->mask   = 0;
203     item->data   = data;
204     item->next   = NULL;
205
206     if (x11->wlist)
207     {
208         curs = x11->wlist;
209         while (curs->next)
210         {
211             curs = curs->next;
212         }
213         curs->next = item;
214     }
215     else
216     {
217         x11->wlist = item;
218     }
219 }
220
221 static void UnRegisterCallback(t_x11 *x11, Window w)
222 {
223     t_wlist *curs;
224
225     curs = x11->wlist;
226     if (curs)
227     {
228         if (curs->w == w)
229         {
230             x11->wlist = curs->next;
231             sfree(curs);
232         }
233         else
234         {
235             while (curs->next && (curs->next->w != w))
236             {
237                 curs = curs->next;
238             }
239             if (curs->next)
240             {
241                 t_wlist *tmp = curs->next;
242
243                 curs->next = curs->next->next;
244                 sfree(tmp);
245             }
246         }
247     }
248 }
249
250 static void SetInputMask(t_x11 *x11, Window w, unsigned long mask)
251 {
252     t_wlist *curs;
253
254     curs = GetWList(x11, w);
255     if (curs)
256     {
257         curs->mask = mask;
258         XSelectInput(x11->disp, w, (long)mask);
259     }
260     else
261     {
262         fprintf(x11->console, "No such window (%d)\n", (int)w);
263     }
264 }
265
266 static unsigned long GetInputMask(t_x11 *x11, Window w)
267 {
268     t_wlist *curs;
269
270     curs = GetWList(x11, w);
271     if (curs)
272     {
273         return curs->mask;
274     }
275     else
276     {
277         return 0;
278     }
279 }
280
281 static void CleanUp(t_x11 *x11)
282 {
283     t_wlist *curs;
284
285     curs = x11->wlist;
286     while (curs)
287     {
288         x11->wlist = curs->next;
289         XDestroyWindow(x11->disp, curs->w);
290         sfree(curs);
291         curs = x11->wlist;
292     }
293     XCloseDisplay(x11->disp);
294 }
295
296 static void Flush(t_x11 *x11)
297 {
298     fflush(x11->console);
299 }
300
301 t_x11 *GetX11(int *argc, char *argv[])
302 {
303     static const char *v_name[] = {
304         "DirectColor", "TrueColor", "PseudoColor",
305         "StaticColor", "GrayScale", "StaticGray"
306     };
307     static int         v_class[] = {
308         DirectColor, TrueColor, PseudoColor,
309         StaticColor, GrayScale, StaticGray
310     };
311 #define NCLASS (sizeof(v_class)/sizeof(v_class[0]))
312
313     XVisualInfo     v_info;
314     t_x11          *x11;
315     int             ARGC;
316     char          **ARGV;
317     char           *display;
318     char           *fontname;
319     char           *title, *FG = NULL, *BG = NULL;
320     bool            bVerbose = false;
321     int             i;
322
323     title = strdup(argv[0]);
324
325     /* First check environment */
326     fontname = getenv("GMXFONT");
327     display  = getenv("DISPLAY");
328
329     snew(ARGV, *argc);
330     ARGC = 1;
331     for (i = 1; (i < *argc); i++)
332     {
333         if (argv[i][0] == '-')
334         {
335             if (strlen(argv[i]) > 1)
336             {
337                 if ((*argc) > i+1)
338                 {
339                     switch (argv[i][1])
340                     {
341                         case 'b':
342                             BG = argv[++i];
343                             break;
344                         case 'd':
345                             display = argv[++i];
346                             break;
347                         case 'f':
348                             switch (argv[i][2])
349                             {
350                                 case 'o':
351                                     fontname = argv[++i];
352                                     break;
353                                 case 'g':
354                                     FG = argv[++i];
355                                     break;
356                             }
357                             break;
358                         case 't':
359                             sfree(title);
360                             title = strdup(argv[++i]);
361                             break;
362                         case 'v':
363                             bVerbose = true;
364                             break;
365                         default:
366                             ARGV[ARGC++] = argv[i];
367                             break;
368                     }
369                 }
370             }
371         }
372         else
373         {
374             ARGV[ARGC++] = argv[i];
375         }
376     }
377     for (i = 1; (i < ARGC); i++)
378     {
379         argv[i] = ARGV[i];
380     }
381     *argc      = ARGC;
382     argv[ARGC] = NULL;
383
384     snew(x11, 1);
385     x11->dispname = display;
386     if (bVerbose)
387     {
388         x11->console = stderr;
389     }
390     else
391     if ((x11->console = fopen("/dev/null", "w")) == NULL)
392     {
393         x11->console = stderr;
394     }
395
396     if ((x11->disp = XOpenDisplay(display)) == NULL)
397     {
398         if (bVerbose)
399         {
400             fprintf(x11->console, "Display %s invalid\n", display);
401         }
402         return NULL;
403     }
404
405     if ((x11->font = GetFont(x11->console, x11->disp, fontname)) == NULL)
406     {
407         return NULL;
408     }
409     if ((x11->gc = GetGC(x11->disp, x11->font)) == NULL)
410     {
411         return NULL;
412     }
413
414     x11->root   = DefaultRootWindow(x11->disp);
415     x11->screen = DefaultScreen(x11->disp);
416     x11->depth  = DefaultDepth(x11->disp, x11->screen);
417     x11->cmap   = DefaultColormap(x11->disp, x11->screen);
418
419     /* These colours will be mapped to black on a monochrome screen */
420     x11->fg = BLACK = BLUE = GREEN = CYAN = RED = BROWN = GREY = DARKGREY =
421                                         BlackPixel(x11->disp, x11->screen);
422
423     /* These colours will be mapped to white on a monochrome screen */
424     x11->bg       =
425         LIGHTBLUE = LIGHTGREY = LIGHTGREEN = LIGHTCYAN = LIGHTRED = VIOLET = YELLOW = WHITE =
426                                         WhitePixel(x11->disp, x11->screen);
427
428     if (x11->depth > 1)
429     {
430         /* Not B & W, Look what kind of screen we've got... */
431         for (i = 0; (i < (int)NCLASS); i++)
432         {
433             if (!XMatchVisualInfo(x11->disp, x11->screen, x11->depth,
434                                   v_class[i], &v_info))
435             {
436                 break;
437             }
438         }
439         if ((i == 4) || (i == 5))
440         {
441             fprintf(x11->console, "Greyscale screen, using B & W only\n");
442         }
443         else
444         {
445             /* We have real color! */
446             fprintf(x11->console, "%s screen with depth %d.\n",
447                     (i == NCLASS) ? "Unknown" : v_name[i], x11->depth);
448             GetNamedColor(x11, "midnight blue", &BLUE);
449             GetNamedColor(x11, "DarkGreen", &GREEN);
450             GetNamedColor(x11, "SeaGreen", &CYAN);
451             GetNamedColor(x11, "red4", &RED);
452             GetNamedColor(x11, "Gray", &GREY);
453             GetNamedColor(x11, "Gray", &DARKGREY);
454             GetNamedColor(x11, "LightGray", &LIGHTGREY);
455             GetNamedColor(x11, "green", &LIGHTGREEN);
456             GetNamedColor(x11, "cyan", &LIGHTCYAN);
457             GetNamedColor(x11, "tomato1", &LIGHTRED);
458             GetNamedColor(x11, "violet", &VIOLET);
459             GetNamedColor(x11, "yellow", &YELLOW);
460             GetNamedColor(x11, "brown", &BROWN);
461             GetNamedColor(x11, "CornFlowerBlue", &LIGHTBLUE);
462         }
463     }
464     else
465     {
466         fprintf(x11->console, "Monochrome screen.\n");
467     }
468
469     /* We should use Xrm here... */
470     if (FG)
471     {
472         GetNamedColor(x11, FG, &(x11->fg));
473     }
474     else
475     {
476         x11->fg = BLACK;
477     }
478     if (BG)
479     {
480         GetNamedColor(x11, BG, &(x11->bg));
481     }
482     else
483     {
484         x11->bg = LIGHTGREY;
485     }
486     x11->title = strdup(title);
487     sfree(title);
488     x11->wlist              = NULL;
489     x11->GetNamedColor      = &GetNamedColor;
490     x11->MainLoop           = &MainLoop;
491     x11->RegisterCallback   = &RegisterCallback;
492     x11->UnRegisterCallback = &UnRegisterCallback;
493     x11->SetInputMask       = &SetInputMask;
494     x11->GetInputMask       = &GetInputMask;
495     x11->CleanUp            = &CleanUp;
496     x11->Flush              = &Flush;
497
498     x11->Flush(x11);
499
500     return x11;
501 }