Example #1
0
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");
}
Example #2
0
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;
}
Example #3
0
/* 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;
}
Example #4
0
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);
}
Example #5
0
/* 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);
   }
}