from __future__ import absolute_import, division, print_function, unicode_literals
import threading
import six_mod
import ctypes
import logging
import pi3d
from pi3d.constants import PLATFORM_PI, PLATFORM_ANDROID
if pi3d.USE_PYGAME:
import pygame
elif pi3d.PLATFORM != PLATFORM_PI and pi3d.PLATFORM != PLATFORM_ANDROID:
from pyxlib import xlib
#from pyxlib.x import FocusChangeMask
LOGGER = logging.getLogger(__name__)
class _nixMouse(threading.Thread):
"""holds Mouse object, see also (the preferred) events methods"""
BUTTON_1 = 1 << 1
BUTTON_2 = 1 << 2
LEFT_BUTTON = 9 # 1001
RIGHT_BUTTON = 10 # 1010
MIDDLE_BUTTON = 12 # 1100
BUTTON_UP = 8 # 1000
MOUSE_WHEEL_UP = 13 # TODO way of setting IMPS/2 mouse to get scroll events
MOUSE_WHEEL_DOWN = 14 # would need 4 bytes rather than 3 in _check_event()
BUTTONS = BUTTON_1 & BUTTON_2
HEADER = 1 << 3
XSIGN = 1 << 4
YSIGN = 1 << 5
INSTANCE = None
def __init__(self, mouse='mice', restrict=True, width=1920, height=1200, use_x=False):
"""
Arguments:
*mouse*
/dev/input/ device name
*restrict*
stops or allows the mouse x and y values to carry on going beyond:
*width*
mouse x limit
*height*
mouse y limit
"""
super(_nixMouse, self).__init__()
self.fd = open('/dev/input/' + mouse, 'rb')
self.running = False
self.buffr = '' if six_mod.PY3 else b''
self.lock = threading.RLock()
self.width = width
self.height = height
self.restrict = restrict
#create a pointer to this so Display.destroy can stop the thread
from pi3d.Display import Display
Display.INSTANCE.external_mouse = self
self.use_x = False
if use_x: # version as argument to __init__
if pi3d.PLATFORM != pi3d.PLATFORM_ANDROID and pi3d.PLATFORM != pi3d.PLATFORM_PI:
self.d = Display.INSTANCE.opengl.d
self.window = Display.INSTANCE.opengl.window
self.root = ctypes.c_ulong(0)
self.child = ctypes.c_ulong(0)
self.x = ctypes.c_int(0)
self.y = ctypes.c_int(0)
self.rootx = ctypes.c_int(0)
self.rooty = ctypes.c_int(0)
self.mask = ctypes.c_uint(0)
self.use_x = True
self.x_offset = Display.INSTANCE.width // 2 + 1
self.y_offset = Display.INSTANCE.height // 2
self.daemon = True # to kill app rather than waiting for mouse event
self.reset()
def reset(self):
with self.lock:
self._x = self._y = self._dx = self._dy = 0
self.button = False
self._buttons = 0
def start(self):
if not self.running:
self.running = True
super(_nixMouse, self).start()
def run(self):
while self.running:
self._check_event()
self.fd.close()
def position(self):
''' returns x, y tuple
'''
if self.use_x:
xlib.XQueryPointer(self.d, self.window,
ctypes.byref(self.root), ctypes.byref(self.child),
ctypes.byref(self.rootx), ctypes.byref(self.rooty),
ctypes.byref(self.x),ctypes.byref(self.y),
self.mask)
return self.x.value - self.x_offset, self.y_offset - self.y.value
else:
with self.lock:
return self._x, self._y
def velocity(self):
''' returns dx, dy tuple of distance moved since last reading
'''
with self.lock:
dx, dy = self._dx, self._dy
self._dx, self._dy = 0, 0 # need resetting after a read as no event will do this
return dx, dy
def button_status(self):
'''return the button status - use events system for capturing button
events more scientifically.
in _check_event self.buffr returns the following values:
Mouse.LEFT_BUTTON 9
Mouse.RIGHT_BUTTON 10
Mouse.MIDDLE_BUTTON 12
Mouse.BUTTONUP 8
'''
with self.lock:
return self._buttons
def _check_event(self):
if len(self.buffr) >= 3:
buttons = [ord(c) for c in self.buffr]
if buttons[0] in [8, 9, 10, 12]:
self._buttons = buttons[0]
#else:
# self._buttons = 0
buttons = buttons[0]
self.buffr = self.buffr[1:]
if (buttons & _nixMouse.HEADER) > 0:
dx, dy = map(ord, self.buffr[0:2])
self.buffr = self.buffr[2:]
self.button = buttons & _nixMouse.BUTTONS
if (buttons & _nixMouse.XSIGN) > 0:
dx -= 256
if (buttons & _nixMouse.YSIGN) > 0:
dy -= 256
x = self._x + dx
y = self._y + dy
if self.restrict:
x = min(max(x, 0), self.width - 1)
y = min(max(y, 0), self.height - 1)
with self.lock:
self._x, self._y, self._dx, self._dy = x, y, dx, dy
else:
try:
strn = self.fd.read(3).decode("latin-1")
self.buffr += strn
except Exception as e:
LOGGER.error("exception is: %s", e)
self.stop()
return
def stop(self):
self.running = False
class _pygameMouse(object):
"""holds Mouse object, see also (the preferred) events methods"""
BUTTON_1 = 1 << 1
BUTTON_2 = 1 << 2
LEFT_BUTTON = 9 # 1001
RIGHT_BUTTON = 10 # 1010
MIDDLE_BUTTON = 12 # 1100
BUTTON_UP = 8 # 1000
MOUSE_WHEEL_UP = 13
MOUSE_WHEEL_DOWN = 14
BUTTON_MAP = {1:LEFT_BUTTON, 2:MIDDLE_BUTTON, 3:RIGHT_BUTTON,
4:MOUSE_WHEEL_UP, 5:MOUSE_WHEEL_DOWN}
BUTTONS = BUTTON_1 & BUTTON_2
HEADER = 1 << 3
XSIGN = 1 << 4
YSIGN = 1 << 5
INSTANCE = None
def __init__(self, restrict=True, width=1920, height=1200, use_x=False):
"""
Arguments:
*mouse*
/dev/input/ device name
*restrict*
stops or allows the mouse x and y values to carry on going beyond:
*width*
mouse x limit
*height*
mouse y limit
"""
self._x = self._y = self._dx = self._dy = 0
from pi3d.Display import Display
self.centre = (Display.INSTANCE.width / 2, Display.INSTANCE.height / 2)
self.restrict = restrict
if not self.restrict:
import pygame
pygame.mouse.set_pos(self.centre)
pygame.mouse.set_visible(False)
self._buttons = _pygameMouse.BUTTON_UP
def reset(self):
pass
def start(self):
pass
def run(self):
pass
def _check_event(self):
import pygame
pos_list = pygame.event.get(pygame.MOUSEMOTION)
if len(pos_list) > 0:
x, y = pos_list[-1].pos # discard all but the last position
if self.restrict:
self._dx = x - self._x
self._dy = -y - self._y # swap to +ve upwards
self._x = x - self.centre[0]
self._y = -y + self.centre[1]
else:
self._dx = x - self.centre[0]
self._dy = self.centre[1] - y # swap to +ve upwards
self._x += self._dx
self._y += self._dy
pygame.mouse.set_pos(self.centre)
but_list = pygame.event.get(pygame.MOUSEBUTTONDOWN)
if len(but_list) > 0:
self._buttons = _pygameMouse.BUTTON_MAP[but_list[-1].button] # discard all but last button
else:
but_list = pygame.event.get(pygame.MOUSEBUTTONUP)
if len(but_list) > 0:
self._buttons = _pygameMouse.BUTTON_UP
def position(self):
''' returns x, y tuple
'''
self._check_event()
return self._x, self._y
def velocity(self):
''' returns dx, dy tuple of distance moved since last reading
'''
self._check_event()
dx, dy = self._dx, self._dy
self._dx, self._dy = 0, 0 # need resetting after a read as no event will do this
return dx, dy
def button_status(self):
'''return the button status - use events system for capturing button
events more scientifically.
in _check_event self.buffr returns the following values:
Mouse.LEFT_BUTTON 9
Mouse.RIGHT_BUTTON 10
Mouse.MIDDLE_BUTTON 12
Mouse.BUTTONUP 8
'''
self._check_event()
b_val = self._buttons
#self._buttons = _pygameMouse.BUTTON_UP
return b_val
def stop(self):
pass
[docs]def Mouse(*args, **kwds):
if pi3d.USE_PYGAME:
if not _pygameMouse.INSTANCE:
_pygameMouse.INSTANCE = _pygameMouse(*args, **kwds)
return _pygameMouse.INSTANCE
else:
if not _nixMouse.INSTANCE:
_nixMouse.INSTANCE = _nixMouse(*args, **kwds)
return _nixMouse.INSTANCE