Skip to content

Commit a39d374

Browse files
authored
Add pygame.mouse.get_just_pressed/released() (#2836)
* Add pygame.mouse.get_just_pressed/released + stubs, tests, docs * Added user docs * Fix format issue caused by clang-format * Rename buttons variable * Docs syntax fix * Rename API functions for clarity * Remove num_button args, update docs * Add METH_NOARGS to function signature * Fix language in docs * add missing newlines * Fix typo and docs
1 parent 0449dca commit a39d374

File tree

9 files changed

+169
-5
lines changed

9 files changed

+169
-5
lines changed

buildconfig/stubs/pygame/mouse.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ from ._common import Coordinate, Sequence, IntCoordinate
99
def get_pressed(num_buttons: Literal[3] = 3) -> Tuple[bool, bool, bool]: ...
1010
@overload
1111
def get_pressed(num_buttons: Literal[5]) -> Tuple[bool, bool, bool, bool, bool]: ...
12+
def get_just_pressed() -> Tuple[bool, bool, bool, bool, bool]: ...
13+
def get_just_released() -> Tuple[bool, bool, bool, bool, bool]: ...
1214
def get_pos() -> Tuple[int, int]: ...
1315
def get_rel() -> Tuple[int, int]: ...
1416
@overload

docs/reST/c_api/event.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,26 @@ Header file: src_c/include/pygame.h
3939
If *event* is ``NULL`` then create an empty event object.
4040
On failure raise a Python exception and return ``NULL``.
4141
42+
.. c:function:: char* pgEvent_GetKeyDownInfo(void)
43+
44+
Return an array of bools (using char) of length SDL_NUM_SCANCODES
45+
with the most recent key presses.
46+
47+
.. c:function:: char* pgEvent_GetKeyUpInfo(void)
48+
49+
Return an array of bools (using char) of length SDL_NUM_SCANCODES
50+
with the most recent key releases.
51+
52+
.. c:function:: char* pgEvent_GetMouseButtonDownInfo(void)
53+
54+
Return an array of bools (using char) of length 5
55+
with the most recent button presses.
56+
57+
.. c:function:: char* pgEvent_GetMouseButtonUpInfo(void)
58+
59+
Return an array of bools (using char) of length 5
60+
with the most recent button releases.
61+
4262
.. c:function:: int pg_post_event(Uint32 type, PyObject *dict)
4363
4464
Posts a pygame event that is an ``SDL_USEREVENT`` on the SDL side. This

docs/reST/ref/mouse.rst

Lines changed: 52 additions & 2 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) -> (button1, button2, button3)`
83-
| :sg:`get_pressed(num_buttons=5) -> (button1, button2, button3, button4, button5)`
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)`
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
@@ -107,6 +107,56 @@ scroll, such as ``which`` (it will tell you what exact mouse device trigger the
107107

108108
.. ## pygame.mouse.get_pressed ##
109109
110+
.. function:: get_just_pressed
111+
112+
| :sl:`get the most recently pressed buttons`
113+
| :sg:`get_just_pressed() -> (left_button, middle_button, right_button, x1_button, x2_button)`
114+
115+
Very similar to :func:`pygame.mouse.get_pressed()`, returning a tuple
116+
of length 5 with the important difference that the buttons are
117+
True only in the frame they start being pressed. This can be convenient
118+
for checking the buttons pressed "this frame", but for more precise results
119+
and correct ordering prefer using the pygame.MOUSEBUTTONDOWN event.
120+
121+
The result of this function is updated when new events are processed,
122+
e.g. in :func:`pygame.event.get()` or :func:`pygame.event.pump()`.
123+
124+
.. seealso:: :func:`pygame.mouse.get_just_released()`
125+
126+
::
127+
128+
if pygame.mouse.get_just_pressed()[0]:
129+
print("LMB just pressed")
130+
131+
.. versionadded:: 2.5.0
132+
133+
.. ## pygame.mouse.get_just_pressed ##
134+
135+
.. function:: get_just_released
136+
137+
| :sl:`get the most recently released buttons`
138+
| :sg:`get_just_released() -> (left_button, middle_button, right_button, x1_button, x2_button)`
139+
140+
Similar to :func:`pygame.mouse.get_pressed()`, returning a tuple
141+
of length 5 with the important difference that the buttons are
142+
True only in the frame they stop being pressed. This can be convenient
143+
for checking the buttons released "this frame", but for more precise results
144+
and correct ordering prefer using the pygame.MOUSEBUTTONUP event.
145+
146+
The result of this function is updated when new events are processed,
147+
e.g. in :func:`pygame.event.get()` or :func:`pygame.event.pump()`.
148+
149+
.. seealso:: :func:`pygame.mouse.get_just_pressed()`
150+
151+
::
152+
153+
if pygame.mouse.get_just_released()[0]:
154+
print("LMB just released")
155+
156+
.. versionadded:: 2.5.0
157+
158+
.. ## pygame.mouse.get_just_released ##
159+
110160
.. function:: get_pos
111161

112162
| :sl:`get the mouse cursor position`

src_c/_pygame.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ typedef enum {
533533
#define PYGAMEAPI_COLOR_NUMSLOTS 5
534534
#define PYGAMEAPI_MATH_NUMSLOTS 2
535535
#define PYGAMEAPI_BASE_NUMSLOTS 29
536-
#define PYGAMEAPI_EVENT_NUMSLOTS 8
536+
#define PYGAMEAPI_EVENT_NUMSLOTS 10
537537
#define PYGAMEAPI_WINDOW_NUMSLOTS 1
538538
#define PYGAMEAPI_GEOMETRY_NUMSLOTS 1
539539

src_c/doc/mouse_doc.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* Auto generated file: with makeref.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) -> (button1, button2, button3)\nget_pressed(num_buttons=5) -> (button1, button2, button3, button4, button5)\nget the state of the mouse buttons"
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"
4+
#define DOC_MOUSE_GETJUSTPRESSED "get_just_pressed() -> (left_button, middle_button, right_button, x1_button, x2_button)\nget the most recently pressed buttons"
5+
#define DOC_MOUSE_GETJUSTRELEASED "get_just_released() -> (left_button, middle_button, right_button, x1_button, x2_button)\nget the most recently released buttons"
46
#define DOC_MOUSE_GETPOS "get_pos() -> (x, y)\nget the mouse cursor position"
57
#define DOC_MOUSE_GETREL "get_rel() -> (x, y)\nget the amount of mouse movement"
68
#define DOC_MOUSE_SETPOS "set_pos([x, y], /) -> None\nset the mouse cursor position"

src_c/event.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ static SDL_Event _pg_last_keydown_event = {0};
9595
/* Not used as text, acts as an array of bools */
9696
static char pressed_keys[SDL_NUM_SCANCODES] = {0};
9797
static char released_keys[SDL_NUM_SCANCODES] = {0};
98+
static char pressed_mouse_buttons[5] = {0};
99+
static char released_mouse_buttons[5] = {0};
98100

99101
#ifdef __EMSCRIPTEN__
100102
/* these macros are no-op here */
@@ -539,6 +541,14 @@ pg_event_filter(void *_, SDL_Event *event)
539541

540542
else if (event->type == SDL_MOUSEBUTTONDOWN ||
541543
event->type == SDL_MOUSEBUTTONUP) {
544+
if (event->type == SDL_MOUSEBUTTONDOWN &&
545+
event->button.button - 1 < 5) {
546+
pressed_mouse_buttons[event->button.button - 1] = 1;
547+
}
548+
else if (event->type == SDL_MOUSEBUTTONUP &&
549+
event->button.button - 1 < 5) {
550+
released_mouse_buttons[event->button.button - 1] = 1;
551+
}
542552
if (event->button.button & PGM_BUTTON_KEEP)
543553
event->button.button ^= PGM_BUTTON_KEEP;
544554
else if (event->button.button >= PGM_BUTTON_WHEELUP)
@@ -1602,6 +1612,8 @@ _pg_event_pump(int dopump)
16021612
* pygame.event.get(), but not on pygame.event.get(pump=False). */
16031613
memset(pressed_keys, 0, sizeof(pressed_keys));
16041614
memset(released_keys, 0, sizeof(released_keys));
1615+
memset(pressed_mouse_buttons, 0, sizeof(pressed_mouse_buttons));
1616+
memset(released_mouse_buttons, 0, sizeof(released_mouse_buttons));
16051617

16061618
SDL_PumpEvents();
16071619
}
@@ -1797,6 +1809,18 @@ pgEvent_GetKeyUpInfo(void)
17971809
return released_keys;
17981810
}
17991811

1812+
char *
1813+
pgEvent_GetMouseButtonDownInfo(void)
1814+
{
1815+
return pressed_mouse_buttons;
1816+
}
1817+
1818+
char *
1819+
pgEvent_GetMouseButtonUpInfo(void)
1820+
{
1821+
return released_mouse_buttons;
1822+
}
1823+
18001824
static PyObject *
18011825
_pg_get_all_events_except(PyObject *obj)
18021826
{
@@ -2303,7 +2327,7 @@ MODINIT_DEFINE(event)
23032327
}
23042328

23052329
/* export the c api */
2306-
assert(PYGAMEAPI_EVENT_NUMSLOTS == 8);
2330+
assert(PYGAMEAPI_EVENT_NUMSLOTS == 10);
23072331
c_api[0] = &pgEvent_Type;
23082332
c_api[1] = pgEvent_New;
23092333
c_api[2] = pg_post_event;
@@ -2312,6 +2336,8 @@ MODINIT_DEFINE(event)
23122336
c_api[5] = pg_GetKeyRepeat;
23132337
c_api[6] = pgEvent_GetKeyDownInfo;
23142338
c_api[7] = pgEvent_GetKeyUpInfo;
2339+
c_api[8] = pgEvent_GetMouseButtonDownInfo;
2340+
c_api[9] = pgEvent_GetMouseButtonUpInfo;
23152341

23162342
apiobj = encapsulate_api(c_api, "event");
23172343
if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) {

src_c/include/_pygame.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,12 @@ typedef struct pgEventObject pgEventObject;
409409

410410
#define pgEvent_GetKeyUpInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 7))
411411

412+
#define pgEvent_GetMouseButtonDownInfo \
413+
(*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 8))
414+
415+
#define pgEvent_GetMouseButtonUpInfo \
416+
(*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 9))
417+
412418
#define import_pygame_event() IMPORT_PYGAME_MODULE(event)
413419
#endif
414420

src_c/mouse.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,40 @@ mouse_get_pressed(PyObject *self, PyObject *args, PyObject *kwargs)
162162
return tuple;
163163
}
164164

165+
static PyObject *
166+
mouse_get_just_pressed(PyObject *self, PyObject *_null)
167+
{
168+
PyObject *tuple;
169+
VIDEO_INIT_CHECK();
170+
171+
char *pressed_buttons = pgEvent_GetMouseButtonDownInfo();
172+
if (!(tuple = PyTuple_New(5)))
173+
return NULL;
174+
175+
for (int i = 0; i < 5; i++) {
176+
PyTuple_SET_ITEM(tuple, i, PyBool_FromLong(pressed_buttons[i]));
177+
}
178+
179+
return tuple;
180+
}
181+
182+
static PyObject *
183+
mouse_get_just_released(PyObject *self, PyObject *_null)
184+
{
185+
PyObject *tuple;
186+
VIDEO_INIT_CHECK();
187+
188+
char *released_buttons = pgEvent_GetMouseButtonUpInfo();
189+
if (!(tuple = PyTuple_New(5)))
190+
return NULL;
191+
192+
for (int i = 0; i < 5; i++) {
193+
PyTuple_SET_ITEM(tuple, i, PyBool_FromLong(released_buttons[i]));
194+
}
195+
196+
return tuple;
197+
}
198+
165199
static PyObject *
166200
mouse_set_visible(PyObject *self, PyObject *args)
167201
{
@@ -500,6 +534,10 @@ static PyMethodDef _mouse_methods[] = {
500534
{"get_rel", (PyCFunction)mouse_get_rel, METH_NOARGS, DOC_MOUSE_GETREL},
501535
{"get_pressed", (PyCFunction)mouse_get_pressed,
502536
METH_VARARGS | METH_KEYWORDS, DOC_MOUSE_GETPRESSED},
537+
{"get_just_pressed", (PyCFunction)mouse_get_just_pressed, METH_NOARGS,
538+
DOC_MOUSE_GETJUSTPRESSED},
539+
{"get_just_released", (PyCFunction)mouse_get_just_released, METH_NOARGS,
540+
DOC_MOUSE_GETJUSTRELEASED},
503541
{"set_visible", mouse_set_visible, METH_VARARGS, DOC_MOUSE_SETVISIBLE},
504542
{"get_visible", mouse_get_visible, METH_NOARGS, DOC_MOUSE_GETVISIBLE},
505543
{"get_focused", (PyCFunction)mouse_get_focused, METH_NOARGS,
@@ -540,6 +578,10 @@ MODINIT_DEFINE(mouse)
540578
if (PyErr_Occurred()) {
541579
return NULL;
542580
}
581+
import_pygame_event();
582+
if (PyErr_Occurred()) {
583+
return NULL;
584+
}
543585

544586
/* create the module */
545587
return PyModule_Create(&_module);

test/mouse_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,22 @@ def test_get_pressed(self):
288288
with self.assertRaises(ValueError):
289289
pygame.mouse.get_pressed(4)
290290

291+
def test_get_just_pressed(self):
292+
mouse_buttons = pygame.mouse.get_just_pressed()
293+
self.assertIsInstance(mouse_buttons, tuple)
294+
self.assertEqual(len(mouse_buttons), 5)
295+
for value in mouse_buttons:
296+
self.assertIsInstance(value, bool)
297+
self.assertEqual(value, False)
298+
299+
def test_get_just_released(self):
300+
mouse_buttons = pygame.mouse.get_just_released()
301+
self.assertIsInstance(mouse_buttons, tuple)
302+
self.assertEqual(len(mouse_buttons), 5)
303+
for value in mouse_buttons:
304+
self.assertIsInstance(value, bool)
305+
self.assertEqual(value, False)
306+
291307
def test_get_pos(self):
292308
"""Ensures get_pos returns the correct types."""
293309
expected_length = 2

0 commit comments

Comments
 (0)