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