-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* First untested draft of mouse API * Add unit tests for mouse ext module * Add new mouse module to news.rst * Update particles example to use mouse ext * Minor docs cleanup
- Loading branch information
Showing
8 changed files
with
329 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
`sdl2.ext.mouse` - Configuring and Handling Mouse Input | ||
======================================================= | ||
|
||
This module provides a number of functions to make it easier to configure and | ||
retrieve mouse input in PySDL2. | ||
|
||
The :func:`show_cursor`, :func:`hide_cursor`, and :func:`cursor_hidden` | ||
functions allow you to easily show, hide, and check the visibility of the mouse | ||
cursor. Additionally, you can check the cursor's absolute or relative location | ||
with the :func:`mouse_coords` and :func:`mouse_delta` functions (respectively), | ||
or obtain the current state of the mouse buttons with | ||
:func:`mouse_button_state`. The location of the mouse cursor can be changed | ||
programatically using :func:`warp_mouse`. | ||
|
||
.. automodule:: sdl2.ext.mouse | ||
:members: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
"""Window routines to manage on-screen windows.""" | ||
from ctypes import c_int, byref | ||
from collections import namedtuple | ||
from .compat import stringify, utf8 | ||
from .err import SDLError, raise_sdl_err | ||
from .window import _get_sdl_window | ||
from .. import mouse | ||
from ..events import SDL_ENABLE, SDL_DISABLE, SDL_QUERY | ||
|
||
__all__ = [ | ||
"show_cursor", "hide_cursor", "cursor_hidden", "mouse_coords", "mouse_delta", | ||
"warp_mouse", "mouse_button_state", "ButtonState", | ||
] | ||
|
||
|
||
class ButtonState(object): | ||
"""A class representing the state of the mouse buttons. | ||
Args: | ||
buttonmask (int): The raw SDL button mask to parse. | ||
Attributes: | ||
raw (int): The raw SDL button mask representing the button state. | ||
""" | ||
def __init__(self, buttonmask): | ||
self.raw = buttonmask | ||
|
||
def __repr__(self): | ||
s = "ButtonState(l={0}, r={1}, m={2})" | ||
return s.format(self.left, self.right, self.middle) | ||
|
||
def __eq__(self, s2): | ||
return self.raw == s2.raw | ||
|
||
def __ne__(self, s2): | ||
return self.raw != s2.raw | ||
|
||
def _check_button(self, bmask): | ||
return int(bool(self.raw & bmask)) | ||
|
||
@property | ||
def any_pressed(self): | ||
"""bool: True if any buttons are currently pressed, otherwise False. | ||
""" | ||
return self.raw != 0 | ||
|
||
@property | ||
def left(self): | ||
"""int: The state of the left mouse button (0 = up, 1 = down). | ||
""" | ||
return self._check_button(mouse.SDL_BUTTON_LMASK) | ||
|
||
@property | ||
def right(self): | ||
"""int: The state of the right mouse button (0 = up, 1 = down). | ||
""" | ||
return self._check_button(mouse.SDL_BUTTON_RMASK) | ||
|
||
@property | ||
def middle(self): | ||
"""int: The state of the middle mouse button (0 = up, 1 = down). | ||
""" | ||
return self._check_button(mouse.SDL_BUTTON_MMASK) | ||
|
||
@property | ||
def x1(self): | ||
"""int: The state of the first extra mouse button (0 = up, 1 = down). | ||
""" | ||
return self._check_button(mouse.SDL_BUTTON_X1MASK) | ||
|
||
@property | ||
def x2(self): | ||
"""int: The state of the second extra mouse button (0 = up, 1 = down). | ||
""" | ||
return self._check_button(mouse.SDL_BUTTON_X2MASK) | ||
|
||
|
||
def show_cursor(): | ||
"""Unhides the mouse cursor if it is currently hidden. | ||
""" | ||
ret = mouse.SDL_ShowCursor(SDL_ENABLE) | ||
if ret < 0: | ||
raise_sdl_err("showing the mouse cursor") | ||
|
||
def hide_cursor(): | ||
"""Hides the mouse cursor if it is currently visible. | ||
""" | ||
ret = mouse.SDL_ShowCursor(SDL_DISABLE) | ||
if ret < 0: | ||
raise_sdl_err("hiding the mouse cursor") | ||
|
||
def cursor_hidden(): | ||
"""Checks whether the mouse cursor is currently visible. | ||
Returns: | ||
bool: True if the cursor is hidden, otherwise False. | ||
""" | ||
return mouse.SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE | ||
|
||
def mouse_coords(desktop=False): | ||
"""Get the current x/y coordinates of the mouse cursor. | ||
By default, this function reports the coordinates relative to the top-left | ||
corner of the SDL window that currently has focus. To obtain the mouse | ||
coordinates relative to the top-right corner of the full desktop, this | ||
function can optionally be called with ``desktop`` argument set to True. | ||
Args: | ||
desktop (bool, optional): If True, reports the mouse coordinates | ||
relative to the full desktop instead of the currently-focused SDL | ||
window. Defaults to False. | ||
Returns: | ||
tuple: The current (x, y) coordinates of the mouse cursor. | ||
""" | ||
x, y = c_int(0), c_int(0) | ||
if desktop: | ||
mouse.SDL_GetGlobalMouseState(byref(x), byref(y)) | ||
else: | ||
mouse.SDL_GetMouseState(byref(x), byref(y)) | ||
return (int(x.value), int(y.value)) | ||
|
||
def mouse_button_state(): | ||
"""Gets the current state of each button of the mouse. | ||
Mice in SDL are currently able to have up to 5 buttons: left, right, middle, | ||
and two extras (x1 and x2). You can check each of these individually, or | ||
alternatively check whether any buttons have been pressed:: | ||
bstate = mouse_button_state() | ||
if bstate.any_pressed: | ||
if bstate.left == 1: | ||
print("left button down!") | ||
if bstate.right == 1: | ||
print("right button down!") | ||
Returns: | ||
:obj:`ButtonState`: A representation of the current button state of the | ||
mouse. | ||
""" | ||
x, y = c_int(0), c_int(0) | ||
bmask = mouse.SDL_GetMouseState(byref(x), byref(y)) | ||
return ButtonState(bmask) | ||
|
||
def mouse_delta(): | ||
"""Get the relative change in cursor position since last checked. | ||
The first time this function is called, it will report the (x, y) change in | ||
cursor position since the SDL video or event system was initialized. | ||
Subsequent calls to this function report the change in position since the | ||
previous time the function was called. | ||
Returns: | ||
tuple: The (x, y) change in cursor coordinates since the function was | ||
last called. | ||
""" | ||
x, y = c_int(0), c_int(0) | ||
mouse.SDL_GetRelativeMouseState(byref(x), byref(y)) | ||
return (int(x.value), int(y.value)) | ||
|
||
def warp_mouse(x, y, window=None, desktop=False): | ||
"""Warps the mouse cursor to a given location on the screen. | ||
By default, this warps the mouse cursor relative to the top-left corner of | ||
whatever SDL window currently has mouse focus. For example,:: | ||
warp_mouse(400, 300) | ||
would warp the mouse to the middle of a 800x600 SDL window. Alternatively, | ||
the cursor can be warped within a specific SDL window or relative to the | ||
full desktop. | ||
Args: | ||
x (int): The new X position for the mouse cursor. | ||
y (int): The new Y position for the mouse cursor. | ||
window (:obj:`SDL_Window` or :obj:`~sdl2.ext.Window`, optional): The | ||
SDL window within which to warp the mouse cursor. If not specified | ||
(the default), the cursor will be warped within the SDL window that | ||
currently has mouse focus. | ||
desktop (bool, optional): If True, the mouse cursor will be warped | ||
relative to the full desktop instead of the current SDL window. | ||
Defaults to False. | ||
""" | ||
if desktop: | ||
ret = mouse.SDL_WarpMouseGlobal(x, y) | ||
if ret < 0: | ||
raise_sdl_err("warping the mouse cursor") | ||
else: | ||
if window is not None: | ||
window = _get_sdl_window(window) | ||
mouse.SDL_WarpMouseInWindow(window, x, y) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import sys | ||
import pytest | ||
import sdl2 | ||
from sdl2 import SDL_Window, SDL_ClearError | ||
from sdl2 import ext as sdl2ext | ||
|
||
from .conftest import SKIP_ANNOYING | ||
|
||
|
||
@pytest.fixture(scope="module") | ||
def with_ext_window(with_sdl): | ||
win = sdl2ext.Window("Test", (100, 100)) | ||
win.show() | ||
yield win | ||
win.close() | ||
|
||
|
||
def test_ButtonState(): | ||
test1 = sdl2.SDL_BUTTON_LMASK | sdl2.SDL_BUTTON_RMASK | sdl2.SDL_BUTTON_MMASK | ||
test2 = sdl2.SDL_BUTTON_X1MASK | sdl2.SDL_BUTTON_X2MASK | ||
|
||
b1 = sdl2ext.ButtonState(test1) | ||
assert b1.raw == test1 | ||
assert b1.left and b1.right and b1.middle | ||
assert not (b1.x1 or b1.x2) | ||
assert b1.any_pressed | ||
|
||
b2 = sdl2ext.ButtonState(test2) | ||
assert not (b2.left or b2.right or b2.middle) | ||
assert b2.x1 and b2.x2 | ||
assert b2.any_pressed | ||
|
||
b3 = sdl2ext.ButtonState(0) | ||
assert not b3.any_pressed | ||
|
||
def test_showhide_cursor(with_ext_window): | ||
sdl2ext.hide_cursor() | ||
assert sdl2ext.cursor_hidden() | ||
sdl2ext.show_cursor() | ||
assert not sdl2ext.cursor_hidden() | ||
|
||
def test_mouse_button_state(with_ext_window): | ||
bstate = sdl2ext.mouse_button_state() | ||
assert isinstance(bstate, sdl2ext.ButtonState) | ||
|
||
def test_mouse_coords(with_ext_window): | ||
# Get mouse positon within the window | ||
pos = sdl2ext.mouse_coords() | ||
assert 0 <= pos[0] <= 100 | ||
assert 0 <= pos[1] <= 100 | ||
# Get mouse positon relative to the desktop | ||
pos = sdl2ext.mouse_coords(desktop=True) | ||
assert 0 <= pos[0] | ||
assert 0 <= pos[1] | ||
|
||
def test_mouse_delta(with_ext_window): | ||
# NOTE: Can't test properly with warp_mouse, since warping the mouse in SDL | ||
# doesn't affect the mouse xdelta/ydelta properties | ||
dx, dy = sdl2ext.mouse_delta() | ||
assert type(dx) == int and type(dy) == int | ||
|
||
@pytest.mark.skipif(SKIP_ANNOYING, reason="Skip unless requested") | ||
def test_warp_mouse(with_ext_window): | ||
x_orig, y_orig = sdl2ext.mouse_coords(desktop=True) | ||
# Test warping within a specific window | ||
win = with_ext_window | ||
sdl2ext.warp_mouse(20, 30, window=win) | ||
x, y = sdl2ext.mouse_coords() | ||
assert x == 20 and y == 30 | ||
# Test warping in the window that currently has focus | ||
sdl2ext.warp_mouse(50, 50) | ||
x, y = sdl2ext.mouse_coords() | ||
assert x == 50 and y == 50 | ||
# Test warping relative to the desktop | ||
sdl2ext.warp_mouse(x_orig, y_orig, desktop=True) | ||
x, y = sdl2ext.mouse_coords(desktop=True) | ||
assert x == x_orig and y == y_orig |