4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "bcdisplay.h"
23 #include "bcdisplayinfo.h"
24 #include "bcsignals.h"
25 #include "bcwindowbase.h"
31 #include <X11/Xutil.h>
36 #define TEST_SIZE2 164
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, };
47 BC_DisplayInfo::BC_DisplayInfo(const char *display_name, int show_error)
52 xinerama_screens = -1;
54 init_window(display_name, show_error);
63 BC_DisplayInfo::~BC_DisplayInfo()
65 if( xinerama_info ) XFree(xinerama_info);
67 XCloseDisplay(display);
72 void BC_DisplayInfo::parse_geometry(char *geom, int *x, int *y, int *width, int *height)
74 XParseGeometry(geom, x, y, (unsigned int*)width, (unsigned int*)height);
78 int BC_DisplayInfo::get_xinerama_screens()
80 if( xinerama_screens < 0 ) {
82 if( XineramaIsActive(display) )
83 xinerama_info = XineramaQueryScreens(display, &xinerama_screens);
85 return xinerama_screens;
88 int BC_DisplayInfo::xinerama_geometry(int screen, int &x, int &y, int &w, int &h)
90 int screens = get_xinerama_screens();
91 if( !screens ) return 1;
94 while( --k >= 0 && xinerama_info[k].screen_number != screen );
96 x = xinerama_info[k].x_org; w = xinerama_info[k].width;
97 y = xinerama_info[k].y_org; h = xinerama_info[k].height;
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;
112 x = sx0; w = sx1 - sx0;
113 y = sy0; h = sy1 - sy0;
118 int BC_DisplayInfo::xinerama_big_screen()
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;
126 ret = xinerama_info[k].screen_number;
133 static void get_top_coords(Display *display, Window win, int &px,int &py, int &tx,int &ty)
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);
142 int nx = px, ny = py; pwin = win;
143 for( int i=5; --i>=0; ) {
144 win = pwin; pwin = 0; pcwin = 0; ncwin = 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);
158 int BC_DisplayInfo::gl_fb_config()
161 // find prefered config via glxinfo: mesa_hack
162 static int attribs[] = {
169 GLX_ACCUM_RED_SIZE, 1,
170 GLX_ACCUM_GREEN_SIZE, 1,
171 GLX_ACCUM_BLUE_SIZE, 1,
172 GLX_ACCUM_ALPHA_SIZE, 1,
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;
187 if( fb_cfgs ) { XFree(fb_cfgs); fb_cfgs = 0; }
188 if( vis_info ) { XFree(vis_info); vis_info = 0; }
193 GLX_CONFIG_CAVEAT, GLX_SLOW_CONFIG,
194 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT | GLX_PBUFFER_BIT | GLX_PIXMAP_BIT,
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,
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;
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);
216 if( fb_cfgs && ncfgs ) {
217 for( int i=0; !vis_info && i<ncfgs; ++i )
218 vis_info = glXGetVisualFromFBConfig(display, cfg=fb_cfgs[i]);
221 vis = vis_info->visual;
222 depth = vis_info->depth;
225 printf("%s\n", "BC_DisplayInfo::gl_fb_config failed");
231 int BC_DisplayInfo::gl_probe(Window win)
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);
242 strncpy(gl_shader_version, shader_version, sizeof(gl_shader_version));
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);
256 void BC_DisplayInfo::test_window(int &x_out, int &y_out, int &x_out2, int &y_out2,
260 BC_Display::lock_display("BC_DisplayInfo::test_window");
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,
282 XSizeHints size_hints;
283 XGetNormalHints(display, win, &size_hints);
284 size_hints.flags = PPosition | PSize;
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);
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;
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);
311 case 0: // Get creation config
312 XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
315 XMoveResizeWindow(display, win, x_in,y_in, TEST_SIZE2,TEST_SIZE2);
316 XFlush(display); XSync(display, 0); usleep(100000);
319 case 1: // Get moveresize resizing
320 XTranslateCoordinates(display, win, rootwin, 0,0, &rx,&ry, &cwin);
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);
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;
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);
343 XDestroyWindow(display, win);
347 x_out = MAX(0, MIN(x_out, 48));
348 y_out = MAX(0, MIN(y_out, 48));
351 BC_Display::unlock_display();
355 void BC_DisplayInfo::init_borders()
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);
370 int BC_DisplayInfo::get_top_border()
376 int BC_DisplayInfo::get_left_border()
382 int BC_DisplayInfo::get_right_border()
388 int BC_DisplayInfo::get_bottom_border()
391 return bottom_border;
394 int BC_DisplayInfo::get_gl_max_texture_size()
397 return gl_max_texture_size;
400 const char *BC_DisplayInfo::get_gl_shader_version()
403 return gl_shader_version;
406 void BC_DisplayInfo::init_window(const char *display_name, int show_error)
408 if(display_name && display_name[0] == 0) display_name = NULL;
411 display = BC_Display::get_display(display_name);
414 // This function must be the first Xlib
415 // function a multi-threaded program calls
418 if((display = XOpenDisplay(display_name)) == NULL)
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"));
430 #endif // SINGLE_THREAD
433 BC_Display::lock_display("BC_DisplayInfo::init_window");
435 scrnum = DefaultScreen(display);
436 rootwin = RootWindow(display, scrnum);
437 vis = DefaultVisual(display, scrnum);
438 depth = DefaultDepth(display, scrnum);
440 BC_Display::unlock_display();
441 #endif // SINGLE_THREAD
445 int BC_DisplayInfo::get_root_w()
448 BC_Display::lock_display("BC_DisplayInfo::get_root_w");
450 Screen *screen_ptr = XDefaultScreenOfDisplay(display);
451 int result = WidthOfScreen(screen_ptr);
453 BC_Display::unlock_display();
458 int BC_DisplayInfo::get_root_h()
461 BC_Display::lock_display("BC_DisplayInfo::get_root_h");
463 Screen *screen_ptr = XDefaultScreenOfDisplay(display);
464 int result = HeightOfScreen(screen_ptr);
466 BC_Display::unlock_display();
471 int BC_DisplayInfo::get_abs_cursor_x()
473 int abs_x, abs_y, win_x, win_y;
474 unsigned int temp_mask;
478 BC_Display::lock_display("BC_DisplayInfo::get_abs_cursor_x");
480 XQueryPointer(display, rootwin, &temp_win, &temp_win,
481 &abs_x, &abs_y, &win_x, &win_y, &temp_mask);
483 BC_Display::unlock_display();
488 int BC_DisplayInfo::get_abs_cursor_y()
490 int abs_x, abs_y, win_x, win_y;
491 unsigned int temp_mask;
495 BC_Display::lock_display("BC_DisplayInfo::get_abs_cursor_y");
497 XQueryPointer(display, rootwin, &temp_win, &temp_win,
498 &abs_x, &abs_y, &win_x, &win_y, &temp_mask);
500 BC_Display::unlock_display();
506 int BC_DisplayInfo::get_screen_count()
508 return XScreenCount(display);
512 const char *BC_DisplayInfo::host_display_name(const char *display_name)
514 return XDisplayName(display_name);