c068628815c99afe64cd3ae11c4240ede92ac728
[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,2014, 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 #include "gmxpre.h"
38
39 #include "config.h"
40
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "gromacs/legacyheaders/typedefs.h"
45 #include "gromacs/utility/cstringutil.h"
46 #include "gromacs/utility/smalloc.h"
47 #include "Xstuff.h"
48 #include "x11.h"
49
50 /* These colours will be mapped to black on a monochrome screen */
51 unsigned long BLACK, BLUE, GREEN, CYAN, RED, BROWN, GREY, DARKGREY;
52
53 /* These colours will be mapped to white on a monochrome screen */
54 unsigned long LIGHTBLUE, LIGHTGREEN, LIGHTGREY, LIGHTCYAN, LIGHTRED, VIOLET, YELLOW, WHITE;
55
56 static XFontStruct *XLQF(FILE gmx_unused *err, Display *disp, const char *name)
57 {
58     XFontStruct *font = XLoadQueryFont(disp, name);
59 #ifdef DEBUG
60     if (font != NULL)
61     {
62         fprintf(err, "Loaded font %s\n", name);
63     }
64 #endif
65     return font;
66 }
67
68 static XFontStruct *GetFont(FILE *err, Display *disp, char *name)
69 {
70     static const char *fontnames[] = {
71         "sansb12", "8x13bold", "8x13",
72         "9x15", "6x13", "fixed"
73     };
74 #define MAXNAMES (sizeof(fontnames)/sizeof(fontnames[0]))
75     unsigned int       i;
76     XFontStruct       *font;
77     int                count;
78     char             **fontlist;
79     bool               bFont = false;
80
81     if (name)
82     {
83         bFont = ((font = XLQF(err, disp, name)) != NULL);
84     }
85     else
86     {
87         font = NULL;
88     }
89
90     for (i = 0; (!bFont && (i < MAXNAMES)); i++)
91     {
92         bFont = ((font = XLQF(err, disp, fontnames[i])) != NULL);
93     }
94
95     if (!bFont)
96     {
97         fontlist = XListFonts(disp, "?", 1, &count);
98         if (count != 0)
99         {
100             bFont = ((font = XLQF(err, disp, fontlist[0])) != NULL);
101         }
102     }
103     if (!bFont)
104     {
105         fprintf (err, "Cannot load any suitable font\n");
106     }
107     return font;
108 }
109
110 static GC GetGC(Display *disp, XFontStruct *font)
111 {
112     XGCValues     values;
113
114     values.font       = font->fid;
115     values.foreground = WhitePixel(disp, DefaultScreen(disp));
116
117     return XCreateGC(disp, DefaultRootWindow(disp), GCForeground|GCFont, &values);
118 }
119
120 void GetNamedColor(t_x11 *x11, const char *name, unsigned long *col)
121 {
122     /* If name is found than col set to that colour else col is unchanged */
123     XColor exact, clr;
124
125     if (XAllocNamedColor(x11->disp, x11->cmap, name, &clr, &exact))
126     {
127         *col = clr.pixel;
128     }
129     else
130     {
131         fprintf(x11->console, "No colour %s\n", name);
132     }
133 }
134
135 static t_wlist *GetWList(t_x11 *x11, Window w)
136 {
137     t_wlist *curs;
138
139     curs = x11->wlist;
140     while (curs && (curs->w != w))
141     {
142         curs = curs->next;
143     }
144
145     return curs;
146 }
147
148 static void MainLoop(t_x11 *x11)
149 {
150     bool        bReturn;
151     XEvent      event;
152     t_wlist    *curs;
153     Window      w;
154
155     for (bReturn = false; (!bReturn); )
156     {
157         if (x11->wlist)
158         {
159             XNextEvent(x11->disp, &event);
160             w    = event.xany.window;
161             curs = GetWList(x11, w);
162             if (!curs)
163             {
164                 bReturn = true;
165             }
166             if (!bReturn)
167             {
168                 switch (event.type)
169                 {
170                     case Expose:
171                         /* Filter out expose events with non-zero count field */
172                         if (event.xexpose.count != 0)
173                         {
174                             curs = NULL;
175                         }
176                         break;
177                     case ConfigureNotify:
178                         /* Check if more are coming...
179                            if (XCheckTypedWindowEvent(x11->disp,w,ConfigureNotify,&config))
180                            curs=NULL; */
181                         break;
182                     default:
183                         break;
184                 }
185                 if (curs)
186                 {
187                     bReturn = (*curs->cb)(x11, &event, w, curs->data);
188                 }
189             }
190         }
191     }
192 }
193
194 static void RegisterCallback(t_x11 *x11, Window w, Window Parent,
195                              CallBack cb, void *data)
196 {
197     t_wlist *curs, *item;
198
199     snew(item, 1);
200     item->w      = w;
201     item->Parent = Parent;
202     item->cb     = cb;
203     item->mask   = 0;
204     item->data   = data;
205     item->next   = NULL;
206
207     if (x11->wlist)
208     {
209         curs = x11->wlist;
210         while (curs->next)
211         {
212             curs = curs->next;
213         }
214         curs->next = item;
215     }
216     else
217     {
218         x11->wlist = item;
219     }
220 }
221
222 static void UnRegisterCallback(t_x11 *x11, Window w)
223 {
224     t_wlist *curs;
225
226     curs = x11->wlist;
227     if (curs)
228     {
229         if (curs->w == w)
230         {
231             x11->wlist = curs->next;
232             sfree(curs);
233         }
234         else
235         {
236             while (curs->next && (curs->next->w != w))
237             {
238                 curs = curs->next;
239             }
240             if (curs->next)
241             {
242                 t_wlist *tmp = curs->next;
243
244                 curs->next = curs->next->next;
245                 sfree(tmp);
246             }
247         }
248     }
249 }
250
251 static void SetInputMask(t_x11 *x11, Window w, unsigned long mask)
252 {
253     t_wlist *curs;
254
255     curs = GetWList(x11, w);
256     if (curs)
257     {
258         curs->mask = mask;
259         XSelectInput(x11->disp, w, (long)mask);
260     }
261     else
262     {
263         fprintf(x11->console, "No such window (%d)\n", (int)w);
264     }
265 }
266
267 static unsigned long GetInputMask(t_x11 *x11, Window w)
268 {
269     t_wlist *curs;
270
271     curs = GetWList(x11, w);
272     if (curs)
273     {
274         return curs->mask;
275     }
276     else
277     {
278         return 0;
279     }
280 }
281
282 static void CleanUp(t_x11 *x11)
283 {
284     t_wlist *curs;
285
286     curs = x11->wlist;
287     while (curs)
288     {
289         x11->wlist = curs->next;
290         XDestroyWindow(x11->disp, curs->w);
291         sfree(curs);
292         curs = x11->wlist;
293     }
294     XCloseDisplay(x11->disp);
295 }
296
297 static void Flush(t_x11 *x11)
298 {
299     fflush(x11->console);
300 }
301
302 t_x11 *GetX11(int *argc, char *argv[])
303 {
304     static const char *v_name[] = {
305         "DirectColor", "TrueColor", "PseudoColor",
306         "StaticColor", "GrayScale", "StaticGray"
307     };
308     static int         v_class[] = {
309         DirectColor, TrueColor, PseudoColor,
310         StaticColor, GrayScale, StaticGray
311     };
312 #define NCLASS (sizeof(v_class)/sizeof(v_class[0]))
313
314     XVisualInfo     v_info;
315     t_x11          *x11;
316     int             ARGC;
317     char          **ARGV;
318     char           *display;
319     char           *fontname;
320     char           *title, *FG = NULL, *BG = NULL;
321     bool            bVerbose = false;
322     int             i;
323
324     title = gmx_strdup(argv[0]);
325
326     /* First check environment */
327     fontname = getenv("GMX_FONT");
328     display  = getenv("DISPLAY");
329
330     snew(ARGV, *argc);
331     ARGC = 1;
332     for (i = 1; (i < *argc); i++)
333     {
334         if (argv[i][0] == '-')
335         {
336             if (strlen(argv[i]) > 1)
337             {
338                 if ((*argc) > i+1)
339                 {
340                     switch (argv[i][1])
341                     {
342                         case 'b':
343                             BG = argv[++i];
344                             break;
345                         case 'd':
346                             display = argv[++i];
347                             break;
348                         case 'f':
349                             switch (argv[i][2])
350                             {
351                                 case 'o':
352                                     fontname = argv[++i];
353                                     break;
354                                 case 'g':
355                                     FG = argv[++i];
356                                     break;
357                             }
358                             break;
359                         case 't':
360                             sfree(title);
361                             title = gmx_strdup(argv[++i]);
362                             break;
363                         case 'v':
364                             bVerbose = true;
365                             break;
366                         default:
367                             ARGV[ARGC++] = argv[i];
368                             break;
369                     }
370                 }
371             }
372         }
373         else
374         {
375             ARGV[ARGC++] = argv[i];
376         }
377     }
378     for (i = 1; (i < ARGC); i++)
379     {
380         argv[i] = ARGV[i];
381     }
382     *argc      = ARGC;
383     argv[ARGC] = NULL;
384
385     snew(x11, 1);
386     x11->dispname = display;
387     if (bVerbose)
388     {
389         x11->console = stderr;
390     }
391     else
392     if ((x11->console = fopen("/dev/null", "w")) == NULL)
393     {
394         x11->console = stderr;
395     }
396
397     if ((x11->disp = XOpenDisplay(display)) == NULL)
398     {
399         if (bVerbose)
400         {
401             fprintf(x11->console, "Display %s invalid\n", display);
402         }
403         return NULL;
404     }
405
406     if ((x11->font = GetFont(x11->console, x11->disp, fontname)) == NULL)
407     {
408         return NULL;
409     }
410     if ((x11->gc = GetGC(x11->disp, x11->font)) == NULL)
411     {
412         return NULL;
413     }
414
415     x11->root   = DefaultRootWindow(x11->disp);
416     x11->screen = DefaultScreen(x11->disp);
417     x11->depth  = DefaultDepth(x11->disp, x11->screen);
418     x11->cmap   = DefaultColormap(x11->disp, x11->screen);
419
420     /* These colours will be mapped to black on a monochrome screen */
421     x11->fg = BLACK = BLUE = GREEN = CYAN = RED = BROWN = GREY = DARKGREY =
422                                         BlackPixel(x11->disp, x11->screen);
423
424     /* These colours will be mapped to white on a monochrome screen */
425     x11->bg       =
426         LIGHTBLUE = LIGHTGREY = LIGHTGREEN = LIGHTCYAN = LIGHTRED = VIOLET = YELLOW = WHITE =
427                                         WhitePixel(x11->disp, x11->screen);
428
429     if (x11->depth > 1)
430     {
431         /* Not B & W, Look what kind of screen we've got... */
432         for (i = 0; (i < (int)NCLASS); i++)
433         {
434             if (!XMatchVisualInfo(x11->disp, x11->screen, x11->depth,
435                                   v_class[i], &v_info))
436             {
437                 break;
438             }
439         }
440         if ((i == 4) || (i == 5))
441         {
442             fprintf(x11->console, "Greyscale screen, using B & W only\n");
443         }
444         else
445         {
446             /* We have real color! */
447             fprintf(x11->console, "%s screen with depth %d.\n",
448                     (i == NCLASS) ? "Unknown" : v_name[i], x11->depth);
449             GetNamedColor(x11, "midnight blue", &BLUE);
450             GetNamedColor(x11, "DarkGreen", &GREEN);
451             GetNamedColor(x11, "SeaGreen", &CYAN);
452             GetNamedColor(x11, "red4", &RED);
453             GetNamedColor(x11, "Gray", &GREY);
454             GetNamedColor(x11, "Gray", &DARKGREY);
455             GetNamedColor(x11, "LightGray", &LIGHTGREY);
456             GetNamedColor(x11, "green", &LIGHTGREEN);
457             GetNamedColor(x11, "cyan", &LIGHTCYAN);
458             GetNamedColor(x11, "tomato1", &LIGHTRED);
459             GetNamedColor(x11, "violet", &VIOLET);
460             GetNamedColor(x11, "yellow", &YELLOW);
461             GetNamedColor(x11, "brown", &BROWN);
462             GetNamedColor(x11, "CornFlowerBlue", &LIGHTBLUE);
463         }
464     }
465     else
466     {
467         fprintf(x11->console, "Monochrome screen.\n");
468     }
469
470     /* We should use Xrm here... */
471     if (FG)
472     {
473         GetNamedColor(x11, FG, &(x11->fg));
474     }
475     else
476     {
477         x11->fg = BLACK;
478     }
479     if (BG)
480     {
481         GetNamedColor(x11, BG, &(x11->bg));
482     }
483     else
484     {
485         x11->bg = LIGHTGREY;
486     }
487     x11->title = gmx_strdup(title);
488     sfree(title);
489     x11->wlist              = NULL;
490     x11->GetNamedColor      = &GetNamedColor;
491     x11->MainLoop           = &MainLoop;
492     x11->RegisterCallback   = &RegisterCallback;
493     x11->UnRegisterCallback = &UnRegisterCallback;
494     x11->SetInputMask       = &SetInputMask;
495     x11->GetInputMask       = &GetInputMask;
496     x11->CleanUp            = &CleanUp;
497     x11->Flush              = &Flush;
498
499     x11->Flush(x11);
500
501     return x11;
502 }