1# ---------------------------------------------------------------------------- 2# pyglet 3# Copyright (c) 2006-2008 Alex Holkner 4# Copyright (c) 2008-2020 pyglet contributors 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 11# * Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# * Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in 15# the documentation and/or other materials provided with the 16# distribution. 17# * Neither the name of pyglet nor the names of its 18# contributors may be used to endorse or promote products 19# derived from this software without specific prior written 20# permission. 21# 22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33# POSSIBILITY OF SUCH DAMAGE. 34# ---------------------------------------------------------------------------- 35 36from pyglet.input.base import Tablet, TabletCanvas 37from pyglet.input.base import TabletCursor, DeviceOpenException 38from pyglet.input.x11_xinput import XInputWindowEventDispatcher 39from pyglet.input.x11_xinput import get_devices, DeviceResponder 40 41 42try: 43 from pyglet.libs.x11 import xinput as xi 44 _have_xinput = True 45except: 46 _have_xinput = False 47 48 49class XInputTablet(Tablet): 50 name = 'XInput Tablet' 51 52 def __init__(self, cursors): 53 self.cursors = cursors 54 55 def open(self, window): 56 return XInputTabletCanvas(window, self.cursors) 57 58 59class XInputTabletCanvas(DeviceResponder, TabletCanvas): 60 def __init__(self, window, cursors): 61 super(XInputTabletCanvas, self).__init__(window) 62 self.cursors = cursors 63 64 dispatcher = XInputWindowEventDispatcher.get_dispatcher(window) 65 66 self.display = window.display 67 self._open_devices = [] 68 self._cursor_map = {} 69 for cursor in cursors: 70 device = cursor.device 71 device_id = device._device_id 72 self._cursor_map[device_id] = cursor 73 74 cursor.max_pressure = device.axes[2].max 75 76 if self.display._display != device.display._display: 77 raise DeviceOpenException('Window and device displays differ') 78 79 open_device = xi.XOpenDevice(device.display._display, device_id) 80 if not open_device: 81 # Ignore this cursor; fail if no cursors added 82 continue 83 self._open_devices.append(open_device) 84 85 dispatcher.open_device(device_id, open_device, self) 86 87 def close(self): 88 for device in self._open_devices: 89 xi.XCloseDevice(self.display._display, device) 90 91 def _motion(self, e): 92 cursor = self._cursor_map.get(e.deviceid) 93 x = e.x 94 y = self.window.height - e.y 95 pressure = e.axis_data[2] / float(cursor.max_pressure) 96 self.dispatch_event('on_motion', cursor, x, y, pressure, 0.0, 0.0) 97 98 def _proximity_in(self, e): 99 cursor = self._cursor_map.get(e.deviceid) 100 self.dispatch_event('on_enter', cursor) 101 102 def _proximity_out(self, e): 103 cursor = self._cursor_map.get(e.deviceid) 104 self.dispatch_event('on_leave', cursor) 105 106 107class XInputTabletCursor(TabletCursor): 108 def __init__(self, device): 109 super(XInputTabletCursor, self).__init__(device.name) 110 self.device = device 111 112 113def get_tablets(display=None): 114 # Each cursor appears as a separate xinput device; find devices that look 115 # like Wacom tablet cursors and amalgamate them into a single tablet. 116 valid_names = ('stylus', 'cursor', 'eraser', 'wacom', 'pen', 'pad') 117 cursors = [] 118 devices = get_devices(display) 119 for device in devices: 120 dev_name = device.name.lower().split() 121 if any(n in dev_name for n in valid_names) and len(device.axes) >= 3: 122 cursors.append(XInputTabletCursor(device)) 123 124 if cursors: 125 return [XInputTablet(cursors)] 126 return [] 127