50e07bb19f9a0b16f03ec93324422ec641bd0377
[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         screen = -1;
49         xinerama_screens = -1;
50         xinerama_info = 0;
51         init_window(display_name, show_error);
52 }
53
54 BC_DisplayInfo::~BC_DisplayInfo()
55 {
56         if( xinerama_info ) XFree(xinerama_info);
57 #ifndef SINGLE_THREAD
58         XCloseDisplay(display);
59 #endif
60 }
61
62
63 void BC_DisplayInfo::parse_geometry(char *geom, int *x, int *y, int *width, int *height)
64 {
65         XParseGeometry(geom, x, y, (unsigned int*)width, (unsigned int*)height);
66 }
67
68
69 int BC_DisplayInfo::get_xinerama_screens()
70 {
71         if( xinerama_screens < 0 ) {
72                 xinerama_screens = 0;
73                 if( XineramaIsActive(display) )
74                         xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
75         }
76         return xinerama_screens;
77 }
78
79 int BC_DisplayInfo::xinerama_geometry(int screen, int &x, int &y, int &w, int &h)
80 {
81         int screens = get_xinerama_screens();
82         if( !screens ) return 1;
83         if( screen >= 0 ) {
84                 int k = screens;
85                 while( --k >= 0 && xinerama_info[k].screen_number != screen );
86                 if( k < 0 ) return 1;
87                 x = xinerama_info[k].x_org;  w = xinerama_info[k].width;
88                 y = xinerama_info[k].y_org;  h = xinerama_info[k].height;
89         }
90         else {
91                 int sx0 = INT_MAX, sx1 = INT_MIN;
92                 int sy0 = INT_MAX, sy1 = INT_MIN;
93                 for( int i=0; i<screens; ++i ) {
94                         int x0 = xinerama_info[i].x_org;
95                         int x1 = x0 + xinerama_info[i].width;
96                         if( sx0 > x0 ) sx0 = x0;
97                         if( sx1 < x1 ) sx1 = x1;
98                         int y0 = xinerama_info[i].y_org;
99                         int y1 = y0 + xinerama_info[i].height;
100                         if( sy0 > y0 ) sy0 = y0;
101                         if( sy1 < y1 ) sy1 = y1;
102                 }
103                 x = sx0;  w = sx1 - sx0;
104                 y = sy0;  h = sy1 - sy0;
105         }
106         return 0;
107 }
108
109 static void get_top_coords(Display *display, Window win, int &px,int &py, int &tx,int &ty)
110 {
111         Window *pcwin = 0;  unsigned int ncwin = 0;
112         Window cwin = 0, pwin = 0, root = 0;
113         XQueryTree(display, win, &root, &pwin, &pcwin, &ncwin);
114         if( pcwin ) XFree(pcwin);
115         XTranslateCoordinates(display, pwin, root, 0,0, &px,&py, &cwin);
116 //printf(" win=%lx, px/py=%d/%d\n", win, px,py);
117
118         int nx = px, ny = py;  pwin = win;
119         for( int i=5; --i>=0; ) {
120                 win = pwin;  pwin = 0;  pcwin = 0;  ncwin = 0;
121                 Window rwin = 0;
122 // XQuerytTree has been known to fail here
123                 XQueryTree(display, win, &rwin, &pwin, &pcwin, &ncwin);
124                 if( pcwin ) XFree(pcwin);
125                 if( !rwin || rwin != root || pwin == root ) break;
126                 XTranslateCoordinates(display, pwin, root, 0,0, &nx,&ny, &cwin);
127 //printf(" win=%lx, nx/ny=%d/%d\n", win, nx,ny);
128         }
129         tx = nx;  ty = ny;
130 }
131
132
133 int BC_DisplayInfo::gl_probe(Display *dpy, Window win)
134 {
135 #ifdef HAVE_GL
136         int fbAttribSingle[] = {
137                 GLX_RENDER_TYPE,   GLX_RGBA_BIT,
138                 GLX_RED_SIZE,      1,
139                 GLX_GREEN_SIZE,    1,
140                 GLX_BLUE_SIZE,     1,
141                 GLX_DOUBLEBUFFER,  False,
142                 None };
143         int fbAttribDouble[] = {
144                 GLX_RENDER_TYPE,   GLX_RGBA_BIT,
145                 GLX_RED_SIZE,      1,
146                 GLX_GREEN_SIZE,    1,
147                 GLX_BLUE_SIZE,     1,
148                 GLX_DOUBLEBUFFER,  True,
149                 None };
150         int scrnum = DefaultScreen(dpy);
151         int n_fb_cfgs = 0;
152         GLXFBConfig *fb_cfgs = glXChooseFBConfig(dpy, scrnum, fbAttribSingle, &n_fb_cfgs);
153         if( !fb_cfgs )
154                 fb_cfgs = glXChooseFBConfig(dpy, scrnum, fbAttribDouble, &n_fb_cfgs);
155         if( !fb_cfgs )
156                 return 1;
157         XVisualInfo *vis_info = 0;
158         GLXFBConfig glx_fb_config = 0;
159         for( int i=0; !vis_info && i<n_fb_cfgs; ++i ) {
160                 if( vis_info ) { XFree(vis_info);  vis_info = 0; }
161                 glx_fb_config = fb_cfgs[i];
162                 vis_info = glXGetVisualFromFBConfig(dpy, glx_fb_config);
163         }
164         if( !vis_info )
165                 return 1;
166         XFree(vis_info);
167         GLXWindow glx_win = glXCreateWindow(dpy, glx_fb_config, win, 0);
168         if( !glx_win ) return 1;
169         GLXContext glx_ctxt = glXCreateNewContext(dpy, glx_fb_config, GLX_RGBA_TYPE, 0, True);
170         if( glx_ctxt ) {
171                 if( glXMakeContextCurrent(dpy, glx_win, glx_win, glx_ctxt) ) {
172                         const char *shader_version = (const char *)
173                                 glGetString(GL_SHADING_LANGUAGE_VERSION);
174                         if( shader_version )
175                                 strncpy(gl_shader_version, shader_version, sizeof(gl_shader_version));
176                         glXMakeContextCurrent(dpy, 0, 0, 0);
177                 }
178                 glXDestroyContext(dpy, glx_ctxt);
179         }
180         glXDestroyWindow(dpy, glx_win);
181         return 0;
182 #else
183         return 1;
184 #endif
185 }
186
187
188 void BC_DisplayInfo::test_window(int &x_out, int &y_out, int &x_out2, int &y_out2,
189                 int x_in, int y_in)
190 {
191 #ifdef SINGLE_THREAD
192         BC_Display::lock_display("BC_DisplayInfo::test_window");
193 #endif
194
195         x_out = 0;
196         y_out = 0;
197         int x_out1 = 0;
198         int y_out1 = 0;
199         x_out2 = 0;
200         y_out2 = 0;
201
202         unsigned long mask = CWEventMask | CWWinGravity | CWBackPixel;
203         XSetWindowAttributes attr;
204         attr.event_mask = StructureNotifyMask;
205         attr.win_gravity = SouthEastGravity;
206         attr.background_pixel = BlackPixel(display,screen);
207         Window win = XCreateWindow(display, rootwin,
208                         x_in, y_in, TEST_SIZE, TEST_SIZE,
209                         0, default_depth, InputOutput,
210                         vis, mask, &attr);
211         gl_probe(display, win);
212         XSizeHints size_hints;
213         XGetNormalHints(display, win, &size_hints);
214         size_hints.flags = PPosition | PSize;
215         size_hints.x = x_in;
216         size_hints.y = y_in;
217         size_hints.width = TEST_SIZE;
218         size_hints.height = TEST_SIZE;
219         XSetStandardProperties(display, win,
220                 "x", "x", None, 0, 0, &size_hints);
221         XClearWindow(display, win);
222         XMapWindow(display, win);
223         XFlush(display);  XSync(display, 0);  usleep(100000);
224
225         XEvent event;
226         int state = 0;
227
228         while( state < 3 ) {
229                 XNextEvent(display, &event);
230 //printf("BC_DisplayInfo::test_window 1 event=%d %d\n", event.type, XPending(display));
231                 if( event.xany.window != win ) continue;
232                 if( event.type != ConfigureNotify ) continue;
233                 Window cwin = 0;
234                 int rx = 0, ry = 0, px = 0, py = 0, tx = 0, ty = 0;
235 //printf("BC_DisplayInfo::test_window 1 state=%d x=%d y=%d w=%d h=%d bw=%d sev=%d\n",
236 //  state, event.xconfigure.x, event.xconfigure.y,
237 //  event.xconfigure.width, event.xconfigure.height,
238 //  event.xconfigure.border_width, event.xconfigure.send_event);
239                 get_top_coords(display,win, px,py, tx,ty);
240 //printf("x_in,y_in=%d,%d dx,dy=%d,%d\n", x_in,y_in, x_in-tx,y_in-ty);
241                 switch( state ) {
242                 case 0: // Get creation config
243                         XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
244                         x_out = rx - x_in;
245                         y_out = ry - y_in;
246                         XMoveResizeWindow(display, win, x_in,y_in, TEST_SIZE2,TEST_SIZE2);
247                         XFlush(display);  XSync(display, 0);  usleep(100000);
248                         ++state;
249                         break;
250                 case 1: // Get moveresize resizing
251                         XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
252                         x_out1 = px;
253                         y_out1 = py;
254                         x_in += TEST_DSIZE;  y_in += TEST_DSIZE;
255                         XMoveResizeWindow(display, win, x_in,y_in, TEST_SIZE2,TEST_SIZE2);
256                         XFlush(display);  XSync(display, 0);  usleep(100000);
257                         ++state;
258                         break;
259                 case 2: // Get moveresize move
260                         XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
261                         x_out2 = px - x_out1 - TEST_DSIZE;
262                         y_out2 = py - y_out1 - TEST_DSIZE;
263                         ++state;
264                         break;
265                 }
266         }
267 //printf("\nBC_DisplayInfo::test_window 3 x0,y0=%d,%d, x1,y1=%d,%d, x2,y2=%d,%d\n",
268 //  x_out,y_out, x_out1,y_out1, x_out2,y_out2);
269 //printf("\nx_in,y_in=%d,%d\n", x_in,y_in);
270
271         XDestroyWindow(display, win);
272         XFlush(display);
273         XSync(display, 0);
274
275         x_out = MAX(0, MIN(x_out, 48));
276         y_out = MAX(0, MIN(y_out, 48));
277
278 #ifdef SINGLE_THREAD
279         BC_Display::unlock_display();
280 #endif
281 }
282
283 void BC_DisplayInfo::init_borders()
284 {
285         if(top_border < 0)
286         {
287                 BC_DisplayInfo display_info;
288                 display_info.test_window(left_border, top_border,
289                         auto_reposition_x, auto_reposition_y, 100, 100);
290                 right_border = left_border;
291                 bottom_border = left_border;
292 //printf("BC_DisplayInfo::init_borders border=%d %d auto=%d %d\n",
293 //  left_border, top_border, auto_reposition_x, auto_reposition_y);
294         }
295 }
296
297
298 int BC_DisplayInfo::get_top_border()
299 {
300         init_borders();
301         return top_border;
302 }
303
304 int BC_DisplayInfo::get_left_border()
305 {
306         init_borders();
307         return left_border;
308 }
309
310 int BC_DisplayInfo::get_right_border()
311 {
312         init_borders();
313         return right_border;
314 }
315
316 int BC_DisplayInfo::get_bottom_border()
317 {
318         init_borders();
319         return bottom_border;
320 }
321
322 const char *BC_DisplayInfo::get_gl_shader_version()
323 {
324         init_borders();
325         return gl_shader_version;
326 }
327
328 void BC_DisplayInfo::init_window(const char *display_name, int show_error)
329 {
330         if(display_name && display_name[0] == 0) display_name = NULL;
331
332 #ifdef SINGLE_THREAD
333         display = BC_Display::get_display(display_name);
334 #else
335
336 // This function must be the first Xlib
337 // function a multi-threaded program calls
338         XInitThreads();
339
340         if((display = XOpenDisplay(display_name)) == NULL)
341         {
342                 if(!show_error) return;
343                 fprintf(stderr,_("BC_DisplayInfo::init_window: cannot open display \"%s\".\n"),
344                         display_name ? display_name : "");
345                 if(getenv("DISPLAY") == NULL)
346                         fprintf(stderr, _("'DISPLAY' environment variable not set.\n"));
347                 if((display = XOpenDisplay(0)) == NULL) {
348                         fprintf(stderr,_("BC_DisplayInfo::init_window: cannot connect to X server.\n"));
349                         exit(1);
350                 }
351         }
352 #endif // SINGLE_THREAD
353
354 #ifdef SINGLE_THREAD
355         BC_Display::lock_display("BC_DisplayInfo::init_window");
356 #endif
357         screen = DefaultScreen(display);
358         rootwin = RootWindow(display, screen);
359         vis = DefaultVisual(display, screen);
360         default_depth = DefaultDepth(display, screen);
361 #ifdef SINGLE_THREAD
362         BC_Display::unlock_display();
363 #endif // SINGLE_THREAD
364 }
365
366
367 int BC_DisplayInfo::get_root_w()
368 {
369 #ifdef SINGLE_THREAD
370         BC_Display::lock_display("BC_DisplayInfo::get_root_w");
371 #endif
372         Screen *screen_ptr = XDefaultScreenOfDisplay(display);
373         int result = WidthOfScreen(screen_ptr);
374 #ifdef SINGLE_THREAD
375         BC_Display::unlock_display();
376 #endif
377         return result;
378 }
379
380 int BC_DisplayInfo::get_root_h()
381 {
382 #ifdef SINGLE_THREAD
383         BC_Display::lock_display("BC_DisplayInfo::get_root_h");
384 #endif
385         Screen *screen_ptr = XDefaultScreenOfDisplay(display);
386         int result = HeightOfScreen(screen_ptr);
387 #ifdef SINGLE_THREAD
388         BC_Display::unlock_display();
389 #endif
390         return result;
391 }
392
393 int BC_DisplayInfo::get_abs_cursor_x()
394 {
395         int abs_x, abs_y, win_x, win_y;
396         unsigned int temp_mask;
397         Window temp_win;
398
399 #ifdef SINGLE_THREAD
400         BC_Display::lock_display("BC_DisplayInfo::get_abs_cursor_x");
401 #endif
402         XQueryPointer(display,
403            rootwin,
404            &temp_win,
405            &temp_win,
406        &abs_x,
407            &abs_y,
408            &win_x,
409            &win_y,
410            &temp_mask);
411 #ifdef SINGLE_THREAD
412         BC_Display::unlock_display();
413 #endif
414         return abs_x;
415 }
416
417 int BC_DisplayInfo::get_abs_cursor_y()
418 {
419         int abs_x, abs_y, win_x, win_y;
420         unsigned int temp_mask;
421         Window temp_win;
422
423 #ifdef SINGLE_THREAD
424         BC_Display::lock_display("BC_DisplayInfo::get_abs_cursor_y");
425 #endif
426         XQueryPointer(display,
427            rootwin,
428            &temp_win,
429            &temp_win,
430        &abs_x,
431            &abs_y,
432            &win_x,
433            &win_y,
434            &temp_mask);
435 #ifdef SINGLE_THREAD
436         BC_Display::unlock_display();
437 #endif
438         return abs_y;
439 }
440
441
442 int BC_DisplayInfo::get_screen_count()
443 {
444         return XScreenCount(display);
445 }
446
447
448 const char *BC_DisplayInfo::host_display_name(const char *display_name)
449 {
450         return XDisplayName(display_name);
451 }
452