static void xdpy_destroy_display(ALLEGRO_DISPLAY *d) { ALLEGRO_SYSTEM_XGLX *s = (void *)al_get_system_driver(); ALLEGRO_DISPLAY_XGLX *glx = (void *)d; ALLEGRO_OGL_EXTRAS *ogl = d->ogl_extras; ALLEGRO_DEBUG("destroy display.\n"); /* If we're the last display, convert all bitmpas to display independent * (memory) bitmaps. */ if (s->system.displays._size == 1) { while (d->bitmaps._size > 0) { ALLEGRO_BITMAP **bptr = _al_vector_ref_back(&d->bitmaps); ALLEGRO_BITMAP *b = *bptr; _al_convert_to_memory_bitmap(b); } } else { /* Pass all bitmaps to any other living display. (We assume all displays * are compatible.) */ size_t i; ALLEGRO_DISPLAY **living = NULL; ASSERT(s->system.displays._size > 1); for (i = 0; i < s->system.displays._size; i++) { living = _al_vector_ref(&s->system.displays, i); if (*living != d) break; } for (i = 0; i < d->bitmaps._size; i++) { ALLEGRO_BITMAP **add = _al_vector_alloc_back(&(*living)->bitmaps); ALLEGRO_BITMAP **ref = _al_vector_ref(&d->bitmaps, i); *add = *ref; (*add)->display = *living; } } _al_xglx_unuse_adapter(s, glx->adapter); _al_ogl_unmanage_extensions(d); ALLEGRO_DEBUG("unmanaged extensions.\n"); _al_mutex_lock(&s->lock); _al_vector_find_and_delete(&s->system.displays, &d); XDestroyWindow(s->x11display, glx->window); if (s->mouse_grab_display == d) { s->mouse_grab_display = NULL; } ALLEGRO_DEBUG("destroy window.\n"); if (d->flags & ALLEGRO_FULLSCREEN) { size_t i; ALLEGRO_DISPLAY **living = NULL; bool last_fullscreen = true; /* If any other fullscreen display is still active on the same adapter, * we must not touch the video mode. */ for (i = 0; i < s->system.displays._size; i++) { living = _al_vector_ref(&s->system.displays, i); ALLEGRO_DISPLAY_XGLX *living_glx = (void*)*living; if (*living == d) continue; /* check for fullscreen displays on the same adapter */ if (living_glx->adapter == glx->adapter && al_get_display_flags(*living) & ALLEGRO_FULLSCREEN) { last_fullscreen = false; } } if (last_fullscreen) { ALLEGRO_DEBUG("restore modes.\n"); _al_xglx_restore_video_mode(s, glx->adapter); } else { ALLEGRO_DEBUG("*not* restoring modes.\n"); } } if (ogl->backbuffer) { _al_ogl_destroy_backbuffer(ogl->backbuffer); ogl->backbuffer = NULL; ALLEGRO_DEBUG("destroy backbuffer.\n"); } if (glx->context) { glXDestroyContext(s->gfxdisplay, glx->context); glx->context = NULL; ALLEGRO_DEBUG("destroy context.\n"); } /* XXX quick pre-release hack */ /* In multi-window programs these result in a double-free bugs. */ #if 0 if (glx->fbc) { al_free(glx->fbc); glx->fbc = NULL; XFree(glx->xvinfo); glx->xvinfo = NULL; } else if (glx->xvinfo) { al_free(glx->xvinfo); glx->xvinfo = NULL; } #endif _al_cond_destroy(&glx->mapped); _al_vector_free(&d->bitmaps); _al_event_source_free(&d->es); al_free(d->ogl_extras); al_free(d->vertex_cache); al_free(d); _al_mutex_unlock(&s->lock); ALLEGRO_DEBUG("destroy display finished.\n"); }
static bool xdpy_resize_display(ALLEGRO_DISPLAY *d, int w, int h) { ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver(); ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d; XWindowAttributes xwa; int attempts; bool ret = false; /* A fullscreen-window can't be resized. */ if (d->flags & ALLEGRO_FULLSCREEN_WINDOW) return false; _al_mutex_lock(&system->lock); /* It seems some X servers will treat the resize as a no-op if the window is * already the right size, so check for it to avoid a deadlock later. */ XGetWindowAttributes(system->x11display, glx->window, &xwa); if (xwa.width == w && xwa.height == h) { _al_mutex_unlock(&system->lock); return false; } if (d->flags & ALLEGRO_FULLSCREEN) { _al_xwin_set_fullscreen_window(d, 0); if (!_al_xglx_fullscreen_set_mode(system, glx, w, h, 0, 0)) { ret = false; goto skip_resize; } attempts = 3; } else { attempts = 1; } /* Hack: try multiple times to resize the window, with delays. KDE reacts * slowly to the video mode change, and won't resize our window until a * while after. It would be better to wait for some sort of event rather * than just waiting some amount of time, but I didn't manage to find that * event. --pw */ for (; attempts >= 0; attempts--) { const int old_resize_count = glx->resize_count; ALLEGRO_DEBUG("calling XResizeWindow, attempts=%d\n", attempts); _al_xwin_reset_size_hints(d); glx->programmatic_resize = true; XResizeWindow(system->x11display, glx->window, w, h); _al_display_xglx_await_resize(d, old_resize_count, (d->flags & ALLEGRO_FULLSCREEN)); glx->programmatic_resize = false; _al_xwin_set_size_hints(d, INT_MAX, INT_MAX); if (d->w == w && d->h == h) { ret = true; break; } /* Wait before trying again. */ al_rest(0.333); } if (attempts == 0) { ALLEGRO_ERROR("XResizeWindow didn't work; giving up\n"); } skip_resize: if (d->flags & ALLEGRO_FULLSCREEN) { _al_xwin_set_fullscreen_window(d, 1); _al_xglx_set_above(d, 1); _al_xglx_fullscreen_to_display(system, glx); ALLEGRO_DEBUG("xdpy: resize fullscreen?\n"); } _al_mutex_unlock(&system->lock); return ret; }
/* Create a new X11 display, which maps directly to a GLX window. */ static ALLEGRO_DISPLAY *xdpy_create_display(int w, int h) { ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver(); int adapter = al_get_new_display_adapter(); if (system->x11display == NULL) { ALLEGRO_WARN("Not connected to X server.\n"); return NULL; } if (w <= 0 || h <= 0) { ALLEGRO_ERROR("Invalid window size %dx%d\n", w, h); return NULL; } _al_mutex_lock(&system->lock); ALLEGRO_DISPLAY_XGLX *d = al_calloc(1, sizeof *d); ALLEGRO_DISPLAY *display = (void*)d; ALLEGRO_OGL_EXTRAS *ogl = al_calloc(1, sizeof *ogl); display->ogl_extras = ogl; int major, minor; glXQueryVersion(system->x11display, &major, &minor); d->glx_version = major * 100 + minor * 10; ALLEGRO_INFO("GLX %.1f.\n", d->glx_version / 100.f); display->w = w; display->h = h; display->vt = _al_display_xglx_driver(); display->refresh_rate = al_get_new_display_refresh_rate(); display->flags = al_get_new_display_flags(); // FIXME: default? Is this the right place to set this? display->flags |= ALLEGRO_OPENGL; // store our initial virtual adapter, used by fullscreen and positioning code d->adapter = adapter; ALLEGRO_DEBUG("selected adapter %i\n", adapter); if (d->adapter < 0) { d->adapter = _al_xglx_get_default_adapter(system); } _al_xglx_use_adapter(system, d->adapter); /* if we're in multi-head X mode, bail if we try to use more than one display * as there are bugs in X/glX that cause us to hang in X if we try to use more than one. */ /* if we're in real xinerama mode, also bail, x makes mouse use evil */ bool true_xinerama_active = false; #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA bool xrandr_active = false; #ifdef ALLEGRO_XWINDOWS_WITH_XRANDR xrandr_active = system->xrandr_available; #endif true_xinerama_active = !xrandr_active && system->xinerama_available; #endif if ((true_xinerama_active || ScreenCount(system->x11display) > 1) && system->adapter_use_count) { uint32_t i, adapter_use_count = 0; for (i = 0; i < 32; i++) { if (system->adapter_map[i]) adapter_use_count++; } if (adapter_use_count > 1) { ALLEGRO_ERROR("Use of more than one adapter at once in multi-head X or X with true Xinerama active is not possible.\n"); al_free(d); al_free(ogl); _al_mutex_unlock(&system->lock); return NULL; } } ALLEGRO_DEBUG("xdpy: selected adapter %i\n", d->adapter); // store or initial X Screen, used by window creation, fullscreen, and glx visual code d->xscreen = _al_xglx_get_xscreen(system, d->adapter); ALLEGRO_DEBUG("xdpy: selected xscreen %i\n", d->xscreen); d->is_mapped = false; _al_cond_init(&d->mapped); d->resize_count = 0; d->programmatic_resize = false; _al_xglx_config_select_visual(d); if (!d->xvinfo) { ALLEGRO_ERROR("FIXME: Need better visual selection.\n"); ALLEGRO_ERROR("No matching visual found.\n"); al_free(d); al_free(ogl); _al_mutex_unlock(&system->lock); return NULL; } ALLEGRO_INFO("Selected X11 visual %lu.\n", d->xvinfo->visualid); /* Add ourself to the list of displays. */ ALLEGRO_DISPLAY_XGLX **add; add = _al_vector_alloc_back(&system->system.displays); *add = d; /* Each display is an event source. */ _al_event_source_init(&display->es); /* Create a colormap. */ Colormap cmap = XCreateColormap(system->x11display, RootWindow(system->x11display, d->xvinfo->screen), d->xvinfo->visual, AllocNone); /* Create an X11 window */ XSetWindowAttributes swa; int mask = CWBorderPixel | CWColormap | CWEventMask; swa.colormap = cmap; swa.border_pixel = 0; swa.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | EnterWindowMask | LeaveWindowMask | FocusChangeMask | ExposureMask | PropertyChangeMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask; /* For a non-compositing window manager, a black background can look * less broken if the application doesn't react to expose events fast * enough. However in some cases like resizing, the black background * causes horrible flicker. */ if (!(display->flags & ALLEGRO_RESIZABLE)) { mask |= CWBackPixel; swa.background_pixel = BlackPixel(system->x11display, d->xvinfo->screen); } int x_off = INT_MAX, y_off = INT_MAX; if (display->flags & ALLEGRO_FULLSCREEN) { _al_xglx_get_display_offset(system, d->adapter, &x_off, &y_off); } else { /* we want new_display_adapter's offset to add to the new_window_position */ int xscr_x = 0, xscr_y = 0; al_get_new_window_position(&x_off, &y_off); if (adapter >= 0) { /* non default adapter. I'm assuming this means the user wants the window to be placed on the adapter offset by new display pos */ _al_xglx_get_display_offset(system, d->adapter, &xscr_x, &xscr_y); if (x_off != INT_MAX) x_off += xscr_x; if (y_off != INT_MAX) y_off += xscr_y; } } d->window = XCreateWindow(system->x11display, RootWindow(system->x11display, d->xvinfo->screen), x_off != INT_MAX ? x_off : 0, y_off != INT_MAX ? y_off : 0, w, h, 0, d->xvinfo->depth, InputOutput, d->xvinfo->visual, mask, &swa); // Try to set full screen mode if requested, fail if we can't if (display->flags & ALLEGRO_FULLSCREEN) { /* According to the spec, the window manager is supposed to disable * window decorations when _NET_WM_STATE_FULLSCREEN is in effect. * However, some WMs may not be fully compliant, e.g. Fluxbox. */ xdpy_set_frame(display, false); _al_xglx_set_above(display, 1); if (!_al_xglx_fullscreen_set_mode(system, d, w, h, 0, display->refresh_rate)) { ALLEGRO_DEBUG("xdpy: failed to set fullscreen mode.\n"); xdpy_destroy_display(display); _al_mutex_unlock(&system->lock); return NULL; } //XSync(system->x11display, False); } if (display->flags & ALLEGRO_FRAMELESS) { xdpy_set_frame(display, false); } ALLEGRO_DEBUG("X11 window created.\n"); _al_xwin_set_size_hints(display, x_off, y_off); XLockDisplay(system->x11display); d->wm_delete_window_atom = XInternAtom(system->x11display, "WM_DELETE_WINDOW", False); XSetWMProtocols(system->x11display, d->window, &d->wm_delete_window_atom, 1); XMapWindow(system->x11display, d->window); ALLEGRO_DEBUG("X11 window mapped.\n"); XUnlockDisplay(system->x11display); /* Send the pending request to the X server. */ XSync(system->x11display, False); /* To avoid race conditions where some X11 functions fail before the window * is mapped, we wait here until it is mapped. Note that the thread is * locked, so the event could not possibly have been processed yet in the * events thread. So as long as no other map events occur, the condition * should only be signalled when our window gets mapped. */ while (!d->is_mapped) { _al_cond_wait(&d->mapped, &system->lock); } /* We can do this at any time, but if we already have a mapped * window when switching to fullscreen it will use the same * monitor (with the MetaCity version I'm using here right now). */ if ((display->flags & ALLEGRO_FULLSCREEN_WINDOW)) { ALLEGRO_INFO("Toggling fullscreen flag for %d x %d window.\n", display->w, display->h); _al_xwin_reset_size_hints(display); _al_xwin_set_fullscreen_window(display, 2); _al_xwin_set_size_hints(display, INT_MAX, INT_MAX); XWindowAttributes xwa; XGetWindowAttributes(system->x11display, d->window, &xwa); display->w = xwa.width; display->h = xwa.height; ALLEGRO_INFO("Using ALLEGRO_FULLSCREEN_WINDOW of %d x %d\n", display->w, display->h); } if (display->flags & ALLEGRO_FULLSCREEN) { /* kwin wants these here */ /* metacity wants these here too */ /* XXX compiz is quiky, can't seem to find a combination of hints that * make sure we are layerd over panels, and are positioned properly */ //_al_xwin_set_fullscreen_window(display, 1); _al_xglx_set_above(display, 1); _al_xglx_fullscreen_to_display(system, d); /* Grab mouse if we only have one display, ungrab it if we have more than * one. */ if (_al_vector_size(&system->system.displays) == 1) { al_grab_mouse(display); } else if (_al_vector_size(&system->system.displays) > 1) { al_ungrab_mouse(); } } if (!_al_xglx_config_create_context(d)) { xdpy_destroy_display(display); _al_mutex_unlock(&system->lock); return NULL; } /* Make our GLX context current for reading and writing in the current * thread. */ if (d->fbc) { if (!glXMakeContextCurrent(system->gfxdisplay, d->glxwindow, d->glxwindow, d->context)) { ALLEGRO_ERROR("glXMakeContextCurrent failed\n"); } } else { if (!glXMakeCurrent(system->gfxdisplay, d->glxwindow, d->context)) { ALLEGRO_ERROR("glXMakeCurrent failed\n"); } } _al_ogl_manage_extensions(display); _al_ogl_set_extensions(ogl->extension_api); /* Print out OpenGL version info */ ALLEGRO_INFO("OpenGL Version: %s\n", (const char*)glGetString(GL_VERSION)); ALLEGRO_INFO("Vendor: %s\n", (const char*)glGetString(GL_VENDOR)); ALLEGRO_INFO("Renderer: %s\n", (const char*)glGetString(GL_RENDERER)); if (display->ogl_extras->ogl_info.version < _ALLEGRO_OPENGL_VERSION_1_2) { ALLEGRO_EXTRA_DISPLAY_SETTINGS *eds = _al_get_new_display_settings(); if (eds->required & (1<<ALLEGRO_COMPATIBLE_DISPLAY)) { ALLEGRO_ERROR("Allegro requires at least OpenGL version 1.2 to work.\n"); xdpy_destroy_display(display); _al_mutex_unlock(&system->lock); return NULL; } display->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY] = 0; } #if 0 // Apparently, you can get a OpenGL 3.0 context without specifically creating // it with glXCreateContextAttribsARB, and not every OpenGL 3.0 is evil, but we // can't tell the difference at this stage. else if (display->ogl_extras->ogl_info.version > _ALLEGRO_OPENGL_VERSION_2_1) { /* We don't have OpenGL3 a driver. */ display->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY] = 0; } #endif if (display->extra_settings.settings[ALLEGRO_COMPATIBLE_DISPLAY]) _al_ogl_setup_gl(display); /* vsync */ /* Fill in the user setting. */ display->extra_settings.settings[ALLEGRO_VSYNC] = _al_get_new_display_settings()->settings[ALLEGRO_VSYNC]; /* We set the swap interval to 0 if vsync is forced off, and to 1 * if it is forced on. * http://www.opengl.org/registry/specs/SGI/swap_control.txt * If the option is set to 0, we simply use the system default. The * above extension specifies vsync on as default though, so in the * end with GLX we can't force vsync on, just off. */ ALLEGRO_DEBUG("requested vsync=%d.\n", display->extra_settings.settings[ALLEGRO_VSYNC]); if (display->extra_settings.settings[ALLEGRO_VSYNC]) { if (display->ogl_extras->extension_list->ALLEGRO_GLX_SGI_swap_control) { int x = 1; if (display->extra_settings.settings[ALLEGRO_VSYNC] == 2) x = 0; if (glXSwapIntervalSGI(x)) { ALLEGRO_WARN("glXSwapIntervalSGI(%d) failed.\n", x); } } else { ALLEGRO_WARN("no vsync, GLX_SGI_swap_control missing.\n"); /* According to the specification that means it's on, but * the driver might have disabled it. So we do not know. */ display->extra_settings.settings[ALLEGRO_VSYNC] = 0; } } d->invisible_cursor = None; /* Will be created on demand. */ d->current_cursor = None; /* Initially, we use the root cursor. */ d->cursor_hidden = false; d->icon = None; d->icon_mask = None; _al_mutex_unlock(&system->lock); return display; }
static void xfvm_init(ALLEGRO_SYSTEM_XGLX *s) { int event_base = 0; int error_base = 0; /* init xfvm info to defaults */ s->xfvm_available = 0; s->xfvm_screen_count = 0; s->xfvm_screen = NULL; _al_mutex_lock(&s->lock); if (XF86VidModeQueryExtension(s->x11display, &event_base, &error_base)) { int minor_version = 0, major_version = 0; int status = XF86VidModeQueryVersion(s->x11display, &major_version, &minor_version); ALLEGRO_INFO("XF86VidMode version: %i.%i\n", major_version, minor_version); if (!status) { ALLEGRO_WARN("XF86VidMode not available, XF86VidModeQueryVersion failed.\n"); } else { // I don't actually know what versions are required here, just going to assume any is ok for now. ALLEGRO_INFO("XF86VidMode %i.%i is active\n", major_version, minor_version); s->xfvm_available = 1; } } else { ALLEGRO_WARN("XF86VidMode extension is not available.\n"); } if (s->xfvm_available) { int num_screens; #ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA /* This is some fun stuff right here, if XRANDR is available, we can't use the xinerama screen count * and we really want the xrandr init in xglx_initialize to come last so it overrides xf86vm if available * and I really don't want to add more XRRQuery* or #ifdefs here. * I don't think XRandR can be disabled once its loaded, * so just seeing if its in the extension list should be fine. */ /* interesting thing to note is if XRandR is available, that means we have TwinView, * and not multi-head Xinerama mode, as True Xinerama disables XRandR on my NVidia. * which means all of those xfvm_screen_count != xinerama_screen_count tests * only apply to TwinView. As this code below sets xfvm_screen_count to xinerama_screen_count * making all those compares fail, and make us fall back to the normal xfvm multi-head code. */ /* second note, if FakeXinerama is disabled on TwinView setups, we will end up using * XRandR, as there is no other way to detect TwinView outside of libNVCtrl */ int ext_op, ext_evt, ext_err; Bool ext_ret = XQueryExtension(s->x11display, "RANDR", &ext_op, &ext_evt, &ext_err); if (s->xinerama_available && ext_ret == False) { num_screens = s->xinerama_screen_count; } else #endif { num_screens = ScreenCount(s->x11display); } ALLEGRO_DEBUG("XF86VidMode Got %d screens.\n", num_screens); s->xfvm_screen_count = num_screens; s->xfvm_screen = al_calloc(num_screens, sizeof(*s->xfvm_screen)); if (!s->xfvm_screen) { ALLEGRO_ERROR("XF86VidMode: failed to allocate screen array.\n"); s->xfvm_available = 0; } else { int i; for (i = 0; i < num_screens; i++) { ALLEGRO_DEBUG("XF86VidMode GetAllModeLines on screen %d.\n", i); if (!XF86VidModeGetAllModeLines(s->x11display, i, &(s->xfvm_screen[i].mode_count), &(s->xfvm_screen[i].modes))) { /* XXX what to do here? */ } } _al_xglx_mmon_interface.get_num_display_modes = xfvm_get_num_modes; _al_xglx_mmon_interface.get_display_mode = xfvm_get_mode; _al_xglx_mmon_interface.set_mode = xfvm_set_mode; _al_xglx_mmon_interface.store_mode = xfvm_store_video_mode; _al_xglx_mmon_interface.restore_mode = xfvm_restore_video_mode; _al_xglx_mmon_interface.get_display_offset = xfvm_get_display_offset; _al_xglx_mmon_interface.get_num_adapters = xfvm_get_num_adapters; _al_xglx_mmon_interface.get_monitor_info = xfvm_get_monitor_info; _al_xglx_mmon_interface.get_default_adapter = xfvm_get_default_adapter; _al_xglx_mmon_interface.get_xscreen = xfvm_get_xscreen; _al_xglx_mmon_interface.post_setup = xfvm_post_setup; } } _al_mutex_unlock(&s->lock); }
/* generic multi-head x */ int _al_xsys_mheadx_get_default_adapter(ALLEGRO_SYSTEM_XGLX *s) { int i; ALLEGRO_DEBUG("mhead get default adapter\n"); if (ScreenCount(s->x11display) == 1) return 0; _al_mutex_lock(&s->lock); Window focus; int revert_to = 0; XWindowAttributes attr; Screen *focus_screen; if (!XGetInputFocus(s->x11display, &focus, &revert_to)) { ALLEGRO_ERROR("XGetInputFocus failed!"); _al_mutex_unlock(&s->lock); return 0; } if (focus == None) { ALLEGRO_ERROR("XGetInputFocus returned None!\n"); _al_mutex_unlock(&s->lock); return 0; } else if (focus == PointerRoot) { ALLEGRO_DEBUG("XGetInputFocus returned PointerRoot.\n"); /* XXX TEST THIS >:( */ Window root, child; int root_x, root_y; int win_x, win_y; unsigned int mask; if (XQueryPointer(s->x11display, focus, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask) == False) { ALLEGRO_ERROR("XQueryPointer failed :("); _al_mutex_unlock(&s->lock); return 0; } focus = root; } else { ALLEGRO_DEBUG("XGetInputFocus returned %i!\n", (int)focus); } XGetWindowAttributes(s->x11display, focus, &attr); focus_screen = attr.screen; int ret = 0; for (i = 0; i < ScreenCount(s->x11display); i++) { if (ScreenOfDisplay(s->x11display, i) == focus_screen) { _al_mutex_unlock(&s->lock); ret = i; break; } } _al_mutex_unlock(&s->lock); return ret; }
static bool xdpy_set_system_mouse_cursor(ALLEGRO_DISPLAY *display, ALLEGRO_SYSTEM_MOUSE_CURSOR cursor_id) { ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display; ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver(); Display *xdisplay = system->x11display; Window xwindow = glx->window; unsigned int cursor_shape; switch (cursor_id) { case ALLEGRO_SYSTEM_MOUSE_CURSOR_DEFAULT: case ALLEGRO_SYSTEM_MOUSE_CURSOR_ARROW: case ALLEGRO_SYSTEM_MOUSE_CURSOR_PROGRESS: cursor_shape = XC_left_ptr; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_BUSY: cursor_shape = XC_watch; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_QUESTION: cursor_shape = XC_question_arrow; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_EDIT: cursor_shape = XC_xterm; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_MOVE: cursor_shape = XC_fleur; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_N: cursor_shape = XC_top_side; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_S: cursor_shape = XC_bottom_side; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_E: cursor_shape = XC_right_side; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_W: cursor_shape = XC_left_side; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NE: cursor_shape = XC_top_right_corner; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_SW: cursor_shape = XC_bottom_left_corner; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_NW: cursor_shape = XC_top_left_corner; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_RESIZE_SE: cursor_shape = XC_bottom_right_corner; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_PRECISION: cursor_shape = XC_crosshair; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_LINK: cursor_shape = XC_hand2; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_ALT_SELECT: cursor_shape = XC_hand1; break; case ALLEGRO_SYSTEM_MOUSE_CURSOR_UNAVAILABLE: cursor_shape = XC_X_cursor; break; default: return false; } _al_mutex_lock(&system->lock); glx->current_cursor = XCreateFontCursor(xdisplay, cursor_shape); /* XXX: leak? */ if (!glx->cursor_hidden) { XDefineCursor(xdisplay, xwindow, glx->current_cursor); } _al_mutex_unlock(&system->lock); return true; }
static void xglx_background_thread(_AL_THREAD *self, void *arg) { ALLEGRO_SYSTEM_XGLX *s = arg; XEvent event; double last_reset_screensaver_time = 0.0; while (!_al_get_thread_should_stop(self)) { /* Note: * Most older X11 implementations are not thread-safe no matter what, so * we simply cannot sit inside a blocking XNextEvent from another thread * if another thread also uses X11 functions. * * The usual use of XNextEvent is to only call it from the main thread. We * could of course do this for A5, just needs some slight adjustments to * the events system (polling for an Allegro event would call a function * of the system driver). * * As an alternative, we can use locking. This however can never fully * work, as for example OpenGL implementations also will access X11, in a * way we cannot know and cannot control (and we can't require users to * only call graphics functions inside a lock). * * However, most X11 implementations are somewhat thread safe, and do * use locking quite a bit themselves, so locking mostly does work. * * (Yet another alternative might be to use a separate X11 display * connection for graphics output.) * */ _al_mutex_lock(&s->lock); while (XEventsQueued(s->x11display, QueuedAfterFlush)) { XNextEvent(s->x11display, &event); process_x11_event(s, event); } /* The Xlib manual is particularly useless about the XResetScreenSaver() * function. Nevertheless, this does seem to work to inhibit the native * screensaver facility. Probably it won't do anything for other * systems, though. */ if (s->inhibit_screensaver) { double now = al_get_time(); if (now - last_reset_screensaver_time > 10.0) { XResetScreenSaver(s->x11display); last_reset_screensaver_time = now; } } _al_mutex_unlock(&s->lock); /* If no X11 events are there, unlock so other threads can run. We use * a select call to wake up when as soon as anything is available on * the X11 connection - and just for safety also wake up 10 times * a second regardless. */ int x11_fd = ConnectionNumber(s->x11display); fd_set fdset; FD_ZERO(&fdset); FD_SET(x11_fd, &fdset); struct timeval small_time = {0, 100000}; /* 10 times a second */ select(x11_fd + 1, &fdset, NULL, NULL, &small_time); } }