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