displayinfo update gl_probe for nvidia shader lang ver
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcdisplayinfo.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplay.h"
23 #include "bcdisplayinfo.h"
24 #include "bcsignals.h"
25 #include "bcwindowbase.h"
26 #include "clip.h"
27 #include "language.h"
28
29 #include <X11/X.h>
30 #include <X11/Xlib.h>
31 #include <X11/Xutil.h>
32 #include <unistd.h>
33
34 #define TEST_SIZE 128
35 #define TEST_DSIZE 28
36 #define TEST_SIZE2 164
37
38 int BC_DisplayInfo::top_border = -1;
39 int BC_DisplayInfo::left_border = -1;
40 int BC_DisplayInfo::bottom_border = -1;
41 int BC_DisplayInfo::right_border = -1;
42 int BC_DisplayInfo::auto_reposition_x = -1;
43 int BC_DisplayInfo::auto_reposition_y = -1;
44 char BC_DisplayInfo::gl_shader_version[64] = { 0, };
45
46 BC_DisplayInfo::BC_DisplayInfo(const char *display_name, int show_error)
47 {
48         vis = 0;
49         depth = 0;
50         scrnum = -1;
51         xinerama_screens = -1;
52         xinerama_info = 0;
53         init_window(display_name, show_error);
54 }
55
56 BC_DisplayInfo::~BC_DisplayInfo()
57 {
58         if( xinerama_info ) XFree(xinerama_info);
59 #ifndef SINGLE_THREAD
60         XCloseDisplay(display);
61 #endif
62 }
63
64
65 void BC_DisplayInfo::parse_geometry(char *geom, int *x, int *y, int *width, int *height)
66 {
67         XParseGeometry(geom, x, y, (unsigned int*)width, (unsigned int*)height);
68 }
69
70
71 int BC_DisplayInfo::get_xinerama_screens()
72 {
73         if( xinerama_screens < 0 ) {
74                 xinerama_screens = 0;
75                 if( XineramaIsActive(display) )
76                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
77         }
78         return xinerama_screens;
79 }
80
81 int BC_DisplayInfo::xinerama_geometry(int screen, int &x, int &y, int &w, int &h)
82 {
83         int screens = get_xinerama_screens();
84         if( !screens ) return 1;
85         if( screen >= 0 ) {
86                 int k = screens;
87                 while( --k >= 0 && xinerama_info[k].screen_number != screen );
88                 if( k < 0 ) return 1;
89                 x = xinerama_info[k].x_org;  w = xinerama_info[k].width;
90                 y = xinerama_info[k].y_org;  h = xinerama_info[k].height;
91         }
92         else {
93                 int sx0 = INT_MAX, sx1 = INT_MIN;
94                 int sy0 = INT_MAX, sy1 = INT_MIN;
95                 for( int i=0; i<screens; ++i ) {
96                         int x0 = xinerama_info[i].x_org;
97                         int x1 = x0 + xinerama_info[i].width;
98                         if( sx0 > x0 ) sx0 = x0;
99                         if( sx1 < x1 ) sx1 = x1;
100                         int y0 = xinerama_info[i].y_org;
101                         int y1 = y0 + xinerama_info[i].height;
102                         if( sy0 > y0 ) sy0 = y0;
103                         if( sy1 < y1 ) sy1 = y1;
104                 }
105                 x = sx0;  w = sx1 - sx0;
106                 y = sy0;  h = sy1 - sy0;
107         }
108         return 0;
109 }
110
111 static void get_top_coords(Display *display, Window win, int &px,int &py, int &tx,int &ty)
112 {
113         Window *pcwin = 0;  unsigned int ncwin = 0;
114         Window cwin = 0, pwin = 0, root = 0;
115         XQueryTree(display, win, &root, &pwin, &pcwin, &ncwin);
116         if( pcwin ) XFree(pcwin);
117         XTranslateCoordinates(display, pwin, root, 0,0, &px,&py, &cwin);
118 //printf(" win=%lx, px/py=%d/%d\n", win, px,py);
119
120         int nx = px, ny = py;  pwin = win;
121         for( int i=5; --i>=0; ) {
122                 win = pwin;  pwin = 0;  pcwin = 0;  ncwin = 0;
123                 Window rwin = 0;
124 // XQuerytTree has been known to fail here
125                 XQueryTree(display, win, &rwin, &pwin, &pcwin, &ncwin);
126                 if( pcwin ) XFree(pcwin);
127                 if( !rwin || rwin != root || pwin == root ) break;
128                 XTranslateCoordinates(display, pwin, root, 0,0, &nx,&ny, &cwin);
129 //printf(" win=%lx, nx/ny=%d/%d\n", win, nx,ny);
130         }
131         tx = nx;  ty = ny;
132 }
133
134
135 #ifdef HAVE_GL
136 int BC_DisplayInfo::gl_probe()
137 {
138         int ncfgs = 0;
139         XVisualInfo *vis_info = 0;
140         GLXFBConfig *fb_cfgs = 0, cfg = 0;
141 #if 0
142 // find prefered config via glxinfo: mesa_hack
143         static int attribs[] = {
144                 GLX_RGBA,
145                 GLX_RED_SIZE, 1,
146                 GLX_GREEN_SIZE, 1,
147                 GLX_BLUE_SIZE, 1,
148                 GLX_DEPTH_SIZE, 1,
149                 GLX_STENCIL_SIZE, 1,
150                 GLX_ACCUM_RED_SIZE, 1,
151                 GLX_ACCUM_GREEN_SIZE, 1,
152                 GLX_ACCUM_BLUE_SIZE, 1,
153                 GLX_ACCUM_ALPHA_SIZE, 1,
154                 GLX_DOUBLEBUFFER,
155                 None
156         };
157         vis_info = glXChooseVisual(display, scrnum, attribs);
158         VisualID vis_id = vis_info ? vis_info->visualid : 0;
159         fb_cfgs = glXGetFBConfigs(display, scrnum, &ncfgs);
160         int n = !fb_cfgs ? 0 : ncfgs;
161         for( int i=0; --n>=0; ++i ) {
162                 if( vis_info ) { XFree(vis_info);  vis_info = 0; }
163                 vis_info = glXGetVisualFromFBConfig(display, cfg=fb_cfgs[i]);
164                 if( vis_info && vis_info->visualid == vis_id ) break;
165         }
166         if( n < 0 ) {
167                 cfg = 0;
168                 if( fb_cfgs ) { XFree(fb_cfgs);  fb_cfgs = 0; }
169                 if( vis_info ) { XFree(vis_info);  vis_info = 0; }
170         }
171 #endif
172         if( !fb_cfgs ) do {
173                 int fb_attrs[] = {
174                         GLX_CONFIG_CAVEAT,      GLX_SLOW_CONFIG,
175                         GLX_DRAWABLE_TYPE,      GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
176                         GLX_DOUBLEBUFFER,       1,
177                         GLX_RENDER_TYPE,        GLX_RGBA_BIT,
178                         GLX_ACCUM_RED_SIZE,     1,
179                         GLX_ACCUM_GREEN_SIZE,   1,
180                         GLX_ACCUM_BLUE_SIZE,    1,
181                         GLX_ACCUM_ALPHA_SIZE,   1,
182                         GLX_RED_SIZE,           8,
183                         GLX_GREEN_SIZE,         8,
184                         GLX_BLUE_SIZE,          8,
185                         GLX_ALPHA_SIZE,         8,
186                         None
187                 };
188                 fb_cfgs = glXChooseFBConfig(display, scrnum, fb_attrs+2, &ncfgs);
189                 if( fb_cfgs && ncfgs ) break;
190                 fb_cfgs = glXChooseFBConfig(display, scrnum, fb_attrs+0, &ncfgs);
191                 if( fb_cfgs && ncfgs ) break;
192                 fb_attrs[5] = 0;
193                 fb_cfgs = glXChooseFBConfig(display, scrnum, fb_attrs+2, &ncfgs);
194                 if( fb_cfgs && ncfgs ) break;
195                 fb_cfgs = glXChooseFBConfig(display, scrnum, fb_attrs+0, &ncfgs);
196         } while(0);
197         if( fb_cfgs && ncfgs ) {
198                 for( int i=0; !vis_info && i<ncfgs; ++i )
199                         vis_info = glXGetVisualFromFBConfig(display, cfg=fb_cfgs[i]);
200         }
201         if( vis_info ) {
202                 vis = vis_info->visual;
203                 depth = vis_info->depth;
204         }
205         else {
206                 printf("%s\n", "BC_DisplayInfo::gl_fb_config failed");
207                 cfg = 0;
208         }
209         GLXContext glx_ctx = !cfg ? 0 :
210                 glXCreateNewContext(display, cfg, GLX_RGBA_TYPE, 0, True);
211         if( glx_ctx && glXMakeContextCurrent(display, None, None, glx_ctx) ) {
212                 const char *shader_version = (const char *)
213                         glGetString(GL_SHADING_LANGUAGE_VERSION);
214                 if( shader_version )
215                         strncpy(gl_shader_version, shader_version, sizeof(gl_shader_version));
216         }
217         glXMakeContextCurrent(display, None, None, 0);
218         if( glx_ctx ) glXDestroyContext(display, glx_ctx);
219         if( fb_cfgs ) XFree(fb_cfgs);
220         if( vis_info ) XFree(vis_info);
221         return 0;
222 }
223 #endif
224
225
226 void BC_DisplayInfo::test_window(int &x_out, int &y_out, int &x_out2, int &y_out2,
227                 int x_in, int y_in)
228 {
229 #ifdef SINGLE_THREAD
230         BC_Display::lock_display("BC_DisplayInfo::test_window");
231 #endif
232         x_out = 0;
233         y_out = 0;
234         int x_out1 = 0;
235         int y_out1 = 0;
236         x_out2 = 0;
237         y_out2 = 0;
238
239         unsigned long mask = CWEventMask | CWWinGravity | CWBackPixel;
240         XSetWindowAttributes attr;
241         attr.event_mask = StructureNotifyMask;
242         attr.win_gravity = SouthEastGravity;
243         attr.background_pixel = BlackPixel(display, scrnum);
244         Window win = XCreateWindow(display, rootwin,
245                         x_in, y_in, TEST_SIZE, TEST_SIZE,
246                         0, depth, InputOutput,
247                         vis, mask, &attr);
248         XSizeHints size_hints;
249         XGetNormalHints(display, win, &size_hints);
250         size_hints.flags = PPosition | PSize;
251         size_hints.x = x_in;
252         size_hints.y = y_in;
253         size_hints.width = TEST_SIZE;
254         size_hints.height = TEST_SIZE;
255         XSetStandardProperties(display, win,
256                 "x", "x", None, 0, 0, &size_hints);
257         XClearWindow(display, win);
258         XMapWindow(display, win);
259         XFlush(display);  XSync(display, 0);  usleep(100000);
260         XEvent event;
261         int state = 0;
262
263         while( state < 3 ) {
264                 XNextEvent(display, &event);
265 //printf("BC_DisplayInfo::test_window 1 event=%d %d\n", event.type, XPending(display));
266                 if( event.xany.window != win ) continue;
267                 if( event.type != ConfigureNotify ) continue;
268                 Window cwin = 0;
269                 int rx = 0, ry = 0, px = 0, py = 0, tx = 0, ty = 0;
270 //printf("BC_DisplayInfo::test_window 1 state=%d x=%d y=%d w=%d h=%d bw=%d sev=%d\n",
271 //  state, event.xconfigure.x, event.xconfigure.y,
272 //  event.xconfigure.width, event.xconfigure.height,
273 //  event.xconfigure.border_width, event.xconfigure.send_event);
274                 get_top_coords(display,win, px,py, tx,ty);
275 //printf("x_in,y_in=%d,%d dx,dy=%d,%d\n", x_in,y_in, x_in-tx,y_in-ty);
276                 switch( state ) {
277                 case 0: // Get creation config
278                         XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
279                         x_out = rx - x_in;
280                         y_out = ry - y_in;
281                         XMoveResizeWindow(display, win, x_in,y_in, TEST_SIZE2,TEST_SIZE2);
282                         XFlush(display);  XSync(display, 0);  usleep(100000);
283                         ++state;
284                         break;
285                 case 1: // Get moveresize resizing
286                         XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
287                         x_out1 = px;
288                         y_out1 = py;
289                         x_in += TEST_DSIZE;  y_in += TEST_DSIZE;
290                         XMoveResizeWindow(display, win, x_in,y_in, TEST_SIZE2,TEST_SIZE2);
291                         XFlush(display);  XSync(display, 0);  usleep(100000);
292                         ++state;
293                         break;
294                 case 2: // Get moveresize move
295                         XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
296                         x_out2 = px - x_out1 - TEST_DSIZE;
297                         y_out2 = py - y_out1 - TEST_DSIZE;
298                         ++state;
299                         break;
300                 }
301         }
302 //printf("\nBC_DisplayInfo::test_window 3 x0,y0=%d,%d, x1,y1=%d,%d, x2,y2=%d,%d\n",
303 //  x_out,y_out, x_out1,y_out1, x_out2,y_out2);
304 //printf("\nx_in,y_in=%d,%d\n", x_in,y_in);
305
306         XDestroyWindow(display, win);
307         XFlush(display);
308         XSync(display, 0);
309
310         x_out = MAX(0, MIN(x_out, 48));
311         y_out = MAX(0, MIN(y_out, 48));
312
313 #ifdef HAVE_GL
314         gl_probe();
315 #endif
316 #ifdef SINGLE_THREAD
317         BC_Display::unlock_display();
318 #endif
319 }
320
321 void BC_DisplayInfo::init_borders()
322 {
323         if(top_border < 0)
324         {
325                 BC_DisplayInfo display_info;
326                 display_info.test_window(left_border, top_border,
327                         auto_reposition_x, auto_reposition_y, 100, 100);
328                 right_border = left_border;
329                 bottom_border = left_border;
330 //printf("BC_DisplayInfo::init_borders border=%d %d auto=%d %d\n",
331 //  left_border, top_border, auto_reposition_x, auto_reposition_y);
332         }
333 }
334
335
336 int BC_DisplayInfo::get_top_border()
337 {
338         init_borders();
339         return top_border;
340 }
341
342 int BC_DisplayInfo::get_left_border()
343 {
344         init_borders();
345         return left_border;
346 }
347
348 int BC_DisplayInfo::get_right_border()
349 {
350         init_borders();
351         return right_border;
352 }
353
354 int BC_DisplayInfo::get_bottom_border()
355 {
356         init_borders();
357         return bottom_border;
358 }
359
360 const char *BC_DisplayInfo::get_gl_shader_version()
361 {
362         init_borders();
363         return gl_shader_version;
364 }
365
366 void BC_DisplayInfo::init_window(const char *display_name, int show_error)
367 {
368         if(display_name && display_name[0] == 0) display_name = NULL;
369
370 #ifdef SINGLE_THREAD
371         display = BC_Display::get_display(display_name);
372 #else
373
374 // This function must be the first Xlib
375 // function a multi-threaded program calls
376         XInitThreads();
377
378         if((display = XOpenDisplay(display_name)) == NULL)
379         {
380                 if(!show_error) return;
381                 fprintf(stderr,_("BC_DisplayInfo::init_window: cannot open display \"%s\".\n"),
382                         display_name ? display_name : "");
383                 if(getenv("DISPLAY") == NULL)
384                         fprintf(stderr, _("'DISPLAY' environment variable not set.\n"));
385                 if((display = XOpenDisplay(0)) == NULL) {
386                         fprintf(stderr,_("BC_DisplayInfo::init_window: cannot connect to X server.\n"));
387                         exit(1);
388                 }
389         }
390 #endif // SINGLE_THREAD
391
392 #ifdef SINGLE_THREAD
393         BC_Display::lock_display("BC_DisplayInfo::init_window");
394 #endif
395         scrnum = DefaultScreen(display);
396         rootwin = RootWindow(display, scrnum);
397         vis = DefaultVisual(display, scrnum);
398         depth = DefaultDepth(display, scrnum);
399 #ifdef SINGLE_THREAD
400         BC_Display::unlock_display();
401 #endif // SINGLE_THREAD
402 }
403
404
405 int BC_DisplayInfo::get_root_w()
406 {
407 #ifdef SINGLE_THREAD
408         BC_Display::lock_display("BC_DisplayInfo::get_root_w");
409 #endif
410         Screen *screen_ptr = XDefaultScreenOfDisplay(display);
411         int result = WidthOfScreen(screen_ptr);
412 #ifdef SINGLE_THREAD
413         BC_Display::unlock_display();
414 #endif
415         return result;
416 }
417
418 int BC_DisplayInfo::get_root_h()
419 {
420 #ifdef SINGLE_THREAD
421         BC_Display::lock_display("BC_DisplayInfo::get_root_h");
422 #endif
423         Screen *screen_ptr = XDefaultScreenOfDisplay(display);
424         int result = HeightOfScreen(screen_ptr);
425 #ifdef SINGLE_THREAD
426         BC_Display::unlock_display();
427 #endif
428         return result;
429 }
430
431 int BC_DisplayInfo::get_abs_cursor_x()
432 {
433         int abs_x, abs_y, win_x, win_y;
434         unsigned int temp_mask;
435         Window temp_win;
436
437 #ifdef SINGLE_THREAD
438         BC_Display::lock_display("BC_DisplayInfo::get_abs_cursor_x");
439 #endif
440         XQueryPointer(display, rootwin, &temp_win, &temp_win,
441                         &abs_x, &abs_y, &win_x, &win_y, &temp_mask);
442 #ifdef SINGLE_THREAD
443         BC_Display::unlock_display();
444 #endif
445         return abs_x;
446 }
447
448 int BC_DisplayInfo::get_abs_cursor_y()
449 {
450         int abs_x, abs_y, win_x, win_y;
451         unsigned int temp_mask;
452         Window temp_win;
453
454 #ifdef SINGLE_THREAD
455         BC_Display::lock_display("BC_DisplayInfo::get_abs_cursor_y");
456 #endif
457         XQueryPointer(display, rootwin, &temp_win, &temp_win,
458                         &abs_x, &abs_y, &win_x, &win_y, &temp_mask);
459 #ifdef SINGLE_THREAD
460         BC_Display::unlock_display();
461 #endif
462         return abs_y;
463 }
464
465
466 int BC_DisplayInfo::get_screen_count()
467 {
468         return XScreenCount(display);
469 }
470
471
472 const char *BC_DisplayInfo::host_display_name(const char *display_name)
473 {
474         return XDisplayName(display_name);
475 }
476