Skip to content

Commit ded57ba

Browse files
authored
Add desktop argument to get_pos and get_pressed (#3105)
1 parent 2e952dc commit ded57ba

File tree

5 files changed

+100
-42
lines changed

5 files changed

+100
-42
lines changed

buildconfig/stubs/pygame/mouse.pyi

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ from pygame.surface import Surface
77
from pygame.typing import Coordinate, SequenceLike, IntCoordinate
88

99
@overload
10-
def get_pressed(num_buttons: Literal[3] = 3) -> Tuple[bool, bool, bool]: ...
10+
def get_pressed(
11+
num_buttons: Literal[3] = 3,
12+
desktop: bool = False) -> Tuple[bool, bool, bool]: ...
1113
@overload
12-
def get_pressed(num_buttons: Literal[5]) -> Tuple[bool, bool, bool, bool, bool]: ...
14+
def get_pressed(
15+
num_buttons: Literal[5],
16+
desktop: bool = False) -> Tuple[bool, bool, bool, bool, bool]: ...
1317
def get_just_pressed() -> Tuple[bool, bool, bool, bool, bool]: ...
1418
def get_just_released() -> Tuple[bool, bool, bool, bool, bool]: ...
15-
def get_pos() -> Tuple[int, int]: ...
19+
def get_pos(desktop: bool = False) -> Tuple[int, int]: ...
1620
def get_rel() -> Tuple[int, int]: ...
1721
@overload
1822
def set_pos(pos: Coordinate, /) -> None: ...

docs/reST/ref/mouse.rst

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ scroll, such as ``which`` (it will tell you what exact mouse device trigger the
7979
.. function:: get_pressed
8080

8181
| :sl:`get the state of the mouse buttons`
82-
| :sg:`get_pressed(num_buttons=3) -> (left_button, middle_button, right_button)`
83-
| :sg:`get_pressed(num_buttons=5) -> (left_button, middle_button, right_button, x1_button, x2_button)`
82+
| :sg:`get_pressed(num_buttons=3, desktop=False) -> (left_button, middle_button, right_button)`
83+
| :sg:`get_pressed(num_buttons=5, desktop=False) -> (left_button, middle_button, right_button, x1_button, x2_button)`
8484
8585
Returns a sequence of booleans representing the state of all the mouse
8686
buttons. A true value means the mouse is currently being pressed at the time
@@ -97,12 +97,21 @@ scroll, such as ``which`` (it will tell you what exact mouse device trigger the
9797
are added to the returned tuple. Only ``3`` and ``5`` are valid values
9898
for this parameter.
9999

100+
If the ``desktop`` argument is ``True`` the mouse state will be correct even
101+
if the window has no focus. In addition since it queries the OS it does not depend
102+
on the last event pump while being slightly slower.
103+
100104
.. note:: On ``X11`` some X servers use middle button emulation. When you
101105
click both buttons ``1`` and ``3`` at the same time a ``2`` button event
102106
can be emitted.
103107

108+
.. warning:: Due to design constraints it is impossible to retrieve the desktop
109+
mouse state on Wayland. The normal mouse state is returned instead.
110+
104111
.. versionchangedold:: 2.0.0 ``num_buttons`` argument added
105112

113+
.. versionchanged:: 2.5.2 Added the ``desktop`` argument
114+
106115
.. ## pygame.mouse.get_pressed ##
107116
108117
.. function:: get_just_pressed
@@ -158,12 +167,20 @@ scroll, such as ``which`` (it will tell you what exact mouse device trigger the
158167
.. function:: get_pos
159168

160169
| :sl:`get the mouse cursor position`
161-
| :sg:`get_pos() -> (x, y)`
170+
| :sg:`get_pos(desktop=False) -> (x, y)`
171+
172+
By default returns the ``x`` and ``y`` position of the mouse cursor. The position
173+
is relative to the top-left corner of the display. The cursor position can be
174+
located outside of the display window, but is always constrained to the screen.
175+
176+
If the ``desktop`` argument is ``True``, the position will be instead relative to the
177+
top-left corner of the primary monitor. The position might be negative or exceed
178+
the desktop bounds if multiple monitors are present.
179+
180+
.. warning:: Due to design constraints it is impossible to retrieve the desktop
181+
mouse state on Wayland. The relative mouse position is returned instead.
162182

163-
Returns the ``x`` and ``y`` position of the mouse cursor. The position is
164-
relative to the top-left corner of the display. The cursor position can be
165-
located outside of the display window, but is always constrained to the
166-
screen.
183+
.. versionchanged:: 2.5.2 Added the ``desktop`` argument
167184

168185
.. ## pygame.mouse.get_pos ##
169186

src_c/doc/mouse_doc.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/* Auto generated file: with make_docs.py . Docs go in docs/reST/ref/ . */
22
#define DOC_MOUSE "pygame module to work with the mouse"
3-
#define DOC_MOUSE_GETPRESSED "get_pressed(num_buttons=3) -> (left_button, middle_button, right_button)\nget_pressed(num_buttons=5) -> (left_button, middle_button, right_button, x1_button, x2_button)\nget the state of the mouse buttons"
3+
#define DOC_MOUSE_GETPRESSED "get_pressed(num_buttons=3, desktop=False) -> (left_button, middle_button, right_button)\nget_pressed(num_buttons=5, desktop=False) -> (left_button, middle_button, right_button, x1_button, x2_button)\nget the state of the mouse buttons"
44
#define DOC_MOUSE_GETJUSTPRESSED "get_just_pressed() -> (left_button, middle_button, right_button, x1_button, x2_button)\nget the most recently pressed buttons"
55
#define DOC_MOUSE_GETJUSTRELEASED "get_just_released() -> (left_button, middle_button, right_button, x1_button, x2_button)\nget the most recently released buttons"
6-
#define DOC_MOUSE_GETPOS "get_pos() -> (x, y)\nget the mouse cursor position"
6+
#define DOC_MOUSE_GETPOS "get_pos(desktop=False) -> (x, y)\nget the mouse cursor position"
77
#define DOC_MOUSE_GETREL "get_rel() -> (x, y)\nget the amount of mouse movement"
88
#define DOC_MOUSE_SETPOS "set_pos([x, y], /) -> None\nset the mouse cursor position"
99
#define DOC_MOUSE_SETVISIBLE "set_visible(bool, /) -> bool\nhide or show the mouse cursor"

src_c/mouse.c

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -63,37 +63,48 @@ mouse_set_pos(PyObject *self, PyObject *args)
6363
}
6464

6565
static PyObject *
66-
mouse_get_pos(PyObject *self, PyObject *_null)
66+
mouse_get_pos(PyObject *self, PyObject *args, PyObject *kwargs)
6767
{
6868
int x, y;
69+
int desktop = 0;
6970

70-
VIDEO_INIT_CHECK();
71-
SDL_GetMouseState(&x, &y);
71+
static char *kwids[] = {"desktop", NULL};
7272

73-
{
74-
SDL_Window *sdlWindow = pg_GetDefaultWindow();
75-
SDL_Renderer *sdlRenderer = SDL_GetRenderer(sdlWindow);
76-
if (sdlRenderer != NULL) {
77-
SDL_Rect vprect;
78-
float scalex, scaley;
79-
80-
SDL_RenderGetScale(sdlRenderer, &scalex, &scaley);
81-
SDL_RenderGetViewport(sdlRenderer, &vprect);
82-
83-
x = (int)(x / scalex);
84-
y = (int)(y / scaley);
85-
86-
x -= vprect.x;
87-
y -= vprect.y;
73+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|p", kwids, &desktop))
74+
return NULL;
75+
VIDEO_INIT_CHECK();
8876

89-
if (x < 0)
90-
x = 0;
91-
if (x >= vprect.w)
92-
x = vprect.w - 1;
93-
if (y < 0)
94-
y = 0;
95-
if (y >= vprect.h)
96-
y = vprect.h - 1;
77+
if (desktop == 1) {
78+
SDL_GetGlobalMouseState(&x, &y);
79+
}
80+
else {
81+
SDL_GetMouseState(&x, &y);
82+
83+
{
84+
SDL_Window *sdlWindow = pg_GetDefaultWindow();
85+
SDL_Renderer *sdlRenderer = SDL_GetRenderer(sdlWindow);
86+
if (sdlRenderer != NULL) {
87+
SDL_Rect vprect;
88+
float scalex, scaley;
89+
90+
SDL_RenderGetScale(sdlRenderer, &scalex, &scaley);
91+
SDL_RenderGetViewport(sdlRenderer, &vprect);
92+
93+
x = (int)(x / scalex);
94+
y = (int)(y / scaley);
95+
96+
x -= vprect.x;
97+
y -= vprect.y;
98+
99+
if (x < 0)
100+
x = 0;
101+
if (x >= vprect.w)
102+
x = vprect.w - 1;
103+
if (y < 0)
104+
y = 0;
105+
if (y >= vprect.h)
106+
y = vprect.h - 1;
107+
}
97108
}
98109
}
99110

@@ -130,18 +141,22 @@ mouse_get_pressed(PyObject *self, PyObject *args, PyObject *kwargs)
130141
PyObject *tuple;
131142
int state;
132143
int num_buttons = 3;
144+
int desktop = 0;
133145

134-
static char *kwids[] = {"num_buttons", NULL};
146+
static char *kwids[] = {"num_buttons", "desktop", NULL};
135147

136-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwids, &num_buttons))
148+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ip", kwids, &num_buttons,
149+
&desktop))
137150
return NULL;
138151
VIDEO_INIT_CHECK();
139152

140153
if (num_buttons != 3 && num_buttons != 5)
141154
return RAISE(PyExc_ValueError,
142155
"Number of buttons needs to be 3 or 5.");
143156

144-
state = SDL_GetMouseState(NULL, NULL);
157+
state = desktop ? SDL_GetGlobalMouseState(NULL, NULL)
158+
: SDL_GetMouseState(NULL, NULL);
159+
145160
if (!(tuple = PyTuple_New(num_buttons)))
146161
return NULL;
147162

@@ -530,7 +545,8 @@ mouse_set_relative_mode(PyObject *self, PyObject *arg)
530545

531546
static PyMethodDef _mouse_methods[] = {
532547
{"set_pos", mouse_set_pos, METH_VARARGS, DOC_MOUSE_SETPOS},
533-
{"get_pos", (PyCFunction)mouse_get_pos, METH_NOARGS, DOC_MOUSE_GETPOS},
548+
{"get_pos", (PyCFunction)mouse_get_pos, METH_VARARGS | METH_KEYWORDS,
549+
DOC_MOUSE_GETPOS},
534550
{"get_rel", (PyCFunction)mouse_get_rel, METH_NOARGS, DOC_MOUSE_GETREL},
535551
{"get_pressed", (PyCFunction)mouse_get_pressed,
536552
METH_VARARGS | METH_KEYWORDS, DOC_MOUSE_GETPRESSED},

test/mouse_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,17 @@ def test_get_pressed(self):
285285
for value in buttons_pressed:
286286
self.assertIsInstance(value, bool)
287287

288+
desktop_pressed1 = pygame.mouse.get_pressed(desktop=True)
289+
desktop_pressed2 = pygame.mouse.get_pressed(5, desktop=True)
290+
desktop_pressed3 = pygame.mouse.get_pressed(3, True)
291+
self.assertEqual(len(desktop_pressed1), 3)
292+
self.assertEqual(len(desktop_pressed2), 5)
293+
self.assertEqual(len(desktop_pressed3), 3)
294+
for desktop_pressed in [desktop_pressed1, desktop_pressed2, desktop_pressed3]:
295+
self.assertIsInstance(desktop_pressed, tuple)
296+
for value in desktop_pressed:
297+
self.assertIsInstance(value, bool)
298+
288299
with self.assertRaises(ValueError):
289300
pygame.mouse.get_pressed(4)
290301

@@ -315,6 +326,16 @@ def test_get_pos(self):
315326
for value in pos:
316327
self.assertIsInstance(value, int)
317328

329+
desktop_pos1 = pygame.mouse.get_pos(True)
330+
desktop_pos2 = pygame.mouse.get_pos(desktop=True)
331+
self.assertEqual(desktop_pos1, desktop_pos2)
332+
333+
for desktop_pos in [desktop_pos1, desktop_pos2]:
334+
self.assertIsInstance(desktop_pos, tuple)
335+
self.assertEqual(len(desktop_pos), expected_length)
336+
for value in desktop_pos:
337+
self.assertIsInstance(value, int)
338+
318339
def test_set_pos__invalid_pos(self):
319340
"""Ensures set_pos handles invalid positions correctly."""
320341
for invalid_pos in ((1,), [1, 2, 3], 1, "1", (1, "1"), []):

0 commit comments

Comments
 (0)