PageRenderTime 1258ms CodeModel.GetById 181ms app.highlight 785ms RepoModel.GetById 181ms app.codeStats 1ms

/pyglet/window/xlib/__init__.py

https://code.google.com/p/pyglet/
Python | 1309 lines | 979 code | 171 blank | 159 comment | 200 complexity | 90e468b936e8d3fa51fc596265cf1835 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1# ----------------------------------------------------------------------------
   2# pyglet
   3# Copyright (c) 2006-2008 Alex Holkner
   4# All rights reserved.
   5# 
   6# Redistribution and use in source and binary forms, with or without
   7# modification, are permitted provided that the following conditions 
   8# are met:
   9#
  10#  * Redistributions of source code must retain the above copyright
  11#    notice, this list of conditions and the following disclaimer.
  12#  * Redistributions in binary form must reproduce the above copyright 
  13#    notice, this list of conditions and the following disclaimer in
  14#    the documentation and/or other materials provided with the
  15#    distribution.
  16#  * Neither the name of pyglet nor the names of its
  17#    contributors may be used to endorse or promote products
  18#    derived from this software without specific prior written
  19#    permission.
  20#
  21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  28# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  29# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32# POSSIBILITY OF SUCH DAMAGE.
  33# ----------------------------------------------------------------------------
  34
  35__docformat__ = 'restructuredtext'
  36__version__ = '$Id$'
  37
  38from ctypes import *
  39import unicodedata
  40import warnings
  41
  42import pyglet
  43from pyglet.window import WindowException, NoSuchDisplayException, \
  44    MouseCursorException, MouseCursor, \
  45    DefaultMouseCursor, ImageMouseCursor, BaseWindow, _PlatformEventHandler, \
  46    _ViewEventHandler
  47from pyglet.window import key
  48from pyglet.window import mouse
  49from pyglet.event import EventDispatcher
  50
  51from pyglet.canvas.xlib import XlibCanvas
  52
  53from pyglet.libs.x11 import xlib
  54from pyglet.libs.x11 import cursorfont
  55
  56from pyglet.compat import asbytes
  57
  58try:
  59    from pyglet.libs.x11 import xsync
  60    _have_xsync = True
  61except:
  62    _have_xsync = False
  63
  64class mwmhints_t(Structure):
  65    _fields_ = [
  66        ('flags', c_uint32),
  67        ('functions', c_uint32),
  68        ('decorations', c_uint32),
  69        ('input_mode', c_int32),
  70        ('status', c_uint32)
  71    ]
  72
  73# XXX: wraptypes can't parse the header this function is in yet
  74XkbSetDetectableAutoRepeat = xlib._lib.XkbSetDetectableAutoRepeat
  75XkbSetDetectableAutoRepeat.restype = c_int
  76XkbSetDetectableAutoRepeat.argtypes = [POINTER(xlib.Display), c_int, POINTER(c_int)]
  77_can_detect_autorepeat = None
  78
  79XA_CARDINAL = 6 # Xatom.h:14
  80
  81# Do we have the November 2000 UTF8 extension?
  82_have_utf8 = hasattr(xlib._lib, 'Xutf8TextListToTextProperty')
  83
  84# symbol,ctrl -> motion mapping
  85_motion_map = {
  86    (key.UP, False):        key.MOTION_UP,
  87    (key.RIGHT, False):     key.MOTION_RIGHT,
  88    (key.DOWN, False):      key.MOTION_DOWN,
  89    (key.LEFT, False):      key.MOTION_LEFT,
  90    (key.RIGHT, True):      key.MOTION_NEXT_WORD,
  91    (key.LEFT, True):       key.MOTION_PREVIOUS_WORD,
  92    (key.HOME, False):      key.MOTION_BEGINNING_OF_LINE,
  93    (key.END, False):       key.MOTION_END_OF_LINE,
  94    (key.PAGEUP, False):    key.MOTION_PREVIOUS_PAGE,
  95    (key.PAGEDOWN, False):  key.MOTION_NEXT_PAGE,
  96    (key.HOME, True):       key.MOTION_BEGINNING_OF_FILE,
  97    (key.END, True):        key.MOTION_END_OF_FILE,
  98    (key.BACKSPACE, False): key.MOTION_BACKSPACE,
  99    (key.DELETE, False):    key.MOTION_DELETE,
 100}
 101
 102class XlibException(WindowException):
 103    '''An X11-specific exception.  This exception is probably a programming
 104    error in pyglet.'''
 105    pass
 106
 107class XlibMouseCursor(MouseCursor):
 108    drawable = False
 109
 110    def __init__(self, cursor):
 111        self.cursor = cursor
 112
 113# Platform event data is single item, so use platform event handler directly.
 114XlibEventHandler = _PlatformEventHandler
 115ViewEventHandler = _ViewEventHandler
 116
 117class XlibWindow(BaseWindow):
 118    _x_display = None       # X display connection
 119    _x_screen_id = None     # X screen index
 120    _x_ic = None            # X input context
 121    _window = None          # Xlib window handle
 122    _minimum_size = None
 123    _maximum_size = None
 124    _override_redirect = False
 125
 126    _x = 0
 127    _y = 0                  # Last known window position
 128    _width = 0
 129    _height = 0             # Last known window size
 130    _mouse_exclusive_client = None  # x,y of "real" mouse during exclusive
 131    _mouse_buttons = [False] * 6 # State of each xlib button
 132    _keyboard_exclusive = False
 133    _active = True
 134    _applied_mouse_exclusive = False
 135    _applied_keyboard_exclusive = False
 136    _mapped = False
 137    _lost_context = False
 138    _lost_context_state = False
 139
 140    _enable_xsync = False
 141    _current_sync_value = None
 142    _current_sync_valid = False
 143
 144    _needs_resize = False   # True when resize event has been received but not
 145                            # dispatched
 146
 147    _default_event_mask = (0x1ffffff 
 148        & ~xlib.PointerMotionHintMask
 149        & ~xlib.ResizeRedirectMask
 150        & ~xlib.SubstructureNotifyMask)
 151
 152    def __init__(self, *args, **kwargs):
 153        # Bind event handlers
 154        self._event_handlers = {}
 155        self._view_event_handlers = {}
 156        for name in self._platform_event_names:
 157            if not hasattr(self, name):
 158                continue
 159            func = getattr(self, name)
 160            for message in func._platform_event_data:
 161                if hasattr(func, '_view'):
 162                    self._view_event_handlers[message] = func 
 163                else:
 164                    self._event_handlers[message] = func 
 165
 166        super(XlibWindow, self).__init__(*args, **kwargs)
 167
 168        global _can_detect_autorepeat
 169        if _can_detect_autorepeat == None:
 170            supported_rtrn = c_int()
 171            _can_detect_autorepeat = XkbSetDetectableAutoRepeat(self.display._display, c_int(1), byref(supported_rtrn))
 172        if _can_detect_autorepeat:
 173            self.pressed_keys = set()
 174
 175    def _recreate(self, changes):
 176        # If flipping to/from fullscreen, need to recreate the window.  (This
 177        # is the case with both override_redirect method and
 178        # _NET_WM_STATE_FULLSCREEN).
 179        #
 180        # A possible improvement could be to just hide the top window,
 181        # destroy the GLX window, and reshow it again when leaving fullscreen.
 182        # This would prevent the floating window from being moved by the
 183        # WM.
 184        if ('fullscreen' in changes or 'resizable' in changes):
 185            # clear out the GLX context
 186            self.context.detach()
 187            xlib.XDestroyWindow(self._x_display, self._window)
 188            del self.display._window_map[self._window]
 189            del self.display._window_map[self._view]
 190            self._window = None 
 191            self._mapped = False
 192
 193        # TODO: detect state loss only by examining context share.
 194        if 'context' in changes:
 195            self._lost_context = True
 196            self._lost_context_state = True
 197
 198        self._create()
 199
 200    def _create(self):
 201        # Unmap existing window if necessary while we fiddle with it.
 202        if self._window and self._mapped:
 203            self._unmap()
 204
 205        self._x_display = self.display._display
 206        self._x_screen_id = self.display.x_screen
 207
 208
 209        # Create X window if not already existing.
 210        if not self._window:
 211            root = xlib.XRootWindow(self._x_display, self._x_screen_id)
 212
 213            visual_info = self.config.get_visual_info()
 214
 215            visual = visual_info.visual
 216            visual_id = xlib.XVisualIDFromVisual(visual)
 217            default_visual = xlib.XDefaultVisual(
 218                self._x_display, self._x_screen_id)
 219            default_visual_id = xlib.XVisualIDFromVisual(default_visual)
 220            window_attributes = xlib.XSetWindowAttributes()
 221            if visual_id != default_visual_id:
 222                window_attributes.colormap = xlib.XCreateColormap(
 223                    self._x_display, root, visual, xlib.AllocNone)
 224            else:
 225                window_attributes.colormap = xlib.XDefaultColormap(
 226                    self._x_display, self._x_screen_id)
 227            window_attributes.bit_gravity = xlib.StaticGravity
 228
 229            # Issue 287: Compiz on Intel/Mesa doesn't draw window decoration
 230            #            unless CWBackPixel is given in mask.  Should have
 231            #            no effect on other systems, so it's set
 232            #            unconditionally.
 233            mask = xlib.CWColormap | xlib.CWBitGravity | xlib.CWBackPixel
 234
 235            if self._fullscreen:
 236                width, height = self.screen.width, self.screen.height
 237                self._view_x = (width - self._width) // 2
 238                self._view_y = (height - self._height) // 2
 239            else:
 240                width, height = self._width, self._height
 241                self._view_x = self._view_y = 0
 242
 243            self._window = xlib.XCreateWindow(self._x_display, root,
 244                0, 0, width, height, 0, visual_info.depth,
 245                xlib.InputOutput, visual, mask,
 246                byref(window_attributes))
 247            self._view = xlib.XCreateWindow(self._x_display, 
 248                self._window, self._view_x, self._view_y, 
 249                self._width, self._height, 0, visual_info.depth, 
 250                xlib.InputOutput, visual, mask, 
 251                byref(window_attributes));
 252            xlib.XMapWindow(self._x_display, self._view)
 253            xlib.XSelectInput(
 254                self._x_display, self._view, self._default_event_mask)
 255
 256            self.display._window_map[self._window] = \
 257                self.dispatch_platform_event
 258            self.display._window_map[self._view] = \
 259                self.dispatch_platform_event_view
 260
 261            self.canvas = XlibCanvas(self.display, self._view)
 262
 263            self.context.attach(self.canvas)
 264            self.context.set_vsync(self._vsync) # XXX ?
 265
 266            # Setting null background pixmap disables drawing the background,
 267            # preventing flicker while resizing (in theory).
 268            #
 269            # Issue 287: Compiz on Intel/Mesa doesn't draw window decoration if
 270            #            this is called.  As it doesn't seem to have any
 271            #            effect anyway, it's just commented out.
 272            #xlib.XSetWindowBackgroundPixmap(self._x_display, self._window, 0)
 273
 274            self._enable_xsync = (pyglet.options['xsync'] and
 275                                  self.display._enable_xsync and
 276                                  self.config.double_buffer)
 277
 278            # Set supported protocols
 279            protocols = []
 280            protocols.append(xlib.XInternAtom(self._x_display,
 281                                              asbytes('WM_DELETE_WINDOW'), False))
 282            if self._enable_xsync:
 283                protocols.append(xlib.XInternAtom(self._x_display,
 284                                                  asbytes('_NET_WM_SYNC_REQUEST'),
 285                                                  False))
 286            protocols = (c_ulong * len(protocols))(*protocols)
 287            xlib.XSetWMProtocols(self._x_display, self._window,
 288                                 protocols, len(protocols))
 289
 290            # Create window resize sync counter
 291            if self._enable_xsync:
 292                value = xsync.XSyncValue()
 293                self._sync_counter = xlib.XID(
 294                    xsync.XSyncCreateCounter(self._x_display, value))
 295                atom = xlib.XInternAtom(self._x_display,
 296                                        asbytes('_NET_WM_SYNC_REQUEST_COUNTER'), False)
 297                ptr = pointer(self._sync_counter)
 298
 299                xlib.XChangeProperty(self._x_display, self._window,
 300                                     atom, XA_CARDINAL, 32,
 301                                     xlib.PropModeReplace,
 302                                     cast(ptr, POINTER(c_ubyte)), 1)
 303        # Set window attributes
 304        attributes = xlib.XSetWindowAttributes()
 305        attributes_mask = 0
 306
 307        self._override_redirect = False
 308        if self._fullscreen:
 309            if pyglet.options['xlib_fullscreen_override_redirect']:
 310                # Try not to use this any more, it causes problems; disabled
 311                # by default in favour of _NET_WM_STATE_FULLSCREEN.
 312                attributes.override_redirect = self._fullscreen
 313                attributes_mask |= xlib.CWOverrideRedirect
 314                self._override_redirect = True
 315            else:
 316                self._set_wm_state('_NET_WM_STATE_FULLSCREEN')
 317
 318        if self._fullscreen:
 319            xlib.XMoveResizeWindow(self._x_display, self._window, 
 320                self.screen.x, self.screen.y, 
 321                self.screen.width, self.screen.height)
 322        else:
 323            xlib.XResizeWindow(self._x_display, self._window, 
 324                self._width, self._height)
 325
 326        xlib.XChangeWindowAttributes(self._x_display, self._window, 
 327            attributes_mask, byref(attributes))
 328
 329        # Set style
 330        styles = {
 331            self.WINDOW_STYLE_DEFAULT: '_NET_WM_WINDOW_TYPE_NORMAL',
 332            self.WINDOW_STYLE_DIALOG: '_NET_WM_WINDOW_TYPE_DIALOG',
 333            self.WINDOW_STYLE_TOOL: '_NET_WM_WINDOW_TYPE_UTILITY',
 334        }
 335        if self._style in styles:
 336            self._set_atoms_property('_NET_WM_WINDOW_TYPE', 
 337                                     (styles[self._style],))
 338        elif self._style == self.WINDOW_STYLE_BORDERLESS:
 339            MWM_HINTS_DECORATIONS = 1 << 1
 340            PROP_MWM_HINTS_ELEMENTS = 5
 341            mwmhints = mwmhints_t()
 342            mwmhints.flags = MWM_HINTS_DECORATIONS
 343            mwmhints.decorations = 0
 344            name = xlib.XInternAtom(self._x_display, asbytes('_MOTIF_WM_HINTS'), False)
 345            xlib.XChangeProperty(self._x_display, self._window,
 346                name, name, 32, xlib.PropModeReplace, 
 347                cast(pointer(mwmhints), POINTER(c_ubyte)),
 348                PROP_MWM_HINTS_ELEMENTS)
 349
 350        # Set resizeable
 351        if not self._resizable and not self._fullscreen:
 352            self.set_minimum_size(self._width, self._height)
 353            self.set_maximum_size(self._width, self._height)
 354
 355        # Set caption
 356        self.set_caption(self._caption)
 357
 358        # this is supported by some compositors (ie gnome-shell), and more to come
 359        # see: http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#idp6357888
 360        _NET_WM_BYPASS_COMPOSITOR_HINT_ON = c_ulong(int(self._fullscreen))
 361        name = xlib.XInternAtom(self._x_display,
 362                                asbytes('_NET_WM_BYPASS_COMPOSITOR'), False)
 363        ptr = pointer(_NET_WM_BYPASS_COMPOSITOR_HINT_ON)
 364
 365        xlib.XChangeProperty(self._x_display, self._window,
 366                             name, XA_CARDINAL, 32,
 367                             xlib.PropModeReplace,
 368                             cast(ptr, POINTER(c_ubyte)), 1)
 369
 370        # Create input context.  A good but very outdated reference for this
 371        # is http://www.sbin.org/doc/Xlib/chapt_11.html
 372        if _have_utf8 and not self._x_ic:
 373            if not self.display._x_im:
 374                xlib.XSetLocaleModifiers(asbytes('@im=none'))
 375                self.display._x_im = \
 376                    xlib.XOpenIM(self._x_display, None, None, None)
 377
 378            xlib.XFlush(self._x_display);
 379
 380            # Need to set argtypes on this function because it's vararg,
 381            # and ctypes guesses wrong.
 382            xlib.XCreateIC.argtypes = [xlib.XIM,    
 383                                       c_char_p, c_int,
 384                                       c_char_p, xlib.Window,
 385                                       c_char_p, xlib.Window,
 386                                       c_void_p]
 387            self._x_ic = xlib.XCreateIC(self.display._x_im, 
 388                asbytes('inputStyle'), xlib.XIMPreeditNothing|xlib.XIMStatusNothing,
 389                asbytes('clientWindow'), self._window,
 390                asbytes('focusWindow'), self._window,
 391                None)
 392
 393            filter_events = c_ulong()
 394            xlib.XGetICValues(self._x_ic,
 395                              'filterEvents', byref(filter_events),
 396                              None)
 397            self._default_event_mask |= filter_events.value
 398            xlib.XSetICFocus(self._x_ic)
 399
 400        self.switch_to()
 401        if self._visible:
 402            self.set_visible(True)
 403
 404        self.set_mouse_platform_visible()
 405        self._applied_mouse_exclusive = None
 406        self._update_exclusivity()
 407
 408    def _map(self):
 409        if self._mapped:
 410            return
 411
 412        # Map the window, wait for map event before continuing.
 413        xlib.XSelectInput(
 414            self._x_display, self._window, xlib.StructureNotifyMask)
 415        xlib.XMapRaised(self._x_display, self._window)
 416        e = xlib.XEvent()
 417        while True:
 418            xlib.XNextEvent(self._x_display, e)
 419            if e.type == xlib.ConfigureNotify:
 420                self._width = e.xconfigure.width
 421                self._height = e.xconfigure.height
 422            elif e.type == xlib.MapNotify:
 423                break
 424        xlib.XSelectInput(
 425            self._x_display, self._window, self._default_event_mask)
 426        self._mapped = True
 427
 428        if self._override_redirect:
 429            # Possibly an override_redirect issue.
 430            self.activate()
 431        
 432        self._update_view_size()
 433        
 434        self.dispatch_event('on_resize', self._width, self._height)
 435        self.dispatch_event('on_show')
 436        self.dispatch_event('on_expose')
 437
 438    def _unmap(self):
 439        if not self._mapped:
 440            return
 441
 442        xlib.XSelectInput(
 443            self._x_display, self._window, xlib.StructureNotifyMask)
 444        xlib.XUnmapWindow(self._x_display, self._window)
 445        e = xlib.XEvent()
 446        while True:
 447            xlib.XNextEvent(self._x_display, e)
 448            if e.type == xlib.UnmapNotify:
 449                break
 450
 451        xlib.XSelectInput(
 452            self._x_display, self._window, self._default_event_mask)
 453        self._mapped = False
 454
 455    def _get_root(self):
 456        attributes = xlib.XWindowAttributes()
 457        xlib.XGetWindowAttributes(self._x_display, self._window,
 458                                  byref(attributes))
 459        return attributes.root
 460
 461    def _is_reparented(self):
 462        root = c_ulong()
 463        parent = c_ulong()
 464        children = pointer(c_ulong())
 465        n_children = c_uint()
 466        
 467        xlib.XQueryTree(self._x_display, self._window,
 468                        byref(root), byref(parent), byref(children),
 469                        byref(n_children))
 470        
 471        return root.value != parent.value
 472
 473    def close(self):
 474        if not self._window:
 475            return
 476
 477        self.context.destroy()
 478        self._unmap()
 479        if self._window:
 480            xlib.XDestroyWindow(self._x_display, self._window)
 481
 482        del self.display._window_map[self._window]
 483        self._window = None
 484
 485        if _have_utf8:
 486            xlib.XDestroyIC(self._x_ic)
 487            self._x_ic = None
 488
 489        super(XlibWindow, self).close()
 490
 491    def switch_to(self):
 492        if self.context:
 493            self.context.set_current()
 494
 495    def flip(self):
 496        self.draw_mouse_cursor()
 497
 498        # TODO canvas.flip?
 499        if self.context:
 500            self.context.flip()
 501
 502        self._sync_resize()
 503
 504    def set_vsync(self, vsync):
 505        if pyglet.options['vsync'] is not None:
 506            vsync = pyglet.options['vsync']
 507        self._vsync = vsync
 508        self.context.set_vsync(vsync)
 509
 510    def set_caption(self, caption):
 511        if caption is None:
 512            caption = ''
 513        self._caption = caption
 514        self._set_text_property('WM_NAME', caption, allow_utf8=False)
 515        self._set_text_property('WM_ICON_NAME', caption, allow_utf8=False)
 516        self._set_text_property('_NET_WM_NAME', caption)
 517        self._set_text_property('_NET_WM_ICON_NAME', caption)
 518
 519    def get_caption(self):
 520        return self._caption
 521
 522    def set_size(self, width, height):
 523        if self._fullscreen:
 524            raise WindowException('Cannot set size of fullscreen window.')
 525        self._width = width
 526        self._height = height
 527        if not self._resizable:
 528            self.set_minimum_size(width, height)
 529            self.set_maximum_size(width, height)
 530        xlib.XResizeWindow(self._x_display, self._window, width, height)
 531        self._update_view_size()
 532        self.dispatch_event('on_resize', width, height)
 533
 534    def _update_view_size(self):
 535        xlib.XResizeWindow(self._x_display, self._view, 
 536                           self._width, self._height)
 537
 538    def get_size(self):
 539        # XGetGeometry and XWindowAttributes seem to always return the
 540        # original size of the window, which is wrong after the user
 541        # has resized it.
 542        # XXX this is probably fixed now, with fix of resize.
 543        return self._width, self._height
 544
 545    def set_location(self, x, y):
 546        if self._is_reparented():
 547            # Assume the window manager has reparented our top-level window
 548            # only once, in which case attributes.x/y give the offset from
 549            # the frame to the content window.  Better solution would be
 550            # to use _NET_FRAME_EXTENTS, where supported.
 551            attributes = xlib.XWindowAttributes()
 552            xlib.XGetWindowAttributes(self._x_display, self._window,
 553                                      byref(attributes))
 554            # XXX at least under KDE's WM these attrs are both 0
 555            x -= attributes.x
 556            y -= attributes.y
 557        xlib.XMoveWindow(self._x_display, self._window, x, y)
 558
 559    def get_location(self):
 560        child = xlib.Window()
 561        x = c_int()
 562        y = c_int()
 563        xlib.XTranslateCoordinates(self._x_display,
 564                                   self._window,
 565                                   self._get_root(),
 566                                   0, 0,
 567                                   byref(x),
 568                                   byref(y),
 569                                   byref(child))
 570        return x.value, y.value
 571
 572    def activate(self):
 573        xlib.XSetInputFocus(self._x_display, self._window,
 574            xlib.RevertToParent, xlib.CurrentTime)
 575
 576    def set_visible(self, visible=True):
 577        if visible:
 578            self._map()
 579        else:
 580            self._unmap()
 581        self._visible = visible
 582
 583    def set_minimum_size(self, width, height):
 584        self._minimum_size = width, height
 585        self._set_wm_normal_hints()
 586
 587    def set_maximum_size(self, width, height):
 588        self._maximum_size = width, height
 589        self._set_wm_normal_hints()
 590
 591    def minimize(self):
 592        xlib.XIconifyWindow(self._x_display, self._window, self._x_screen_id)
 593
 594    def maximize(self):
 595        self._set_wm_state('_NET_WM_STATE_MAXIMIZED_HORZ',
 596                           '_NET_WM_STATE_MAXIMIZED_VERT')
 597
 598    def set_mouse_platform_visible(self, platform_visible=None):
 599        if not self._window:
 600            return
 601        if platform_visible is None:
 602            platform_visible = self._mouse_visible and \
 603                               not self._mouse_cursor.drawable
 604
 605        if not platform_visible:
 606            # Hide pointer by creating an empty cursor
 607            black = xlib.XBlackPixel(self._x_display, self._x_screen_id)
 608            black = xlib.XColor()
 609            bmp = xlib.XCreateBitmapFromData(self._x_display, self._window,
 610                c_buffer(8), 8, 8)
 611            cursor = xlib.XCreatePixmapCursor(self._x_display, bmp, bmp,
 612                black, black, 0, 0)
 613            xlib.XDefineCursor(self._x_display, self._window, cursor)
 614            xlib.XFreeCursor(self._x_display, cursor)
 615            xlib.XFreePixmap(self._x_display, bmp)
 616        else:
 617            # Restore cursor
 618            if isinstance(self._mouse_cursor, XlibMouseCursor):
 619                xlib.XDefineCursor(self._x_display, self._window, 
 620                                   self._mouse_cursor.cursor)
 621            else:
 622                xlib.XUndefineCursor(self._x_display, self._window)
 623
 624    def set_mouse_position(self, x, y):
 625        xlib.XWarpPointer(self._x_display,
 626            0,              # src window
 627            self._window,   # dst window
 628            0, 0,           # src x, y
 629            0, 0,           # src w, h
 630            x, self._height - y,
 631        )
 632
 633    def _update_exclusivity(self):
 634        mouse_exclusive = self._active and self._mouse_exclusive
 635        keyboard_exclusive = self._active and self._keyboard_exclusive
 636
 637        if mouse_exclusive != self._applied_mouse_exclusive:
 638            if mouse_exclusive:
 639                self.set_mouse_platform_visible(False)
 640
 641                # Restrict to client area
 642                xlib.XGrabPointer(self._x_display, self._window,
 643                    True,
 644                    0,
 645                    xlib.GrabModeAsync,
 646                    xlib.GrabModeAsync,
 647                    self._window,
 648                    0,
 649                    xlib.CurrentTime)
 650
 651                # Move pointer to center of window
 652                x = self._width // 2
 653                y = self._height // 2
 654                self._mouse_exclusive_client = x, y
 655                self.set_mouse_position(x, y)
 656            elif self._fullscreen and not self.screen._xinerama:
 657                # Restrict to fullscreen area (prevent viewport scrolling)
 658                self.set_mouse_position(0, 0)
 659                r = xlib.XGrabPointer(self._x_display, self._view,
 660                    True, 0,
 661                    xlib.GrabModeAsync,
 662                    xlib.GrabModeAsync,
 663                    self._view,
 664                    0,
 665                    xlib.CurrentTime)
 666                if r:
 667                    # Failed to grab, try again later
 668                    self._applied_mouse_exclusive = None
 669                    return
 670                self.set_mouse_platform_visible()
 671            else:
 672                # Unclip
 673                xlib.XUngrabPointer(self._x_display, xlib.CurrentTime)
 674                self.set_mouse_platform_visible()
 675
 676            self._applied_mouse_exclusive = mouse_exclusive
 677
 678        if keyboard_exclusive != self._applied_keyboard_exclusive:
 679            if keyboard_exclusive:
 680                xlib.XGrabKeyboard(self._x_display,
 681                    self._window,
 682                    False,
 683                    xlib.GrabModeAsync,
 684                    xlib.GrabModeAsync,
 685                    xlib.CurrentTime)
 686            else:
 687                xlib.XUngrabKeyboard(self._x_display, xlib.CurrentTime)
 688            self._applied_keyboard_exclusive = keyboard_exclusive
 689
 690    def set_exclusive_mouse(self, exclusive=True):
 691        if exclusive == self._mouse_exclusive:
 692            return
 693
 694        self._mouse_exclusive = exclusive
 695        self._update_exclusivity()
 696
 697    def set_exclusive_keyboard(self, exclusive=True):
 698        if exclusive == self._keyboard_exclusive:
 699            return
 700        
 701        self._keyboard_exclusive = exclusive
 702        self._update_exclusivity()
 703
 704    def get_system_mouse_cursor(self, name):
 705        if name == self.CURSOR_DEFAULT:
 706            return DefaultMouseCursor()
 707
 708        # NQR means default shape is not pretty... surely there is another
 709        # cursor font?
 710        cursor_shapes = {
 711            self.CURSOR_CROSSHAIR:       cursorfont.XC_crosshair,
 712            self.CURSOR_HAND:            cursorfont.XC_hand2,
 713            self.CURSOR_HELP:            cursorfont.XC_question_arrow,  # NQR
 714            self.CURSOR_NO:              cursorfont.XC_pirate,          # NQR
 715            self.CURSOR_SIZE:            cursorfont.XC_fleur,
 716            self.CURSOR_SIZE_UP:         cursorfont.XC_top_side,
 717            self.CURSOR_SIZE_UP_RIGHT:   cursorfont.XC_top_right_corner,
 718            self.CURSOR_SIZE_RIGHT:      cursorfont.XC_right_side,
 719            self.CURSOR_SIZE_DOWN_RIGHT: cursorfont.XC_bottom_right_corner,
 720            self.CURSOR_SIZE_DOWN:       cursorfont.XC_bottom_side,
 721            self.CURSOR_SIZE_DOWN_LEFT:  cursorfont.XC_bottom_left_corner,
 722            self.CURSOR_SIZE_LEFT:       cursorfont.XC_left_side,
 723            self.CURSOR_SIZE_UP_LEFT:    cursorfont.XC_top_left_corner,
 724            self.CURSOR_SIZE_UP_DOWN:    cursorfont.XC_sb_v_double_arrow,
 725            self.CURSOR_SIZE_LEFT_RIGHT: cursorfont.XC_sb_h_double_arrow,
 726            self.CURSOR_TEXT:            cursorfont.XC_xterm,
 727            self.CURSOR_WAIT:            cursorfont.XC_watch,
 728            self.CURSOR_WAIT_ARROW:      cursorfont.XC_watch,           # NQR
 729        }
 730        if name not in cursor_shapes:
 731            raise MouseCursorException('Unknown cursor name "%s"' % name)
 732        cursor = xlib.XCreateFontCursor(self._x_display, cursor_shapes[name])
 733        return XlibMouseCursor(cursor)
 734
 735    def set_icon(self, *images):
 736        # Careful!  XChangeProperty takes an array of long when data type
 737        # is 32-bit (but long can be 64 bit!), so pad high bytes of format if
 738        # necessary.
 739
 740        import sys
 741        format = {
 742            ('little', 4): 'BGRA',
 743            ('little', 8): 'BGRAAAAA',
 744            ('big', 4):    'ARGB',
 745            ('big', 8):    'AAAAARGB'
 746        }[(sys.byteorder, sizeof(c_ulong))]
 747
 748        data = asbytes('')
 749        for image in images:
 750            image = image.get_image_data()
 751            pitch = -(image.width * len(format))
 752            s = c_buffer(sizeof(c_ulong) * 2)
 753            memmove(s, cast((c_ulong * 2)(image.width, image.height), 
 754                            POINTER(c_ubyte)), len(s))
 755            data += s.raw + image.get_data(format, pitch)
 756        buffer = (c_ubyte * len(data))()
 757        memmove(buffer, data, len(data))
 758        atom = xlib.XInternAtom(self._x_display, asbytes('_NET_WM_ICON'), False)
 759        xlib.XChangeProperty(self._x_display, self._window, atom, XA_CARDINAL,
 760            32, xlib.PropModeReplace, buffer, len(data)//sizeof(c_ulong))
 761
 762    # Private utility
 763
 764    def _set_wm_normal_hints(self):
 765        hints = xlib.XAllocSizeHints().contents
 766        if self._minimum_size:
 767            hints.flags |= xlib.PMinSize
 768            hints.min_width, hints.min_height = self._minimum_size
 769        if self._maximum_size:
 770            hints.flags |= xlib.PMaxSize
 771            hints.max_width, hints.max_height = self._maximum_size
 772        xlib.XSetWMNormalHints(self._x_display, self._window, byref(hints))
 773
 774    def _set_text_property(self, name, value, allow_utf8=True):
 775        atom = xlib.XInternAtom(self._x_display, asbytes(name), False)
 776        if not atom:
 777            raise XlibException('Undefined atom "%s"' % name)
 778        assert type(value) in (str, unicode)
 779        property = xlib.XTextProperty()
 780        if _have_utf8 and allow_utf8:
 781            buf = create_string_buffer(value.encode('utf8'))
 782            result = xlib.Xutf8TextListToTextProperty(self._x_display,
 783                cast(pointer(buf), c_char_p), 1, xlib.XUTF8StringStyle, 
 784                byref(property))
 785            if result < 0:
 786                raise XlibException('Could not create UTF8 text property')
 787        else:
 788            buf = create_string_buffer(value.encode('ascii', 'ignore'))
 789            result = xlib.XStringListToTextProperty(
 790                cast(pointer(buf), c_char_p), 1, byref(property))
 791            if result < 0:
 792                raise XlibException('Could not create text property')
 793        xlib.XSetTextProperty(self._x_display,
 794            self._window, byref(property), atom)
 795        # XXX <rj> Xlib doesn't like us freeing this
 796        #xlib.XFree(property.value)
 797
 798    def _set_atoms_property(self, name, values, mode=xlib.PropModeReplace):
 799        name_atom = xlib.XInternAtom(self._x_display, asbytes(name), False)
 800        atoms = []
 801        for value in values:
 802            atoms.append(xlib.XInternAtom(self._x_display, asbytes(value), False))
 803        atom_type = xlib.XInternAtom(self._x_display, asbytes('ATOM'), False)
 804        if len(atoms):
 805            atoms_ar = (xlib.Atom * len(atoms))(*atoms)
 806            xlib.XChangeProperty(self._x_display, self._window,
 807                name_atom, atom_type, 32, mode,
 808                cast(pointer(atoms_ar), POINTER(c_ubyte)), len(atoms))
 809        else:
 810            xlib.XDeleteProperty(self._x_display, self._window, net_wm_state)
 811
 812    def _set_wm_state(self, *states):
 813        # Set property
 814        net_wm_state = xlib.XInternAtom(self._x_display, asbytes('_NET_WM_STATE'), False)
 815        atoms = []
 816        for state in states:
 817            atoms.append(xlib.XInternAtom(self._x_display, asbytes(state), False))
 818        atom_type = xlib.XInternAtom(self._x_display, asbytes('ATOM'), False)
 819        if len(atoms):
 820            atoms_ar = (xlib.Atom * len(atoms))(*atoms)
 821            xlib.XChangeProperty(self._x_display, self._window,
 822                net_wm_state, atom_type, 32, xlib.PropModePrepend,
 823                cast(pointer(atoms_ar), POINTER(c_ubyte)), len(atoms))
 824        else:
 825            xlib.XDeleteProperty(self._x_display, self._window, net_wm_state)
 826
 827        # Nudge the WM
 828        e = xlib.XEvent()
 829        e.xclient.type = xlib.ClientMessage
 830        e.xclient.message_type = net_wm_state
 831        e.xclient.display = cast(self._x_display, POINTER(xlib.Display))
 832        e.xclient.window = self._window
 833        e.xclient.format = 32
 834        e.xclient.data.l[0] = xlib.PropModePrepend
 835        for i, atom in enumerate(atoms):
 836            e.xclient.data.l[i + 1] = atom
 837        xlib.XSendEvent(self._x_display, self._get_root(),
 838            False, xlib.SubstructureRedirectMask, byref(e))
 839
 840    # Event handling
 841
 842    def dispatch_events(self):
 843        self.dispatch_pending_events()
 844
 845        self._allow_dispatch_event = True
 846
 847        e = xlib.XEvent()
 848
 849        # Cache these in case window is closed from an event handler
 850        _x_display = self._x_display
 851        _window = self._window
 852        _view = self._view
 853
 854        # Check for the events specific to this window
 855        while xlib.XCheckWindowEvent(_x_display, _window,
 856                                     0x1ffffff, byref(e)):
 857            # Key events are filtered by the xlib window event
 858            # handler so they get a shot at the prefiltered event.
 859            if e.xany.type not in (xlib.KeyPress, xlib.KeyRelease):
 860                if xlib.XFilterEvent(e, 0):
 861                    continue
 862            self.dispatch_platform_event(e)
 863
 864        # Check for the events specific to this view
 865        while xlib.XCheckWindowEvent(_x_display, _view,
 866                                     0x1ffffff, byref(e)):
 867            # Key events are filtered by the xlib window event
 868            # handler so they get a shot at the prefiltered event.
 869            if e.xany.type not in (xlib.KeyPress, xlib.KeyRelease):
 870                if xlib.XFilterEvent(e, 0):
 871                    continue
 872            self.dispatch_platform_event_view(e)
 873
 874        # Generic events for this window (the window close event).
 875        while xlib.XCheckTypedWindowEvent(_x_display, _window, 
 876                xlib.ClientMessage, byref(e)):
 877            self.dispatch_platform_event(e) 
 878 
 879        if self._needs_resize:
 880            self.dispatch_event('on_resize', self._width, self._height)
 881            self.dispatch_event('on_expose')
 882            self._needs_resize = False
 883
 884        self._allow_dispatch_event = False
 885
 886    def dispatch_pending_events(self):
 887        while self._event_queue:
 888            EventDispatcher.dispatch_event(self, *self._event_queue.pop(0))
 889
 890        # Dispatch any context-related events
 891        if self._lost_context:
 892            self._lost_context = False
 893            EventDispatcher.dispatch_event(self, 'on_context_lost')
 894        if self._lost_context_state:
 895            self._lost_context_state = False
 896            EventDispatcher.dispatch_event(self, 'on_context_state_lost')
 897
 898    def dispatch_platform_event(self, e):
 899        if self._applied_mouse_exclusive is None:
 900            self._update_exclusivity()
 901        event_handler = self._event_handlers.get(e.type)
 902        if event_handler:
 903            event_handler(e)
 904
 905    def dispatch_platform_event_view(self, e):
 906        event_handler = self._view_event_handlers.get(e.type)
 907        if event_handler:
 908            event_handler(e)
 909
 910    @staticmethod
 911    def _translate_modifiers(state):
 912        modifiers = 0
 913        if state & xlib.ShiftMask:
 914            modifiers |= key.MOD_SHIFT
 915        if state & xlib.ControlMask:
 916            modifiers |= key.MOD_CTRL
 917        if state & xlib.LockMask:
 918            modifiers |= key.MOD_CAPSLOCK
 919        if state & xlib.Mod1Mask:
 920            modifiers |= key.MOD_ALT
 921        if state & xlib.Mod2Mask:
 922            modifiers |= key.MOD_NUMLOCK
 923        if state & xlib.Mod4Mask:
 924            modifiers |= key.MOD_WINDOWS
 925        if state & xlib.Mod5Mask:
 926            modifiers |= key.MOD_SCROLLLOCK
 927        return modifiers
 928
 929    # Event handlers
 930    '''
 931    def _event_symbol(self, event):
 932        # pyglet.self.key keysymbols are identical to X11 keysymbols, no
 933        # need to map the keysymbol.
 934        symbol = xlib.XKeycodeToKeysym(self._x_display, event.xkey.keycode, 0)
 935        if symbol == 0:
 936            # XIM event
 937            return None
 938        elif symbol not in key._key_names.keys():
 939            symbol = key.user_key(event.xkey.keycode)
 940        return symbol
 941    '''
 942
 943    def _event_text_symbol(self, ev):
 944        text = None
 945        symbol = xlib.KeySym()
 946        buffer = create_string_buffer(128)
 947
 948        # Look up raw keysym before XIM filters it (default for keypress and
 949        # keyrelease)
 950        count = xlib.XLookupString(ev.xkey,
 951                                   buffer, len(buffer) - 1,
 952                                   byref(symbol), None)
 953
 954        # Give XIM a shot
 955        filtered = xlib.XFilterEvent(ev, ev.xany.window)
 956
 957        if ev.type == xlib.KeyPress and not filtered:
 958            status = c_int()
 959            if _have_utf8:
 960                encoding = 'utf8'
 961                count = xlib.Xutf8LookupString(self._x_ic,
 962                                               ev.xkey,
 963                                               buffer, len(buffer) - 1,
 964                                               byref(symbol), byref(status))
 965                if status.value == xlib.XBufferOverflow:
 966                    raise NotImplementedError('TODO: XIM buffer resize')
 967
 968            else:
 969                encoding = 'ascii'
 970                count = xlib.XLookupString(ev.xkey,
 971                                           buffer, len(buffer) - 1,
 972                                           byref(symbol), None)
 973                if count:
 974                    status.value = xlib.XLookupBoth
 975
 976            if status.value & (xlib.XLookupChars | xlib.XLookupBoth):
 977                text = buffer.value[:count].decode(encoding)
 978
 979            # Don't treat Unicode command codepoints as text, except Return.
 980            if text and unicodedata.category(text) == 'Cc' and text != '\r':
 981                text = None
 982
 983        symbol = symbol.value
 984
 985        # If the event is a XIM filtered event, the keysym will be virtual
 986        # (e.g., aacute instead of A after a dead key).  Drop it, we don't
 987        # want these kind of key events.
 988        if ev.xkey.keycode == 0 and not filtered:
 989            symbol = None
 990
 991        # pyglet.self.key keysymbols are identical to X11 keysymbols, no
 992        # need to map the keysymbol.  For keysyms outside the pyglet set, map
 993        # raw key code to a user key.
 994        if symbol and symbol not in key._key_names and ev.xkey.keycode:
 995            # Issue 353: Symbol is uppercase when shift key held down.
 996            try:
 997                symbol = ord(unichr(symbol).lower())
 998            except ValueError:
 999                # Not a valid unichr, use the keycode
1000                symbol = key.user_key(ev.xkey.keycode) 
1001            else:
1002                # If still not recognised, use the keycode
1003                if symbol not in key._key_names:
1004                    symbol = key.user_key(ev.xkey.keycode)
1005
1006        if filtered:
1007            # The event was filtered, text must be ignored, but the symbol is
1008            # still good.
1009            return None, symbol
1010
1011        return text, symbol
1012
1013    def _event_text_motion(self, symbol, modifiers):
1014        if modifiers & key.MOD_ALT:
1015            return None
1016        ctrl = modifiers & key.MOD_CTRL != 0
1017        return _motion_map.get((symbol, ctrl), None)
1018
1019    @ViewEventHandler
1020    @XlibEventHandler(xlib.KeyPress)
1021    @XlibEventHandler(xlib.KeyRelease)
1022    def _event_key_view(self, ev):
1023        # Try to detect autorepeat ourselves if the server doesn't support it
1024        # XXX: Doesn't always work, better off letting the server do it
1025        global _can_detect_autorepeat
1026        if not _can_detect_autorepeat and ev.type == xlib.KeyRelease:
1027            # Look in the queue for a matching KeyPress with same timestamp,
1028            # indicating an auto-repeat rather than actual key event.
1029            saved = []
1030            while True:
1031                auto_event = xlib.XEvent()
1032                result = xlib.XCheckWindowEvent(self._x_display,
1033                    self._window, xlib.KeyPress|xlib.KeyRelease, 
1034                    byref(auto_event))
1035                if not result:
1036                    break
1037                saved.append(auto_event)
1038                if auto_event.type == xlib.KeyRelease:
1039                    # just save this off for restoration back to the queue
1040                    continue
1041                if ev.xkey.keycode == auto_event.xkey.keycode:
1042                    # Found a key repeat: dispatch EVENT_TEXT* event
1043                    text, symbol = self._event_text_symbol(auto_event)
1044                    modifiers = self._translate_modifiers(ev.xkey.state)
1045                    modifiers_ctrl = modifiers & (key.MOD_CTRL | key.MOD_ALT)
1046                    motion = self._event_text_motion(symbol, modifiers)
1047                    if motion:
1048                        if modifiers & key.MOD_SHIFT:
1049                            self.dispatch_event(
1050                                'on_text_motion_select', motion)
1051                        else:
1052                            self.dispatch_event('on_text_motion', motion)
1053                    elif text and not modifiers_ctrl:
1054                        self.dispatch_event('on_text', text)
1055
1056                    ditched = saved.pop()
1057                    for auto_event in reversed(saved):
1058                        xlib.XPutBackEvent(self._x_display, byref(auto_event))
1059                    return
1060                else:
1061                    # Key code of press did not match, therefore no repeating
1062                    # is going on, stop searching.
1063                    break
1064            # Whoops, put the events back, it's for real.
1065            for auto_event in reversed(saved):
1066                xlib.XPutBackEvent(self._x_display, byref(auto_event))
1067
1068        text, symbol = self._event_text_symbol(ev)
1069        modifiers = self._translate_modifiers(ev.xkey.state)
1070        modifiers_ctrl = modifiers & (key.MOD_CTRL | key.MOD_ALT)
1071        motion = self._event_text_motion(symbol, modifiers)
1072
1073        if ev.type == xlib.KeyPress:
1074            if symbol and (not _can_detect_autorepeat or symbol not in self.pressed_keys):
1075                self.dispatch_event('on_key_press', symbol, modifiers)
1076                if _can_detect_autorepeat:
1077                    self.pressed_keys.add(symbol)
1078            if motion:
1079                if modifiers & key.MOD_SHIFT:
1080                    self.dispatch_event('on_text_motion_select', motion)
1081                else:
1082                    self.dispatch_event('on_text_motion', motion)
1083            elif text and not modifiers_ctrl:
1084                self.dispatch_event('on_text', text)
1085        elif ev.type == xlib.KeyRelease:
1086            if symbol:
1087                self.dispatch_event('on_key_release', symbol, modifiers)
1088                if _can_detect_autorepeat and symbol in self.pressed_keys:
1089                    self.pressed_keys.remove(symbol)
1090
1091    @XlibEventHandler(xlib.KeyPress)
1092    @XlibEventHandler(xlib.KeyRelease)
1093    def _event_key(self, ev):
1094        return self._event_key_view(ev)
1095
1096    @ViewEventHandler
1097    @XlibEventHandler(xlib.MotionNotify)
1098    def _event_motionnotify_view(self, ev):
1099        x = ev.xmotion.x
1100        y = self.height - ev.xmotion.y
1101
1102        if self._mouse_in_window:
1103            dx = x - self._mouse_x
1104            dy = y - self._mouse_y
1105        else:
1106            dx = dy = 0
1107
1108        if self._applied_mouse_exclusive and \
1109           (ev.xmotion.x, ev.xmotion.y) == self._mouse_exclusive_client:
1110            # Ignore events caused by XWarpPointer
1111            self._mouse_x = x
1112            self._mouse_y = y
1113            return
1114
1115        if self._applied_mouse_exclusive:
1116            # Reset pointer position
1117            ex, ey = self._mouse_exclusive_client
1118            xlib.XWarpPointer(self._x_display,
1119                0,
1120                self._window,
1121                0, 0,
1122                0, 0,
1123                ex, ey)
1124
1125        self._mouse_x = x
1126        self._mouse_y = y
1127        self._mouse_in_window = True
1128
1129        buttons = 0
1130        if ev.xmotion.state & xlib.Button1MotionMask:
1131            buttons |= mouse.LEFT
1132        if ev.xmotion.state & xlib.Button2MotionMask:
1133            buttons |= mouse.MIDDLE
1134        if ev.xmotion.state & xlib.Button3MotionMask:
1135            buttons |= mouse.RIGHT
1136
1137        if buttons:
1138            # Drag event
1139            modifiers = self._translate_modifiers(ev.xmotion.state)
1140            self.dispatch_event('on_mouse_drag',
1141                x, y, dx, dy, buttons, modifiers)
1142        else:
1143            # Motion event
1144            self.dispatch_event('on_mouse_motion', x, y, dx, dy)
1145
1146    @XlibEventHandler(xlib.MotionNotify)
1147    def _event_motionnotify(self, ev):
1148        # Window motion looks for drags that are outside the view but within
1149        # the window.
1150        buttons = 0
1151        if ev.xmotion.state & xlib.Button1MotionMask:
1152            buttons |= mouse.LEFT
1153        if ev.xmotion.state & xlib.Button2MotionMask:
1154            buttons |= mouse.MIDDLE
1155        if ev.xmotion.state & xlib.Button3MotionMask:
1156            buttons |= mouse.RIGHT
1157
1158        if buttons:
1159            # Drag event
1160            x = ev.xmotion.x - self._view_x
1161            y = self._height - (ev.xmotion.y - self._view_y)
1162
1163            if self._mouse_in_window:
1164                dx = x - self._mouse_x
1165                dy = y - self._mouse_y
1166            else:
1167                dx = dy = 0
1168            self._mouse_x = x
1169            self._mouse_y = y
1170
1171            modifiers = self._translate_modifiers(ev.xmotion.state)
1172            self.dispatch_event('on_mouse_drag',
1173                x, y, dx, dy, buttons, modifiers)
1174
1175    @XlibEventHandler(xlib.ClientMessage)
1176    def _event_clientmessage(self, ev):
1177        atom = ev.xclient.data.l[0]
1178        if atom == xlib.XInternAtom(ev.xclient.display,
1179                                    asbytes('WM_DELETE_WINDOW'), False):
1180            self.dispatch_event('on_close')
1181        elif (self._enable_xsync and 
1182              atom == xlib.XInternAtom(ev.xclient.display, 
1183                                       asbytes('_NET_WM_SYNC_REQUEST'), False)):
1184            lo = ev.xclient.data.l[2]
1185            hi = ev.xclient.data.l[3]
1186            self._current_sync_value = xsync.XSyncValue(hi, lo)
1187
1188    def _sync_resize(self):
1189        if self._enable_xsync and self._current_sync_valid:
1190            if xsync.XSyncValueIsZero(self._current_sync_value):
1191                self._current_sync_valid = False
1192                return
1193            xsync.XSyncSetCounter(self._x_display, 
1194                                  self._sync_counter,
1195                                  self._current_sync_value)
1196            self._current_sync_value = None
1197            self._current_sync_valid = False
1198
1199    @ViewEventHandler
1200    @XlibEventHandler(xlib.ButtonPress)
1201    @XlibEventHandler(xlib.ButtonRelease)
1202    def _event_button(self, ev):
1203        x = ev.xbutton.x
1204        y = self.height - ev.xbutton.y
1205        button = 1 << (ev.xbutton.button - 1)  # 1, 2, 3 -> 1, 2, 4
1206        modifiers = self._translate_modifiers(ev.xbutton.state)
1207        if ev.type == xlib.ButtonPress:
1208            # override_redirect issue: manually activate this window if
1209            # fullscreen.
1210            if self._override_redirect and not self._active:
1211                self.activate()
1212
1213            if ev.xbutton.button == 4:
1214                self.dispatch_event('on_mouse_scroll', x, y, 0, 1)
1215            elif ev.xbutton.button == 5:
1216                self.dispatch_event('on_mouse_scroll', x, y, 0, -1)
1217            elif ev.xbutton.button < len(self._mouse_buttons):
1218                self._mouse_buttons[ev.xbutton.button] = True
1219                self.dispatch_event('on_mouse_press', 
1220                    x, y, button, modifiers)
1221        else:
1222            if ev.xbutton.button < 4:
1223                self._mouse_buttons[ev.xbutton.button] = False
1224                self.dispatch_event('on_mouse_release', 
1225                    x, y, button, modifiers)
1226
1227    @ViewEventHandler
1228    @XlibEventHandler(xlib.Expose)
1229    def _event_expose(self, ev):
1230        # Ignore all expose events except the last one. We could be told
1231        # about exposure rects - but I don't see the point since we're
1232        # working with OpenGL and we'll just redraw the whole scene.
1233        if ev.xexpose.count > 0: return
1234        self.dispatch_event('on_expose')
1235
1236    @ViewEventHandler
1237    @XlibEventHandler(xlib.EnterNotify)
1238    def _event_enternotify(self, ev):
1239        # figure active mouse buttons
1240        # XXX ignore modifier state?
1241        state = ev.xcrossing.state
1242        self._mouse_buttons[1] = state & xlib.Button1Mask
1243        self._mouse_buttons[2] = state & xlib.Button2Mask
1244        self._mouse_buttons[3] = state & xlib.Button3Mask
1245        self._mous

Large files files are truncated, but you can click here to view the full file