From 93828b93d3e657937d6ae582ae6a5b8a2cf432ae Mon Sep 17 00:00:00 2001 From: gresm <78505251+gresm@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:58:42 +0200 Subject: [PATCH 01/36] Just rename "event.c" to "_event.c". --- buildconfig/Setup.Android.SDL2.in | 2 +- buildconfig/Setup.Emscripten.SDL2.in | 2 +- buildconfig/Setup.SDL2.in | 2 +- src_c/{event.c => _event.c} | 8 ++++---- src_c/display.c | 4 ++-- src_c/include/_pygame.h | 28 +++++++++++++++------------- src_c/meson.build | 6 +++--- src_c/static.c | 6 +++--- src_py/event.py | 1 + src_py/meson.build | 1 + 10 files changed, 32 insertions(+), 28 deletions(-) rename src_c/{event.c => _event.c} (99%) create mode 100644 src_py/event.py diff --git a/buildconfig/Setup.Android.SDL2.in b/buildconfig/Setup.Android.SDL2.in index fc556ad024..5ef285a55e 100644 --- a/buildconfig/Setup.Android.SDL2.in +++ b/buildconfig/Setup.Android.SDL2.in @@ -43,7 +43,7 @@ base src_c/base.c $(SDL) $(DEBUG) color src_c/color.c $(SDL) $(DEBUG) constants src_c/constants.c $(SDL) $(DEBUG) display src_c/display.c $(SDL) $(DEBUG) -event src_c/event.c $(SDL) $(DEBUG) +_event src_c/_event.c $(SDL) $(DEBUG) key src_c/key.c $(SDL) $(DEBUG) mouse src_c/mouse.c $(SDL) $(DEBUG) rect src_c/rect.c src_c/pgcompat_rect.c $(SDL) $(DEBUG) diff --git a/buildconfig/Setup.Emscripten.SDL2.in b/buildconfig/Setup.Emscripten.SDL2.in index 23b22f5766..2912b6d3a3 100644 --- a/buildconfig/Setup.Emscripten.SDL2.in +++ b/buildconfig/Setup.Emscripten.SDL2.in @@ -46,7 +46,7 @@ color src_c/void.c controller_old src_c/void.c display src_c/void.c draw src_c/void.c -event src_c/void.c +_event src_c/void.c font src_c/void.c gfxdraw src_c/void.c joystick src_c/void.c diff --git a/buildconfig/Setup.SDL2.in b/buildconfig/Setup.SDL2.in index 2fd66eeea3..e789e029f5 100644 --- a/buildconfig/Setup.SDL2.in +++ b/buildconfig/Setup.SDL2.in @@ -53,7 +53,7 @@ base src_c/base.c $(SDL) $(DEBUG) color src_c/color.c $(SDL) $(DEBUG) constants src_c/constants.c $(SDL) $(DEBUG) display src_c/display.c $(SDL) $(DEBUG) -event src_c/event.c $(SDL) $(DEBUG) +_event src_c/_event.c $(SDL) $(DEBUG) key src_c/key.c $(SDL) $(DEBUG) mouse src_c/mouse.c $(SDL) $(DEBUG) rect src_c/rect.c src_c/pgcompat_rect.c $(SDL) $(DEBUG) diff --git a/src_c/event.c b/src_c/_event.c similarity index 99% rename from src_c/event.c rename to src_c/_event.c index aa0c95b39e..68719fd7e8 100644 --- a/src_c/event.c +++ b/src_c/_event.c @@ -2241,9 +2241,9 @@ pg_event_custom_type(PyObject *self, PyObject *_null) } static PyMethodDef _event_methods[] = { - {"_internal_mod_init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, + {"init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, "auto initialize for event module"}, - {"_internal_mod_quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, + {"quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, "auto quit for event module"}, {"event_name", event_name, METH_VARARGS, DOC_EVENT_EVENTNAME}, @@ -2274,7 +2274,7 @@ static PyMethodDef _event_methods[] = { {NULL, NULL, 0, NULL}}; -MODINIT_DEFINE(event) +MODINIT_DEFINE(_event) { PyObject *module, *apiobj; static void *c_api[PYGAMEAPI_EVENT_NUMSLOTS]; @@ -2339,7 +2339,7 @@ MODINIT_DEFINE(event) c_api[8] = pgEvent_GetMouseButtonDownInfo; c_api[9] = pgEvent_GetMouseButtonUpInfo; - apiobj = encapsulate_api(c_api, "event"); + apiobj = encapsulate_api(c_api, "_event"); if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { Py_XDECREF(apiobj); Py_DECREF(module); diff --git a/src_c/display.c b/src_c/display.c index f677bce3e3..4002a57493 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -194,7 +194,7 @@ pg_display_quit(PyObject *self, PyObject *_null) pg_SetDefaultWindow(NULL); } - pg_mod_autoquit(IMPPREFIX "event"); + pg_mod_autoquit(IMPPREFIX "_event"); pg_mod_autoquit(IMPPREFIX "time"); pg_mod_autoquit(IMPPREFIX "window"); @@ -250,7 +250,7 @@ pg_display_init(PyObject *self, PyObject *_null) if (!pg_mod_autoinit(IMPPREFIX "time")) return NULL; - if (!pg_mod_autoinit(IMPPREFIX "event")) + if (!pg_mod_autoinit(IMPPREFIX "_event")) return NULL; if (!pg_mod_autoinit(IMPPREFIX "window")) return NULL; diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 1477fe24cc..8851105083 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -388,34 +388,36 @@ typedef struct { typedef struct pgEventObject pgEventObject; #ifndef PYGAMEAPI_EVENT_INTERNAL -#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(event, 0)) +#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_event, 0)) #define pgEvent_Check(x) ((x)->ob_type == &pgEvent_Type) #define pgEvent_New \ - (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(event, 1)) + (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(_event, 1)) #define pg_post_event \ - (*(int (*)(Uint32, PyObject *))PYGAMEAPI_GET_SLOT(event, 2)) + (*(int (*)(Uint32, PyObject *))PYGAMEAPI_GET_SLOT(_event, 2)) #define pg_post_event_dictproxy \ - (*(int (*)(Uint32, pgEventDictProxy *))PYGAMEAPI_GET_SLOT(event, 3)) + (*(int (*)(Uint32, pgEventDictProxy *))PYGAMEAPI_GET_SLOT(_event, 3)) -#define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(event, 4)) +#define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(_event, 4)) -#define pg_GetKeyRepeat (*(void (*)(int *, int *))PYGAMEAPI_GET_SLOT(event, 5)) +#define pg_GetKeyRepeat \ + (*(void (*)(int *, int *))PYGAMEAPI_GET_SLOT(_event, 5)) -#define pgEvent_GetKeyDownInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 6)) +#define pgEvent_GetKeyDownInfo \ + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 6)) -#define pgEvent_GetKeyUpInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 7)) +#define pgEvent_GetKeyUpInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 7)) #define pgEvent_GetMouseButtonDownInfo \ - (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 8)) + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 8)) #define pgEvent_GetMouseButtonUpInfo \ - (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 9)) + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 9)) -#define import_pygame_event() IMPORT_PYGAME_MODULE(event) +#define import_pygame_event() IMPORT_PYGAME_MODULE(_event) #endif /* @@ -536,7 +538,7 @@ PYGAMEAPI_DEFINE_SLOTS(joystick); PYGAMEAPI_DEFINE_SLOTS(display); PYGAMEAPI_DEFINE_SLOTS(surface); PYGAMEAPI_DEFINE_SLOTS(surflock); -PYGAMEAPI_DEFINE_SLOTS(event); +PYGAMEAPI_DEFINE_SLOTS(_event); PYGAMEAPI_DEFINE_SLOTS(rwobject); PYGAMEAPI_DEFINE_SLOTS(pixelarray); PYGAMEAPI_DEFINE_SLOTS(color); @@ -550,7 +552,7 @@ PYGAMEAPI_EXTERN_SLOTS(joystick); PYGAMEAPI_EXTERN_SLOTS(display); PYGAMEAPI_EXTERN_SLOTS(surface); PYGAMEAPI_EXTERN_SLOTS(surflock); -PYGAMEAPI_EXTERN_SLOTS(event); +PYGAMEAPI_EXTERN_SLOTS(_event); PYGAMEAPI_EXTERN_SLOTS(rwobject); PYGAMEAPI_EXTERN_SLOTS(pixelarray); PYGAMEAPI_EXTERN_SLOTS(color); diff --git a/src_c/meson.build b/src_c/meson.build index 8e8287c834..2b8ff2e27d 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -35,9 +35,9 @@ display = py.extension_module( subdir: pg, ) -event = py.extension_module( - 'event', - 'event.c', +_event = py.extension_module( + '_event', + '_event.c', c_args: warnings_error, dependencies: pg_base_deps, install: true, diff --git a/src_c/static.c b/src_c/static.c index b7e8d1ec66..3042b58d44 100644 --- a/src_c/static.c +++ b/src_c/static.c @@ -115,7 +115,7 @@ PyInit_mouse(void); PyMODINIT_FUNC PyInit_key(void); PyMODINIT_FUNC -PyInit_event(void); +PyInit__event(void); PyMODINIT_FUNC PyInit_joystick(void); @@ -308,7 +308,7 @@ PyInit_pygame_static() load_submodule("pygame", PyInit_mask(), "mask"); load_submodule("pygame", PyInit_mouse(), "mouse"); - load_submodule("pygame", PyInit_event(), "event"); + load_submodule("pygame", PyInit__event(), "_event"); load_submodule("pygame", PyInit_joystick(), "joystick"); load_submodule("pygame", PyInit_pg_mixer(), "mixer"); @@ -393,7 +393,7 @@ PyInit_pygame_static() #include "joystick.c" -#include "event.c" +#include "_event.c" #include "mouse.c" diff --git a/src_py/event.py b/src_py/event.py new file mode 100644 index 0000000000..e1c5e04fd5 --- /dev/null +++ b/src_py/event.py @@ -0,0 +1 @@ +from pygame._event import * diff --git a/src_py/meson.build b/src_py/meson.build index 561aebaadb..28607b75a3 100644 --- a/src_py/meson.build +++ b/src_py/meson.build @@ -8,6 +8,7 @@ python_sources = files( 'camera.py', 'colordict.py', 'cursors.py', + 'event.py', 'freetype.py', 'ftfont.py', 'locals.py', From a604caa54142091f20762eed981b2af9057e8d89 Mon Sep 17 00:00:00 2001 From: gresm <78505251+gresm@users.noreply.github.com> Date: Mon, 17 Jun 2024 19:58:42 +0200 Subject: [PATCH 02/36] Just rename "event.c" to "_event.c". --- buildconfig/Setup.Android.SDL2.in | 2 +- buildconfig/Setup.Emscripten.SDL2.in | 2 +- buildconfig/Setup.SDL2.in | 2 +- src_c/{event.c => _event.c} | 8 ++++---- src_c/display.c | 4 ++-- src_c/include/_pygame.h | 28 +++++++++++++++------------- src_c/meson.build | 6 +++--- src_c/static.c | 6 +++--- src_py/event.py | 1 + src_py/meson.build | 1 + 10 files changed, 32 insertions(+), 28 deletions(-) rename src_c/{event.c => _event.c} (99%) create mode 100644 src_py/event.py diff --git a/buildconfig/Setup.Android.SDL2.in b/buildconfig/Setup.Android.SDL2.in index fc556ad024..5ef285a55e 100644 --- a/buildconfig/Setup.Android.SDL2.in +++ b/buildconfig/Setup.Android.SDL2.in @@ -43,7 +43,7 @@ base src_c/base.c $(SDL) $(DEBUG) color src_c/color.c $(SDL) $(DEBUG) constants src_c/constants.c $(SDL) $(DEBUG) display src_c/display.c $(SDL) $(DEBUG) -event src_c/event.c $(SDL) $(DEBUG) +_event src_c/_event.c $(SDL) $(DEBUG) key src_c/key.c $(SDL) $(DEBUG) mouse src_c/mouse.c $(SDL) $(DEBUG) rect src_c/rect.c src_c/pgcompat_rect.c $(SDL) $(DEBUG) diff --git a/buildconfig/Setup.Emscripten.SDL2.in b/buildconfig/Setup.Emscripten.SDL2.in index 23b22f5766..2912b6d3a3 100644 --- a/buildconfig/Setup.Emscripten.SDL2.in +++ b/buildconfig/Setup.Emscripten.SDL2.in @@ -46,7 +46,7 @@ color src_c/void.c controller_old src_c/void.c display src_c/void.c draw src_c/void.c -event src_c/void.c +_event src_c/void.c font src_c/void.c gfxdraw src_c/void.c joystick src_c/void.c diff --git a/buildconfig/Setup.SDL2.in b/buildconfig/Setup.SDL2.in index 2fd66eeea3..e789e029f5 100644 --- a/buildconfig/Setup.SDL2.in +++ b/buildconfig/Setup.SDL2.in @@ -53,7 +53,7 @@ base src_c/base.c $(SDL) $(DEBUG) color src_c/color.c $(SDL) $(DEBUG) constants src_c/constants.c $(SDL) $(DEBUG) display src_c/display.c $(SDL) $(DEBUG) -event src_c/event.c $(SDL) $(DEBUG) +_event src_c/_event.c $(SDL) $(DEBUG) key src_c/key.c $(SDL) $(DEBUG) mouse src_c/mouse.c $(SDL) $(DEBUG) rect src_c/rect.c src_c/pgcompat_rect.c $(SDL) $(DEBUG) diff --git a/src_c/event.c b/src_c/_event.c similarity index 99% rename from src_c/event.c rename to src_c/_event.c index 5a83df7adc..9f199f4c1f 100644 --- a/src_c/event.c +++ b/src_c/_event.c @@ -2240,9 +2240,9 @@ pg_event_custom_type(PyObject *self, PyObject *_null) } static PyMethodDef _event_methods[] = { - {"_internal_mod_init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, + {"init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, "auto initialize for event module"}, - {"_internal_mod_quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, + {"quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, "auto quit for event module"}, {"event_name", event_name, METH_VARARGS, DOC_EVENT_EVENTNAME}, @@ -2273,7 +2273,7 @@ static PyMethodDef _event_methods[] = { {NULL, NULL, 0, NULL}}; -MODINIT_DEFINE(event) +MODINIT_DEFINE(_event) { PyObject *module, *apiobj; static void *c_api[PYGAMEAPI_EVENT_NUMSLOTS]; @@ -2338,7 +2338,7 @@ MODINIT_DEFINE(event) c_api[8] = pgEvent_GetMouseButtonDownInfo; c_api[9] = pgEvent_GetMouseButtonUpInfo; - apiobj = encapsulate_api(c_api, "event"); + apiobj = encapsulate_api(c_api, "_event"); if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { Py_XDECREF(apiobj); Py_DECREF(module); diff --git a/src_c/display.c b/src_c/display.c index ff14e8ad5f..c7d9ab8746 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -184,7 +184,7 @@ pg_display_quit(PyObject *self, PyObject *_null) pg_SetDefaultWindow(NULL); } - pg_mod_autoquit(IMPPREFIX "event"); + pg_mod_autoquit(IMPPREFIX "_event"); pg_mod_autoquit(IMPPREFIX "time"); pg_mod_autoquit(IMPPREFIX "window"); @@ -240,7 +240,7 @@ pg_display_init(PyObject *self, PyObject *_null) if (!pg_mod_autoinit(IMPPREFIX "time")) return NULL; - if (!pg_mod_autoinit(IMPPREFIX "event")) + if (!pg_mod_autoinit(IMPPREFIX "_event")) return NULL; if (!pg_mod_autoinit(IMPPREFIX "window")) return NULL; diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index e1510b7756..77a01981c5 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -381,34 +381,36 @@ typedef struct { typedef struct pgEventObject pgEventObject; #ifndef PYGAMEAPI_EVENT_INTERNAL -#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(event, 0)) +#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_event, 0)) #define pgEvent_Check(x) ((x)->ob_type == &pgEvent_Type) #define pgEvent_New \ - (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(event, 1)) + (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(_event, 1)) #define pg_post_event \ - (*(int (*)(Uint32, PyObject *))PYGAMEAPI_GET_SLOT(event, 2)) + (*(int (*)(Uint32, PyObject *))PYGAMEAPI_GET_SLOT(_event, 2)) #define pg_post_event_dictproxy \ - (*(int (*)(Uint32, pgEventDictProxy *))PYGAMEAPI_GET_SLOT(event, 3)) + (*(int (*)(Uint32, pgEventDictProxy *))PYGAMEAPI_GET_SLOT(_event, 3)) -#define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(event, 4)) +#define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(_event, 4)) -#define pg_GetKeyRepeat (*(void (*)(int *, int *))PYGAMEAPI_GET_SLOT(event, 5)) +#define pg_GetKeyRepeat \ + (*(void (*)(int *, int *))PYGAMEAPI_GET_SLOT(_event, 5)) -#define pgEvent_GetKeyDownInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 6)) +#define pgEvent_GetKeyDownInfo \ + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 6)) -#define pgEvent_GetKeyUpInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 7)) +#define pgEvent_GetKeyUpInfo (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 7)) #define pgEvent_GetMouseButtonDownInfo \ - (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 8)) + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 8)) #define pgEvent_GetMouseButtonUpInfo \ - (*(char *(*)(void))PYGAMEAPI_GET_SLOT(event, 9)) + (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 9)) -#define import_pygame_event() IMPORT_PYGAME_MODULE(event) +#define import_pygame_event() IMPORT_PYGAME_MODULE(_event) #endif /* @@ -529,7 +531,7 @@ PYGAMEAPI_DEFINE_SLOTS(joystick); PYGAMEAPI_DEFINE_SLOTS(display); PYGAMEAPI_DEFINE_SLOTS(surface); PYGAMEAPI_DEFINE_SLOTS(surflock); -PYGAMEAPI_DEFINE_SLOTS(event); +PYGAMEAPI_DEFINE_SLOTS(_event); PYGAMEAPI_DEFINE_SLOTS(rwobject); PYGAMEAPI_DEFINE_SLOTS(pixelarray); PYGAMEAPI_DEFINE_SLOTS(color); @@ -543,7 +545,7 @@ PYGAMEAPI_EXTERN_SLOTS(joystick); PYGAMEAPI_EXTERN_SLOTS(display); PYGAMEAPI_EXTERN_SLOTS(surface); PYGAMEAPI_EXTERN_SLOTS(surflock); -PYGAMEAPI_EXTERN_SLOTS(event); +PYGAMEAPI_EXTERN_SLOTS(_event); PYGAMEAPI_EXTERN_SLOTS(rwobject); PYGAMEAPI_EXTERN_SLOTS(pixelarray); PYGAMEAPI_EXTERN_SLOTS(color); diff --git a/src_c/meson.build b/src_c/meson.build index 8e8287c834..2b8ff2e27d 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -35,9 +35,9 @@ display = py.extension_module( subdir: pg, ) -event = py.extension_module( - 'event', - 'event.c', +_event = py.extension_module( + '_event', + '_event.c', c_args: warnings_error, dependencies: pg_base_deps, install: true, diff --git a/src_c/static.c b/src_c/static.c index c8929de6e7..6d220fc54d 100644 --- a/src_c/static.c +++ b/src_c/static.c @@ -115,7 +115,7 @@ PyInit_mouse(void); PyMODINIT_FUNC PyInit_key(void); PyMODINIT_FUNC -PyInit_event(void); +PyInit__event(void); PyMODINIT_FUNC PyInit_joystick(void); @@ -308,7 +308,7 @@ PyInit_pygame_static() load_submodule("pygame", PyInit_mask(), "mask"); load_submodule("pygame", PyInit_mouse(), "mouse"); - load_submodule("pygame", PyInit_event(), "event"); + load_submodule("pygame", PyInit__event(), "_event"); load_submodule("pygame", PyInit_joystick(), "joystick"); load_submodule("pygame", PyInit_pg_mixer(), "mixer"); @@ -391,7 +391,7 @@ PyInit_pygame_static() #include "joystick.c" -#include "event.c" +#include "_event.c" #include "mouse.c" diff --git a/src_py/event.py b/src_py/event.py new file mode 100644 index 0000000000..e1c5e04fd5 --- /dev/null +++ b/src_py/event.py @@ -0,0 +1 @@ +from pygame._event import * diff --git a/src_py/meson.build b/src_py/meson.build index 561aebaadb..28607b75a3 100644 --- a/src_py/meson.build +++ b/src_py/meson.build @@ -8,6 +8,7 @@ python_sources = files( 'camera.py', 'colordict.py', 'cursors.py', + 'event.py', 'freetype.py', 'ftfont.py', 'locals.py', From 3009fe13690678d3d455ff7070d19275dee4e771 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:50:25 +0200 Subject: [PATCH 03/36] Move custom_type() to python. --- buildconfig/stubs/pygame/_event.pyi | 47 +++++++++++++++++++++++++++++ src_c/_event.c | 26 ++-------------- src_py/event.py | 37 ++++++++++++++++++++++- 3 files changed, 85 insertions(+), 25 deletions(-) create mode 100644 buildconfig/stubs/pygame/_event.pyi diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi new file mode 100644 index 0000000000..dc9a1c852f --- /dev/null +++ b/buildconfig/stubs/pygame/_event.pyi @@ -0,0 +1,47 @@ +from typing import ( + Any, + Dict, + List, + Optional, + Union, + final, +) + +from ._common import Sequence + +@final +class Event: + type: int + dict: Dict[str, Any] + __dict__: Dict[str, Any] + __hash__: None # type: ignore + def __init__( + self, type: int, dict: Dict[str, Any] = ..., **kwargs: Any + ) -> None: ... + def __getattribute__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + def __delattr__(self, name: str) -> None: ... + def __bool__(self) -> bool: ... + +_EventTypes = Union[int, Sequence[int]] + +def pump() -> None: ... +def get( + eventtype: Optional[_EventTypes] = None, + pump: Any = True, + exclude: Optional[_EventTypes] = None, +) -> List[Event]: ... +def poll() -> Event: ... +def wait(timeout: int = 0) -> Event: ... +def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... +def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... +def event_name(type: int, /) -> str: ... +def set_blocked(type: Optional[_EventTypes], /) -> None: ... +def set_allowed(type: Optional[_EventTypes], /) -> None: ... +def get_blocked(type: _EventTypes, /) -> bool: ... +def set_grab(grab: bool, /) -> None: ... +def get_grab() -> bool: ... +def post(event: Event, /) -> bool: ... +def custom_type() -> int: ... + +EventType = Event diff --git a/src_c/_event.c b/src_c/_event.c index 9f199f4c1f..7e977a9930 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -49,11 +49,6 @@ #define PG_GET_LIST_LEN 128 -/* _custom_event stores the next custom user event type that will be - * returned by pygame.event.custom_type() */ -#define _PGE_CUSTOM_EVENT_INIT PGE_USEREVENT + 1 - -static int _custom_event = _PGE_CUSTOM_EVENT_INIT; static int _pg_event_is_init = 0; /* Length of our unicode string in bytes. We need 1 to 3 bytes to store @@ -638,11 +633,6 @@ pgEvent_AutoQuit(PyObject *self, PyObject *_null) _pg_repeat_timer = 0; } PG_UNLOCK_EVFILTER_MUTEX - /* The main reason for _custom_event to be reset here is so we - * can have a unit test that checks if pygame.event.custom_type() - * stops returning new types when they are finished, without that - * test preventing further tests from getting a custom event type.*/ - _custom_event = _PGE_CUSTOM_EVENT_INIT; } _pg_event_is_init = 0; Py_RETURN_NONE; @@ -2229,20 +2219,10 @@ pg_event_get_blocked(PyObject *self, PyObject *obj) return PyBool_FromLong(isblocked); } -static PyObject * -pg_event_custom_type(PyObject *self, PyObject *_null) -{ - if (_custom_event < PG_NUMEVENTS) - return PyLong_FromLong(_custom_event++); - else - return RAISE(pgExc_SDLError, - "pygame.event.custom_type made too many event types."); -} - static PyMethodDef _event_methods[] = { - {"init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, + {"_internal_mod_init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, "auto initialize for event module"}, - {"quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, + {"_internal_mod_quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, "auto quit for event module"}, {"event_name", event_name, METH_VARARGS, DOC_EVENT_EVENTNAME}, @@ -2268,8 +2248,6 @@ static PyMethodDef _event_methods[] = { DOC_EVENT_SETBLOCKED}, {"get_blocked", (PyCFunction)pg_event_get_blocked, METH_O, DOC_EVENT_GETBLOCKED}, - {"custom_type", (PyCFunction)pg_event_custom_type, METH_NOARGS, - DOC_EVENT_CUSTOMTYPE}, {NULL, NULL, 0, NULL}}; diff --git a/src_py/event.py b/src_py/event.py index e1c5e04fd5..f4ecbda2bc 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1 +1,36 @@ -from pygame._event import * +from pygame._event import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame.constants import USEREVENT, NUMEVENTS +from pygame.base import error + + +_is_init = False +_custom_event = USEREVENT + 1 + + +def init(): + global _is_init + + _internal_mod_init() + + _is_init = True + + +def quit(): + global _is_init, _custom_event + + # The main reason for _custom_event to be reset here is so we can have a unit test that checks if pygame.event.custom_type() stops returning new types when they are finished, without that test preventing further tests from getting a custom event type. + _custom_event = USEREVENT + 1 + _internal_mod_quit() + + _is_init = False + + +def custom_type(): + """custom_type() -> int\nmake custom user event type""" + global _custom_event + + if _custom_event < NUMEVENTS: + _custom_event += 1 + return _custom_event - 1 + else: + raise error("pygame.event.custom_type made too many event types.") From 549805e7cb4a17d25af62e85b0777a0373ea4e74 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:05:46 +0200 Subject: [PATCH 04/36] Bugfix --- buildconfig/stubs/pygame/_event.pyi | 1 - src_c/display.c | 4 ++-- src_py/event.py | 5 +++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index dc9a1c852f..f684f3f61e 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -42,6 +42,5 @@ def get_blocked(type: _EventTypes, /) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def post(event: Event, /) -> bool: ... -def custom_type() -> int: ... EventType = Event diff --git a/src_c/display.c b/src_c/display.c index c7d9ab8746..ff14e8ad5f 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -184,7 +184,7 @@ pg_display_quit(PyObject *self, PyObject *_null) pg_SetDefaultWindow(NULL); } - pg_mod_autoquit(IMPPREFIX "_event"); + pg_mod_autoquit(IMPPREFIX "event"); pg_mod_autoquit(IMPPREFIX "time"); pg_mod_autoquit(IMPPREFIX "window"); @@ -240,7 +240,7 @@ pg_display_init(PyObject *self, PyObject *_null) if (!pg_mod_autoinit(IMPPREFIX "time")) return NULL; - if (!pg_mod_autoinit(IMPPREFIX "_event")) + if (!pg_mod_autoinit(IMPPREFIX "event")) return NULL; if (!pg_mod_autoinit(IMPPREFIX "window")) return NULL; diff --git a/src_py/event.py b/src_py/event.py index f4ecbda2bc..7d18dfeb11 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1,4 +1,5 @@ from pygame._event import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame._event import _internal_mod_init as _init, _internal_mod_quit as _quit from pygame.constants import USEREVENT, NUMEVENTS from pygame.base import error @@ -10,7 +11,7 @@ def init(): global _is_init - _internal_mod_init() + _init() _is_init = True @@ -20,7 +21,7 @@ def quit(): # The main reason for _custom_event to be reset here is so we can have a unit test that checks if pygame.event.custom_type() stops returning new types when they are finished, without that test preventing further tests from getting a custom event type. _custom_event = USEREVENT + 1 - _internal_mod_quit() + _quit() _is_init = False From f333b6ee89424adda7a48afe9c67e5c7403956d8 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:02:43 +0200 Subject: [PATCH 05/36] Move event_name() to python. --- buildconfig/stubs/pygame/_event.pyi | 1 - src_c/_event.c | 12 ---- src_c/base.c | 4 ++ src_py/event.py | 92 ++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 14 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index f684f3f61e..9b51f700dd 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -35,7 +35,6 @@ def poll() -> Event: ... def wait(timeout: int = 0) -> Event: ... def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... -def event_name(type: int, /) -> str: ... def set_blocked(type: Optional[_EventTypes], /) -> None: ... def set_allowed(type: Optional[_EventTypes], /) -> None: ... def get_blocked(type: _EventTypes, /) -> bool: ... diff --git a/src_c/_event.c b/src_c/_event.c index 7e977a9930..f1e5d1289e 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1544,16 +1544,6 @@ pgEvent_New(SDL_Event *event) /* event module functions */ -static PyObject * -event_name(PyObject *self, PyObject *arg) -{ - int type; - if (!PyArg_ParseTuple(arg, "i", &type)) - return NULL; - - return PyUnicode_FromString(_pg_name_from_eventtype(type)); -} - static PyObject * set_grab(PyObject *self, PyObject *arg) { @@ -2225,8 +2215,6 @@ static PyMethodDef _event_methods[] = { {"_internal_mod_quit", (PyCFunction)pgEvent_AutoQuit, METH_NOARGS, "auto quit for event module"}, - {"event_name", event_name, METH_VARARGS, DOC_EVENT_EVENTNAME}, - {"set_grab", set_grab, METH_O, DOC_EVENT_SETGRAB}, {"get_grab", (PyCFunction)get_grab, METH_NOARGS, DOC_EVENT_GETGRAB}, diff --git a/src_c/base.c b/src_c/base.c index 2a609e6601..03f58868e7 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -296,6 +296,10 @@ pg_mod_autoquit(const char *modname) funcobj = PyObject_GetAttrString(module, "_internal_mod_quit"); + /* Silence errors */ + if (PyErr_Occurred()) + PyErr_Clear(); + /* If we could not load _internal_mod_quit, load quit function */ if (!funcobj) funcobj = PyObject_GetAttrString(module, "quit"); diff --git a/src_py/event.py b/src_py/event.py index 7d18dfeb11..2205c06f14 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -6,6 +6,93 @@ _is_init = False _custom_event = USEREVENT + 1 +_NAMES_MAPPING = { + 32768: "ActiveEvent", + 257: "AppTerminating", + 258: "AppLowMemory", + 259: "AppWillEnterBackground", + 260: "AppDidEnterBackground", + 261: "AppWillEnterForeground", + 262: "AppDidEnterForeground", + 2304: "ClipboardUpdate", + 768: "KeyDown", + 769: "KeyUp", + 772: "KeyMapChanged", + 263: "LocaleChanged", + 1024: "MouseMotion", + 1025: "MouseButtonDown", + 1026: "MouseButtonUp", + 1536: "JoyAxisMotion", + 1537: "JoyBallMotion", + 1538: "JoyHatMotion", + 1540: "JoyButtonUp", + 1539: "JoyButtonDown", + 256: "Quit", + 513: "SysWMEvent", + 32769: "VideoResize", + 32770: "VideoExpose", + 32771: "MidiIn", + 32772: "MidiOut", + 0: "NoEvent", + 1794: "FingerMotion", + 1792: "FingerDown", + 1793: "FingerUp", + 2050: "MultiGesture", + 1027: "MouseWheel", + 771: "TextInput", + 770: "TextEditing", + 4096: "DropFile", + 4097: "DropText", + 4098: "DropBegin", + 4099: "DropComplete", + 1616: "ControllerAxisMotion", + 1617: "ControllerButtonDown", + 1618: "ControllerButtonUp", + 1619: "ControllerDeviceAdded", + 1620: "ControllerDeviceRemoved", + 1621: "ControllerDeviceMapped", + 1541: "JoyDeviceAdded", + 1542: "JoyDeviceRemoved", + 1622: "ControllerTouchpadDown", + 1623: "ControllerTouchpadMotion", + 1624: "ControllerTouchpadUp", + 1625: "ControllerSensorUpdate", + 4352: "AudioDeviceAdded", + 4353: "AudioDeviceRemoved", + 8192: "RenderTargetsReset", + 8193: "RenderDeviceReset", + 32774: "WindowShown", + 32775: "WindowHidden", + 32776: "WindowExposed", + 32777: "WindowMoved", + 32778: "WindowResized", + 32779: "WindowSizeChanged", + 32780: "WindowMinimized", + 32781: "WindowMaximized", + 32782: "WindowRestored", + 32783: "WindowEnter", + 32784: "WindowLeave", + 32785: "WindowFocusGained", + 32786: "WindowFocusLost", + 32787: "WindowClose", + 32788: "WindowTakeFocus", + 32789: "WindowHitTest", + 32790: "WindowICCProfChanged", + 32791: "WindowDisplayChanged", +} + + +def event_name(type: int): + if type in _NAMES_MAPPING: + return _NAMES_MAPPING[type] + elif type >= USEREVENT and type < NUMEVENTS: + return "UserEvent" + return "Unknown" + + +# class Event: +# def __init__(self, type: int): +# self.type = type def init(): @@ -19,7 +106,10 @@ def init(): def quit(): global _is_init, _custom_event - # The main reason for _custom_event to be reset here is so we can have a unit test that checks if pygame.event.custom_type() stops returning new types when they are finished, without that test preventing further tests from getting a custom event type. + # The main reason for _custom_event to be reset here is + # so we can have a unit test that checks if pygame.event.custom_type() stops + # returning new types when they are finished, + # without that test preventing further tests from getting a custom event type. _custom_event = USEREVENT + 1 _quit() From 8485bdd25efb2ecbf6aabd940c46ceceda566ab7 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 17 Jul 2024 13:31:49 +0200 Subject: [PATCH 06/36] Update _NAMES_MAPPING --- buildconfig/stubs/pygame/event.pyi | 4 +- src_py/event.py | 157 +++++++++++++++-------------- 2 files changed, 82 insertions(+), 79 deletions(-) diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index dc9a1c852f..9f4bd597ac 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -35,7 +35,7 @@ def poll() -> Event: ... def wait(timeout: int = 0) -> Event: ... def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... -def event_name(type: int, /) -> str: ... +def event_name(type: int) -> str: ... def set_blocked(type: Optional[_EventTypes], /) -> None: ... def set_allowed(type: Optional[_EventTypes], /) -> None: ... def get_blocked(type: _EventTypes, /) -> bool: ... @@ -43,5 +43,7 @@ def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def post(event: Event, /) -> bool: ... def custom_type() -> int: ... +def init(): ... +def quit(): ... EventType = Event diff --git a/src_py/event.py b/src_py/event.py index 2205c06f14..36f6b9522f 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -2,90 +2,91 @@ from pygame._event import _internal_mod_init as _init, _internal_mod_quit as _quit from pygame.constants import USEREVENT, NUMEVENTS from pygame.base import error +import pygame as pg _is_init = False _custom_event = USEREVENT + 1 _NAMES_MAPPING = { - 32768: "ActiveEvent", - 257: "AppTerminating", - 258: "AppLowMemory", - 259: "AppWillEnterBackground", - 260: "AppDidEnterBackground", - 261: "AppWillEnterForeground", - 262: "AppDidEnterForeground", - 2304: "ClipboardUpdate", - 768: "KeyDown", - 769: "KeyUp", - 772: "KeyMapChanged", - 263: "LocaleChanged", - 1024: "MouseMotion", - 1025: "MouseButtonDown", - 1026: "MouseButtonUp", - 1536: "JoyAxisMotion", - 1537: "JoyBallMotion", - 1538: "JoyHatMotion", - 1540: "JoyButtonUp", - 1539: "JoyButtonDown", - 256: "Quit", - 513: "SysWMEvent", - 32769: "VideoResize", - 32770: "VideoExpose", - 32771: "MidiIn", - 32772: "MidiOut", - 0: "NoEvent", - 1794: "FingerMotion", - 1792: "FingerDown", - 1793: "FingerUp", - 2050: "MultiGesture", - 1027: "MouseWheel", - 771: "TextInput", - 770: "TextEditing", - 4096: "DropFile", - 4097: "DropText", - 4098: "DropBegin", - 4099: "DropComplete", - 1616: "ControllerAxisMotion", - 1617: "ControllerButtonDown", - 1618: "ControllerButtonUp", - 1619: "ControllerDeviceAdded", - 1620: "ControllerDeviceRemoved", - 1621: "ControllerDeviceMapped", - 1541: "JoyDeviceAdded", - 1542: "JoyDeviceRemoved", - 1622: "ControllerTouchpadDown", - 1623: "ControllerTouchpadMotion", - 1624: "ControllerTouchpadUp", - 1625: "ControllerSensorUpdate", - 4352: "AudioDeviceAdded", - 4353: "AudioDeviceRemoved", - 8192: "RenderTargetsReset", - 8193: "RenderDeviceReset", - 32774: "WindowShown", - 32775: "WindowHidden", - 32776: "WindowExposed", - 32777: "WindowMoved", - 32778: "WindowResized", - 32779: "WindowSizeChanged", - 32780: "WindowMinimized", - 32781: "WindowMaximized", - 32782: "WindowRestored", - 32783: "WindowEnter", - 32784: "WindowLeave", - 32785: "WindowFocusGained", - 32786: "WindowFocusLost", - 32787: "WindowClose", - 32788: "WindowTakeFocus", - 32789: "WindowHitTest", - 32790: "WindowICCProfChanged", - 32791: "WindowDisplayChanged", + pg.ACTIVEEVENT: "ActiveEvent", + pg.APP_TERMINATING: "AppTerminating", + pg.APP_LOWMEMORY: "AppLowMemory", + pg.APP_WILLENTERBACKGROUND: "AppWillEnterBackground", + pg.APP_DIDENTERBACKGROUND: "AppDidEnterBackground", + pg.APP_WILLENTERFOREGROUND: "AppWillEnterForeground", + pg.APP_DIDENTERFOREGROUND: "AppDidEnterForeground", + pg.CLIPBOARDUPDATE: "ClipboardUpdate", + pg.KEYDOWN: "KeyDown", + pg.KEYUP: "KeyUp", + pg.KEYMAPCHANGED: "KeyMapChanged", + pg.LOCALECHANGED: "LocaleChanged", + pg.MOUSEMOTION: "MouseMotion", + pg.MOUSEBUTTONDOWN: "MouseButtonDown", + pg.MOUSEBUTTONUP: "MouseButtonUp", + pg.JOYAXISMOTION: "JoyAxisMotion", + pg.JOYBALLMOTION: "JoyBallMotion", + pg.JOYHATMOTION: "JoyHatMotion", + pg.JOYBUTTONUP: "JoyButtonUp", + pg.JOYBUTTONDOWN: "JoyButtonDown", + pg.QUIT: "Quit", + pg.SYSWMEVENT: "SysWMEvent", + pg.VIDEORESIZE: "VideoResize", + pg.VIDEOEXPOSE: "VideoExpose", + pg.MIDIIN: "MidiIn", + pg.MIDIOUT: "MidiOut", + pg.NOEVENT: "NoEvent", + pg.FINGERMOTION: "FingerMotion", + pg.FINGERDOWN: "FingerDown", + pg.FINGERUP: "FingerUp", + pg.MULTIGESTURE: "MultiGesture", + pg.MOUSEWHEEL: "MouseWheel", + pg.TEXTINPUT: "TextInput", + pg.TEXTEDITING: "TextEditing", + pg.DROPFILE: "DropFile", + pg.DROPTEXT: "DropText", + pg.DROPBEGIN: "DropBegin", + pg.DROPCOMPLETE: "DropComplete", + pg.CONTROLLERAXISMOTION: "ControllerAxisMotion", + pg.CONTROLLERBUTTONDOWN: "ControllerButtonDown", + pg.CONTROLLERBUTTONUP: "ControllerButtonUp", + pg.CONTROLLERDEVICEADDED: "ControllerDeviceAdded", + pg.CONTROLLERDEVICEREMOVED: "ControllerDeviceRemoved", + pg.CONTROLLERDEVICEREMAPPED: "ControllerDeviceMapped", + pg.JOYDEVICEADDED: "JoyDeviceAdded", + pg.JOYDEVICEREMOVED: "JoyDeviceRemoved", + pg.CONTROLLERTOUCHPADDOWN: "ControllerTouchpadDown", + pg.CONTROLLERTOUCHPADMOTION: "ControllerTouchpadMotion", + pg.CONTROLLERTOUCHPADUP: "ControllerTouchpadUp", + pg.CONTROLLERSENSORUPDATE: "ControllerSensorUpdate", + pg.AUDIODEVICEADDED: "AudioDeviceAdded", + pg.AUDIODEVICEREMOVED: "AudioDeviceRemoved", + pg.RENDER_TARGETS_RESET: "RenderTargetsReset", + pg.RENDER_DEVICE_RESET: "RenderDeviceReset", + pg.WINDOWSHOWN: "WindowShown", + pg.WINDOWHIDDEN: "WindowHidden", + pg.WINDOWEXPOSED: "WindowExposed", + pg.WINDOWMOVED: "WindowMoved", + pg.WINDOWRESIZED: "WindowResized", + pg.WINDOWSIZECHANGED: "WindowSizeChanged", + pg.WINDOWMINIMIZED: "WindowMinimized", + pg.WINDOWMAXIMIZED: "WindowMaximized", + pg.WINDOWRESTORED: "WindowRestored", + pg.WINDOWENTER: "WindowEnter", + pg.WINDOWLEAVE: "WindowLeave", + pg.WINDOWFOCUSGAINED: "WindowFocusGained", + pg.WINDOWFOCUSLOST: "WindowFocusLost", + pg.WINDOWCLOSE: "WindowClose", + pg.WINDOWTAKEFOCUS: "WindowTakeFocus", + pg.WINDOWHITTEST: "WindowHitTest", + pg.WINDOWICCPROFCHANGED: "WindowICCProfChanged", + pg.WINDOWDISPLAYCHANGED: "WindowDisplayChanged", } -def event_name(type: int): +def event_name(type: int) -> str: if type in _NAMES_MAPPING: return _NAMES_MAPPING[type] - elif type >= USEREVENT and type < NUMEVENTS: + elif USEREVENT <= type < NUMEVENTS: return "UserEvent" return "Unknown" @@ -120,8 +121,8 @@ def custom_type(): """custom_type() -> int\nmake custom user event type""" global _custom_event - if _custom_event < NUMEVENTS: - _custom_event += 1 - return _custom_event - 1 - else: + if _custom_event >= NUMEVENTS: raise error("pygame.event.custom_type made too many event types.") + + _custom_event += 1 + return _custom_event - 1 From 1b10fd9207d9f3a3432a41f7bd6794bb6fe8ce15 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Thu, 18 Jul 2024 21:06:42 +0200 Subject: [PATCH 07/36] Moving Event() to python, likely not working yet. --- buildconfig/stubs/pygame/_event.pyi | 19 +- src_c/_event.c | 325 ++++++++++------------------ src_c/_pygame.h | 6 +- src_c/include/_pygame.h | 17 +- src_c/time.c | 26 ++- src_py/event.py | 44 +++- 6 files changed, 185 insertions(+), 252 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index 9b51f700dd..237557f21a 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -1,28 +1,12 @@ from typing import ( Any, - Dict, List, Optional, Union, - final, ) from ._common import Sequence -@final -class Event: - type: int - dict: Dict[str, Any] - __dict__: Dict[str, Any] - __hash__: None # type: ignore - def __init__( - self, type: int, dict: Dict[str, Any] = ..., **kwargs: Any - ) -> None: ... - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - def __bool__(self) -> bool: ... - _EventTypes = Union[int, Sequence[int]] def pump() -> None: ... @@ -41,5 +25,4 @@ def get_blocked(type: _EventTypes, /) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def post(event: Event, /) -> bool: ... - -EventType = Event +def register_event_class(cls: type): ... diff --git a/src_c/_event.c b/src_c/_event.c index f1e5d1289e..b14262e405 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -93,6 +93,8 @@ static char released_keys[SDL_NUM_SCANCODES] = {0}; static char pressed_mouse_buttons[5] = {0}; static char released_mouse_buttons[5] = {0}; +static PyObject *_event_class = NULL; + #ifdef __EMSCRIPTEN__ /* these macros are no-op here */ #define PG_LOCK_EVFILTER_MUTEX @@ -1328,218 +1330,101 @@ dict_from_event(SDL_Event *event) return dict; } -/* event object internals */ - -static void -pg_event_dealloc(PyObject *self) +static PyObject * +pgEvent_GetType(void) { - pgEventObject *e = (pgEventObject *)self; - Py_XDECREF(e->dict); - Py_TYPE(self)->tp_free(self); -} + if (!_event_class) + return RAISE(PyExc_RuntimeError, "event type is currently unknown"); -#ifdef PYPY_VERSION -/* Because pypy does not work with the __dict__ tp_dictoffset. */ -PyObject * -pg_EventGetAttr(PyObject *o, PyObject *attr_name) -{ - /* Try e->dict first, if not try the generic attribute. */ - PyObject *result = PyDict_GetItem(((pgEventObject *)o)->dict, attr_name); - if (!result) { - return PyObject_GenericGetAttr(o, attr_name); - } - return result; + Py_INCREF(_event_class); + return _event_class; } -int -pg_EventSetAttr(PyObject *o, PyObject *name, PyObject *value) +static PyObject * +pgEvent_FromEventData(pgEventData e_data) { - /* if the variable is in the dict, deal with it there. - else if it's a normal attribute set it there. - else if it's not an attribute, or in the dict, set it in the dict. - */ - int dictResult; - int setInDict = 0; - PyObject *result = PyDict_GetItem(((pgEventObject *)o)->dict, name); + PyObject *ret = NULL; + PyObject *args = NULL; - if (result) { - setInDict = 1; - } - else { - result = PyObject_GenericGetAttr(o, name); - if (!result) { - PyErr_Clear(); - setInDict = 1; - } - } + PyObject *e_type = pgEvent_GetType(); + if (!e_type) + return NULL; - if (setInDict) { - dictResult = PyDict_SetItem(((pgEventObject *)o)->dict, name, value); - if (dictResult) { - return -1; - } - return 0; - } - else { - return PyObject_GenericSetAttr(o, name, value); + PyObject *num = PyLong_FromLong(e_data.type); + if (!num) + goto finalize; + + args = PyTuple_New(1); + if (!args) { + Py_DECREF(num); + goto finalize; } -} -#endif -PyObject * -pg_event_str(PyObject *self) -{ - pgEventObject *e = (pgEventObject *)self; - return PyUnicode_FromFormat("", e->type, - _pg_name_from_eventtype(e->type), e->dict); -} + PyTuple_SetItem(args, 0, num); + ret = PyObject_Call(e_type, args, e_data.dict); -static int -_pg_event_nonzero(pgEventObject *self) -{ - return self->type != SDL_NOEVENT; +finalize: + Py_DECREF(e_type); + Py_XDECREF(args); + return ret; } -static PyNumberMethods pg_event_as_number = { - .nb_bool = (inquiry)_pg_event_nonzero, -}; - -static PyTypeObject pgEvent_Type; -#define pgEvent_Check(x) ((x)->ob_type == &pgEvent_Type) -#define OFF(x) offsetof(pgEventObject, x) - -static PyMemberDef pg_event_members[] = { - {"__dict__", T_OBJECT, OFF(dict), READONLY}, - {"type", T_INT, OFF(type), READONLY}, - {"dict", T_OBJECT, OFF(dict), READONLY}, - {NULL} /* Sentinel */ -}; - -/* - * eventA == eventB - * eventA != eventB - */ -static PyObject * -pg_event_richcompare(PyObject *o1, PyObject *o2, int opid) +static pgEventData +pgEvent_GetEventData(PyObject *event) { - pgEventObject *e1, *e2; - - if (!pgEvent_Check(o1) || !pgEvent_Check(o2)) { - goto Unimplemented; - } - - e1 = (pgEventObject *)o1; - e2 = (pgEventObject *)o2; - switch (opid) { - case Py_EQ: - return PyBool_FromLong( - e1->type == e2->type && - PyObject_RichCompareBool(e1->dict, e2->dict, Py_EQ) == 1); - case Py_NE: - return PyBool_FromLong( - e1->type != e2->type || - PyObject_RichCompareBool(e1->dict, e2->dict, Py_NE) == 1); - default: - break; - } + pgEventData data = {0}; + data.dict = PyObject_GetAttrString(event, "dict"); -Unimplemented: - Py_INCREF(Py_NotImplemented); - return Py_NotImplemented; -} + if (PyErr_Occurred()) + PyErr_Clear(); -static int -pg_event_init(pgEventObject *self, PyObject *args, PyObject *kwargs) -{ - int type; - PyObject *dict = NULL; - - if (!PyArg_ParseTuple(args, "i|O!", &type, &PyDict_Type, &dict)) { - return -1; + PyObject *e_typeo = PyObject_GetAttrString(event, "type"); + if (!e_typeo) { + Py_XDECREF(data.dict); + data.dict = NULL; + goto finalize; } - if (type < 0 || type >= PG_NUMEVENTS) { - PyErr_SetString(PyExc_ValueError, "event type out of range"); - return -1; - } + data.type = PyLong_AsLong(e_typeo); - if (!dict) { - if (kwargs) { - dict = kwargs; - Py_INCREF(dict); - } - else { - dict = PyDict_New(); - if (!dict) { - PyErr_NoMemory(); - return -1; - } - } - } - else { - if (kwargs) { - if (PyDict_Update(dict, kwargs) == -1) { - return -1; - } - } - /* So that dict is a new reference */ - Py_INCREF(dict); - } - - if (PyDict_GetItemString(dict, "type")) { - PyErr_SetString(PyExc_ValueError, - "redundant type field in event dict"); - Py_DECREF(dict); - return -1; - } - - self->type = _pg_pgevent_deproxify(type); - self->dict = dict; - return 0; +finalize: + return data; } -static PyTypeObject pgEvent_Type = { - PyVarObject_HEAD_INIT(NULL, 0).tp_name = "pygame.event.Event", - .tp_basicsize = sizeof(pgEventObject), - .tp_dealloc = pg_event_dealloc, - .tp_repr = pg_event_str, - .tp_as_number = &pg_event_as_number, -#ifdef PYPY_VERSION - .tp_getattro = pg_EventGetAttr, - .tp_setattro = pg_EventSetAttr, -#else - .tp_getattro = PyObject_GenericGetAttr, - .tp_setattro = PyObject_GenericSetAttr, -#endif - .tp_doc = DOC_EVENT_EVENT, - .tp_richcompare = pg_event_richcompare, - .tp_members = pg_event_members, - .tp_dictoffset = offsetof(pgEventObject, dict), - .tp_init = (initproc)pg_event_init, - .tp_new = PyType_GenericNew, -}; +static void +pgEvent_FreeEventData(pgEventData e_data) +{ + Py_XDECREF(e_data.dict); + e_data.dict = NULL; +} static PyObject * pgEvent_New(SDL_Event *event) { - pgEventObject *e; - e = PyObject_New(pgEventObject, &pgEvent_Type); - if (!e) - return PyErr_NoMemory(); + pgEventData e = {0}; if (event) { - e->type = _pg_pgevent_deproxify(event->type); - e->dict = dict_from_event(event); + e.type = _pg_pgevent_deproxify(event->type); + e.dict = dict_from_event(event); } else { - e->type = SDL_NOEVENT; - e->dict = PyDict_New(); - } - if (!e->dict) { - Py_TYPE(e)->tp_free(e); - return PyErr_NoMemory(); + e.type = SDL_NOEVENT; } - return (PyObject *)e; + + PyObject *ret = pgEvent_FromEventData(e); + pgEvent_FreeEventData(e); + return ret; +} + +static int +pgEvent_Check(PyObject *obj) +{ + PyObject *e_type = pgEvent_GetType(); + if (!e_type) + return -1; + int res = PyObject_IsInstance(obj, e_type); + Py_DECREF(e_type); + return res; } /* event module functions */ @@ -2097,11 +1982,19 @@ static PyObject * pg_event_post(PyObject *self, PyObject *obj) { VIDEO_INIT_CHECK(); - if (!pgEvent_Check(obj)) + int is_event = pgEvent_Check(obj); + if (is_event < 0) + return NULL; + else if (!is_event) return RAISE(PyExc_TypeError, "argument must be an Event object"); - pgEventObject *e = (pgEventObject *)obj; - switch (pg_post_event(e->type, e->dict)) { + pgEventData e = pgEvent_GetEventData(obj); + + if (PyErr_Occurred()) + return NULL; + + switch (pg_post_event(e.type, e.dict)) { + pgEvent_FreeEventData(e); case 0: Py_RETURN_FALSE; case 1: @@ -2209,6 +2102,25 @@ pg_event_get_blocked(PyObject *self, PyObject *obj) return PyBool_FromLong(isblocked); } +static PyObject * +pg_event_register_event_class(PyObject *self, PyObject *obj) +{ + if (!(PyType_Check(obj) && PyCallable_Check(obj))) + return RAISE(PyExc_ValueError, "expected a type"); + + Py_INCREF(obj); + Py_XDECREF(_event_class); + _event_class = obj; + Py_RETURN_NONE; +} + +void +pg_event_free(PyObject *self) +{ + Py_XDECREF(_event_class); + _event_class = NULL; +} + static PyMethodDef _event_methods[] = { {"_internal_mod_init", (PyCFunction)pgEvent_AutoInit, METH_NOARGS, "auto initialize for event module"}, @@ -2236,6 +2148,8 @@ static PyMethodDef _event_methods[] = { DOC_EVENT_SETBLOCKED}, {"get_blocked", (PyCFunction)pg_event_get_blocked, METH_O, DOC_EVENT_GETBLOCKED}, + {"register_event_class", (PyCFunction)pg_event_register_event_class, + METH_O}, {NULL, NULL, 0, NULL}}; @@ -2244,15 +2158,10 @@ MODINIT_DEFINE(_event) PyObject *module, *apiobj; static void *c_api[PYGAMEAPI_EVENT_NUMSLOTS]; - static struct PyModuleDef _module = {PyModuleDef_HEAD_INIT, - "event", - DOC_EVENT, - -1, - _event_methods, - NULL, - NULL, - NULL, - NULL}; + static struct PyModuleDef _module = { + PyModuleDef_HEAD_INIT, "event", DOC_EVENT, -1, + _event_methods, NULL, NULL, NULL, + (freefunc)pg_event_free}; /* imported needed apis; Do this first so if there is an error the module is not loaded. @@ -2267,33 +2176,15 @@ MODINIT_DEFINE(_event) return NULL; } - /* type preparation */ - if (PyType_Ready(&pgEvent_Type) < 0) { - return NULL; - } - /* create the module */ module = PyModule_Create(&_module); if (!module) { return NULL; } - Py_INCREF(&pgEvent_Type); - if (PyModule_AddObject(module, "EventType", (PyObject *)&pgEvent_Type)) { - Py_DECREF(&pgEvent_Type); - Py_DECREF(module); - return NULL; - } - Py_INCREF(&pgEvent_Type); - if (PyModule_AddObject(module, "Event", (PyObject *)&pgEvent_Type)) { - Py_DECREF(&pgEvent_Type); - Py_DECREF(module); - return NULL; - } - /* export the c api */ - assert(PYGAMEAPI_EVENT_NUMSLOTS == 10); - c_api[0] = &pgEvent_Type; + assert(PYGAMEAPI_EVENT_NUMSLOTS == 14); + c_api[0] = pgEvent_GetType; c_api[1] = pgEvent_New; c_api[2] = pg_post_event; c_api[3] = pg_post_event_dictproxy; @@ -2303,6 +2194,10 @@ MODINIT_DEFINE(_event) c_api[7] = pgEvent_GetKeyUpInfo; c_api[8] = pgEvent_GetMouseButtonDownInfo; c_api[9] = pgEvent_GetMouseButtonUpInfo; + c_api[10] = pgEvent_Check; + c_api[11] = pgEvent_FromEventData; + c_api[12] = pgEvent_GetEventData; + c_api[13] = pgEvent_FreeEventData; apiobj = encapsulate_api(c_api, "_event"); if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { diff --git a/src_c/_pygame.h b/src_c/_pygame.h index f16cbbf5af..6a07592539 100644 --- a/src_c/_pygame.h +++ b/src_c/_pygame.h @@ -480,8 +480,8 @@ typedef enum { /* * event module internals */ -struct pgEventObject { - PyObject_HEAD int type; +struct pgEventData { + int type; PyObject *dict; }; @@ -539,7 +539,7 @@ typedef enum { #define PYGAMEAPI_COLOR_NUMSLOTS 5 #define PYGAMEAPI_MATH_NUMSLOTS 2 #define PYGAMEAPI_BASE_NUMSLOTS 29 -#define PYGAMEAPI_EVENT_NUMSLOTS 10 +#define PYGAMEAPI_EVENT_NUMSLOTS 14 #define PYGAMEAPI_WINDOW_NUMSLOTS 1 #define PYGAMEAPI_GEOMETRY_NUMSLOTS 1 diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 77a01981c5..7e74e0e827 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -378,12 +378,10 @@ typedef struct { /* * EVENT module */ -typedef struct pgEventObject pgEventObject; +typedef struct pgEventData pgEventData; #ifndef PYGAMEAPI_EVENT_INTERNAL -#define pgEvent_Type (*(PyTypeObject *)PYGAMEAPI_GET_SLOT(_event, 0)) - -#define pgEvent_Check(x) ((x)->ob_type == &pgEvent_Type) +#define pgEvent_GetType (*(PyObject * (*)(void)) PYGAMEAPI_GET_SLOT(_event, 0)) #define pgEvent_New \ (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(_event, 1)) @@ -410,6 +408,17 @@ typedef struct pgEventObject pgEventObject; #define pgEvent_GetMouseButtonUpInfo \ (*(char *(*)(void))PYGAMEAPI_GET_SLOT(_event, 9)) +#define pgEvent_Check (*(int (*)(PyObject *))PYGAMEAPI_GET_SLOT(_event, 10)) + +#define pgEvent_FromEventData \ + (*(PyObject * (*)(pgEventData)) PYGAMEAPI_GET_SLOT(_event, 11)) + +#define pgEvent_GetEventData \ + (*(pgEventData(*)(PyObject *))PYGAMEAPI_GET_SLOT(_event, 12)) + +#define pgEvent_FreeEventData \ + (*(void (*)(pgEventData))PYGAMEAPI_GET_SLOT(_event, 13)) + #define import_pygame_event() IMPORT_PYGAME_MODULE(_event) #endif diff --git a/src_c/time.c b/src_c/time.c index 383782d9b3..5abf959c4a 100644 --- a/src_c/time.c +++ b/src_c/time.c @@ -395,7 +395,7 @@ time_set_timer(PyObject *self, PyObject *args, PyObject *kwargs) int ticks, loops = 0; PyObject *obj, *ev_dict = NULL; int ev_type; - pgEventObject *e; + pgEventData e; pgSetTimerErr ecode = PG_TIMER_NO_ERROR; static char *kwids[] = {"event", "millis", "loops", NULL}; @@ -421,14 +421,24 @@ time_set_timer(PyObject *self, PyObject *args, PyObject *kwargs) return RAISE(PyExc_ValueError, "event type out of range"); } } - else if (pgEvent_Check(obj)) { - e = (pgEventObject *)obj; - ev_type = e->type; - ev_dict = e->dict; - } else { - return RAISE(PyExc_TypeError, - "first argument must be an event type or event object"); + int is_event = pgEvent_Check(obj); + if (is_event) { + e = pgEvent_GetEventData(obj); + + if (PyErr_Occurred()) + return NULL; + + ev_type = e.type; + ev_dict = e.dict; + + pgEvent_FreeEventData(e); + } + else { + return RAISE( + PyExc_TypeError, + "first argument must be an event type or event object"); + } } #ifndef __EMSCRIPTEN__ diff --git a/src_py/event.py b/src_py/event.py index 36f6b9522f..8490e9578a 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from pygame._event import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] from pygame._event import _internal_mod_init as _init, _internal_mod_quit as _quit from pygame.constants import USEREVENT, NUMEVENTS @@ -86,14 +88,48 @@ def event_name(type: int) -> str: if type in _NAMES_MAPPING: return _NAMES_MAPPING[type] - elif USEREVENT <= type < NUMEVENTS: + if USEREVENT <= type < NUMEVENTS: return "UserEvent" return "Unknown" -# class Event: -# def __init__(self, type: int): -# self.type = type +class Event: + """ + Event(type, dict) -> Event + Event(type, **attributes) -> Event + pygame object for representing events + """ + + type: int + dict: dict[str, ...] + + def __init__(self, type: int, dict_: dict[str, ...] | None = None, **kwargs): + if not 0 <= type < NUMEVENTS: + raise error("event type out of range") + dict_ = dict_ if dict_ is not None else {} + dict_.update(kwargs) + + if "type" in dict_: + raise error("redundant type field in event dict") + + dict_["type"] = type + + self.__dict__ = dict_ + + def __int__(self): + return self.type + + def __bool__(self): + return self.type != pg.NOEVENT + + def __eq__(self, other: Event): + return self.type == other.type and self.dict == other.dict + + def __repr__(self): + return f" Date: Thu, 18 Jul 2024 21:16:15 +0200 Subject: [PATCH 08/36] Moving Event() to python - bugfix. --- src_c/_event.c | 158 ------------------------------------------------ src_py/event.py | 8 +++ 2 files changed, 8 insertions(+), 158 deletions(-) diff --git a/src_c/_event.c b/src_c/_event.c index b14262e405..6a28f1e6e8 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -722,164 +722,6 @@ pg_post_event(Uint32 type, PyObject *dict) return ret; } -static char * -_pg_name_from_eventtype(int type) -{ - switch (type) { - case SDL_ACTIVEEVENT: - return "ActiveEvent"; - case SDL_APP_TERMINATING: - return "AppTerminating"; - case SDL_APP_LOWMEMORY: - return "AppLowMemory"; - case SDL_APP_WILLENTERBACKGROUND: - return "AppWillEnterBackground"; - case SDL_APP_DIDENTERBACKGROUND: - return "AppDidEnterBackground"; - case SDL_APP_WILLENTERFOREGROUND: - return "AppWillEnterForeground"; - case SDL_APP_DIDENTERFOREGROUND: - return "AppDidEnterForeground"; - case SDL_CLIPBOARDUPDATE: - return "ClipboardUpdate"; - case SDL_KEYDOWN: - return "KeyDown"; - case SDL_KEYUP: - return "KeyUp"; - case SDL_KEYMAPCHANGED: - return "KeyMapChanged"; -#if SDL_VERSION_ATLEAST(2, 0, 14) - case SDL_LOCALECHANGED: - return "LocaleChanged"; -#endif - case SDL_MOUSEMOTION: - return "MouseMotion"; - case SDL_MOUSEBUTTONDOWN: - return "MouseButtonDown"; - case SDL_MOUSEBUTTONUP: - return "MouseButtonUp"; - case SDL_JOYAXISMOTION: - return "JoyAxisMotion"; - case SDL_JOYBALLMOTION: - return "JoyBallMotion"; - case SDL_JOYHATMOTION: - return "JoyHatMotion"; - case SDL_JOYBUTTONUP: - return "JoyButtonUp"; - case SDL_JOYBUTTONDOWN: - return "JoyButtonDown"; - case SDL_QUIT: - return "Quit"; - case SDL_SYSWMEVENT: - return "SysWMEvent"; - case SDL_VIDEORESIZE: - return "VideoResize"; - case SDL_VIDEOEXPOSE: - return "VideoExpose"; - case PGE_MIDIIN: - return "MidiIn"; - case PGE_MIDIOUT: - return "MidiOut"; - case SDL_NOEVENT: - return "NoEvent"; - case SDL_FINGERMOTION: - return "FingerMotion"; - case SDL_FINGERDOWN: - return "FingerDown"; - case SDL_FINGERUP: - return "FingerUp"; - case SDL_MULTIGESTURE: - return "MultiGesture"; - case SDL_MOUSEWHEEL: - return "MouseWheel"; - case SDL_TEXTINPUT: - return "TextInput"; - case SDL_TEXTEDITING: - return "TextEditing"; - case SDL_DROPFILE: - return "DropFile"; - case SDL_DROPTEXT: - return "DropText"; - case SDL_DROPBEGIN: - return "DropBegin"; - case SDL_DROPCOMPLETE: - return "DropComplete"; - case SDL_CONTROLLERAXISMOTION: - return "ControllerAxisMotion"; - case SDL_CONTROLLERBUTTONDOWN: - return "ControllerButtonDown"; - case SDL_CONTROLLERBUTTONUP: - return "ControllerButtonUp"; - case SDL_CONTROLLERDEVICEADDED: - return "ControllerDeviceAdded"; - case SDL_CONTROLLERDEVICEREMOVED: - return "ControllerDeviceRemoved"; - case SDL_CONTROLLERDEVICEREMAPPED: - return "ControllerDeviceMapped"; - case SDL_JOYDEVICEADDED: - return "JoyDeviceAdded"; - case SDL_JOYDEVICEREMOVED: - return "JoyDeviceRemoved"; -#if SDL_VERSION_ATLEAST(2, 0, 14) - case SDL_CONTROLLERTOUCHPADDOWN: - return "ControllerTouchpadDown"; - case SDL_CONTROLLERTOUCHPADMOTION: - return "ControllerTouchpadMotion"; - case SDL_CONTROLLERTOUCHPADUP: - return "ControllerTouchpadUp"; - case SDL_CONTROLLERSENSORUPDATE: - return "ControllerSensorUpdate"; -#endif /*SDL_VERSION_ATLEAST(2, 0, 14)*/ - case SDL_AUDIODEVICEADDED: - return "AudioDeviceAdded"; - case SDL_AUDIODEVICEREMOVED: - return "AudioDeviceRemoved"; - case SDL_RENDER_TARGETS_RESET: - return "RenderTargetsReset"; - case SDL_RENDER_DEVICE_RESET: - return "RenderDeviceReset"; - case PGE_WINDOWSHOWN: - return "WindowShown"; - case PGE_WINDOWHIDDEN: - return "WindowHidden"; - case PGE_WINDOWEXPOSED: - return "WindowExposed"; - case PGE_WINDOWMOVED: - return "WindowMoved"; - case PGE_WINDOWRESIZED: - return "WindowResized"; - case PGE_WINDOWSIZECHANGED: - return "WindowSizeChanged"; - case PGE_WINDOWMINIMIZED: - return "WindowMinimized"; - case PGE_WINDOWMAXIMIZED: - return "WindowMaximized"; - case PGE_WINDOWRESTORED: - return "WindowRestored"; - case PGE_WINDOWENTER: - return "WindowEnter"; - case PGE_WINDOWLEAVE: - return "WindowLeave"; - case PGE_WINDOWFOCUSGAINED: - return "WindowFocusGained"; - case PGE_WINDOWFOCUSLOST: - return "WindowFocusLost"; - case PGE_WINDOWCLOSE: - return "WindowClose"; - case PGE_WINDOWTAKEFOCUS: - return "WindowTakeFocus"; - case PGE_WINDOWHITTEST: - return "WindowHitTest"; - case PGE_WINDOWICCPROFCHANGED: - return "WindowICCProfChanged"; - case PGE_WINDOWDISPLAYCHANGED: - return "WindowDisplayChanged"; - } - if (type >= PGE_USEREVENT && type < PG_NUMEVENTS) - return "UserEvent"; - return "Unknown"; -} - /* Helper for adding objects to dictionaries. Check for errors with PyErr_Occurred() */ static void diff --git a/src_py/event.py b/src_py/event.py index 8490e9578a..bda5e11a68 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -128,6 +128,14 @@ def __eq__(self, other: Event): def __repr__(self): return f" Date: Fri, 19 Jul 2024 15:15:29 +0200 Subject: [PATCH 09/36] Moving Event() to python - bugfix 2. --- src_c/_event.c | 13 +++++++++++-- src_py/event.py | 46 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src_c/_event.c b/src_c/_event.c index 6a28f1e6e8..98616125df 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1196,14 +1196,23 @@ pgEvent_FromEventData(pgEventData e_data) if (!num) goto finalize; - args = PyTuple_New(1); + if (e_data.dict) + args = PyTuple_New(2); + else + args = PyTuple_New(1); + if (!args) { Py_DECREF(num); goto finalize; } PyTuple_SetItem(args, 0, num); - ret = PyObject_Call(e_type, args, e_data.dict); + if (e_data.dict) { + Py_INCREF(e_data.dict); + PyTuple_SetItem(args, 1, e_data.dict); + } + + ret = PyObject_Call(e_type, args, NULL); finalize: Py_DECREF(e_type); diff --git a/src_py/event.py b/src_py/event.py index bda5e11a68..b04b3ec61a 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -103,18 +103,26 @@ class Event: type: int dict: dict[str, ...] - def __init__(self, type: int, dict_: dict[str, ...] | None = None, **kwargs): + def __init__(self, type: int, dict: dict[str, ...] | None = None, **kwargs): + if not isinstance(type, int): + raise TypeError("event type must be an integer") + if not 0 <= type < NUMEVENTS: - raise error("event type out of range") - dict_ = dict_ if dict_ is not None else {} - dict_.update(kwargs) + raise ValueError("event type out of range") + + dict = dict if dict is not None else {} + dict.update(kwargs) - if "type" in dict_: - raise error("redundant type field in event dict") + if "type" in dict: + raise ValueError("redundant type field in event dict") - dict_["type"] = type + self._type = type + self._dict = dict - self.__dict__ = dict_ + def __new__(cls, *args, **kwargs): + if "type" in kwargs: + raise ValueError("redundant type field in event dict") + return super().__new__(cls) def __int__(self): return self.type @@ -130,14 +138,28 @@ def __repr__(self): @property def dict(self): - return self.__dict__ + return self._dict + + @property + def type(self): + return self._type + + def __getattr__(self, name): + return self._dict[name] + + def __getattribute__(self, name): + if name == "__dict__": + return super().__getattribute__("_dict") + return super().__getattribute__(name) - @dict.setter - def dict(self, value: dict[str, ...]): - self.__dict__ = value + def __setattr__(self, name, value): + if name in ("_type", "_dict", "type", "dict"): + return super().__setattr__(name, value) + self._dict[name] = value EventType = Event +register_event_class(Event) def init(): From 097ad6a0e2327791080502ebb377dc5bc1733a53 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:25:39 +0200 Subject: [PATCH 10/36] Moving Event() to python - bugfix 3. --- src_c/_event.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src_c/_event.c b/src_c/_event.c index 98616125df..4a5a4c70ec 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1844,8 +1844,10 @@ pg_event_post(PyObject *self, PyObject *obj) if (PyErr_Occurred()) return NULL; - switch (pg_post_event(e.type, e.dict)) { - pgEvent_FreeEventData(e); + int res = pg_post_event(e.type, e.dict); + pgEvent_FreeEventData(e); + + switch (res) { case 0: Py_RETURN_FALSE; case 1: From 931cf5efb04ee08462590f7a2f08aa0e1e6ddb74 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:56:48 +0200 Subject: [PATCH 11/36] Moving Event() to python - bugfix 4. --- buildconfig/stubs/pygame/_common.pyi | 21 ++++++++++++++++++++- buildconfig/stubs/pygame/_event.pyi | 13 +++++++------ buildconfig/stubs/pygame/event.pyi | 18 +++--------------- src_py/event.py | 2 ++ 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/buildconfig/stubs/pygame/_common.pyi b/buildconfig/stubs/pygame/_common.pyi index 19e958a724..690b17139a 100644 --- a/buildconfig/stubs/pygame/_common.pyi +++ b/buildconfig/stubs/pygame/_common.pyi @@ -1,5 +1,5 @@ from os import PathLike -from typing import IO, Callable, Tuple, Union, TypeVar +from typing import IO, Callable, Tuple, Union, TypeVar, Dict, Any, Optional from typing_extensions import Literal as Literal, SupportsIndex as SupportsIndex from typing_extensions import Protocol @@ -22,6 +22,25 @@ class Sequence(Protocol[_T]): def __getitem__(self, __i: SupportsIndex) -> _T: ... def __len__(self) -> int: ... + +class EventLike(Protocol): + # __dict__: Dict[str, Any] + def __init__( + self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... + def __new__(cls, *args, **kwargs) -> "EventLike": ... + def __getattribute__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + def __delattr__(self, name: str) -> None: ... + def __int__(self) -> int: ... + def __bool__(self) -> bool: ... + def __eq__(self, other) -> bool: ... + + @property + def type(self) -> int: ... + @property + def dict(self) -> Dict[str, Any]: ... + # Right now, it isn't possible to annotate sizes (popular tools don't support it) but # when it is, the below types should be appropriately annotated diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index 237557f21a..03d6076c63 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -3,9 +3,10 @@ from typing import ( List, Optional, Union, + Type, ) -from ._common import Sequence +from ._common import Sequence, EventLike _EventTypes = Union[int, Sequence[int]] @@ -14,9 +15,9 @@ def get( eventtype: Optional[_EventTypes] = None, pump: Any = True, exclude: Optional[_EventTypes] = None, -) -> List[Event]: ... -def poll() -> Event: ... -def wait(timeout: int = 0) -> Event: ... +) -> List[EventLike]: ... +def poll() -> EventLike: ... +def wait(timeout: int = 0) -> EventLike: ... def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... def set_blocked(type: Optional[_EventTypes], /) -> None: ... @@ -24,5 +25,5 @@ def set_allowed(type: Optional[_EventTypes], /) -> None: ... def get_blocked(type: _EventTypes, /) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... -def post(event: Event, /) -> bool: ... -def register_event_class(cls: type): ... +def post(event: EventLike, /) -> bool: ... +def register_event_class(cls: Type[EventLike]): ... diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 9f4bd597ac..7a73c6ea6c 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -4,24 +4,12 @@ from typing import ( List, Optional, Union, - final, ) -from ._common import Sequence +from ._common import Sequence, EventLike -@final -class Event: - type: int - dict: Dict[str, Any] - __dict__: Dict[str, Any] - __hash__: None # type: ignore - def __init__( - self, type: int, dict: Dict[str, Any] = ..., **kwargs: Any - ) -> None: ... - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - def __bool__(self) -> bool: ... +class Event(EventLike): + ... _EventTypes = Union[int, Sequence[int]] diff --git a/src_py/event.py b/src_py/event.py index b04b3ec61a..69d539e485 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -131,6 +131,8 @@ def __bool__(self): return self.type != pg.NOEVENT def __eq__(self, other: Event): + if not isinstance(other, Event): + return NotImplemented return self.type == other.type and self.dict == other.dict def __repr__(self): From db570bef4144d1db1d4ba392703050d4efba0ab5 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Fri, 19 Jul 2024 22:32:36 +0200 Subject: [PATCH 12/36] Moving Event() to python - bugfix 5. --- src_py/event.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src_py/event.py b/src_py/event.py index 69d539e485..97b031e8c8 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1,6 +1,6 @@ from __future__ import annotations -from pygame._event import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] +from pygame._event import * # pylint: disable=wildcard-import,unused-wildcard-import; lgtm[py/polluting-import] from pygame._event import _internal_mod_init as _init, _internal_mod_quit as _quit from pygame.constants import USEREVENT, NUMEVENTS from pygame.base import error @@ -156,8 +156,9 @@ def __getattribute__(self, name): def __setattr__(self, name, value): if name in ("_type", "_dict", "type", "dict"): - return super().__setattr__(name, value) - self._dict[name] = value + super().__setattr__(name, value) + else: + self._dict[name] = value EventType = Event From 06b8a145579e211111fa2ce3000297c6c4cc2648 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Fri, 19 Jul 2024 23:24:41 +0200 Subject: [PATCH 13/36] Add documentation. --- docs/reST/c_api/event.rst | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/docs/reST/c_api/event.rst b/docs/reST/c_api/event.rst index 7f053faf3d..5f36b4850f 100644 --- a/docs/reST/c_api/event.rst +++ b/docs/reST/c_api/event.rst @@ -13,25 +13,32 @@ The extension module :py:mod:`pygame.event`. Header file: src_c/include/pygame.h +.. c:type:: pgEventData -.. c:type:: pgEventObject - - The :py:class:`pygame.event.EventType` object C struct. + Struct holding information about the event object. .. c:member:: int type The event type code. + + .. c:member:: PyObject* dict + + Dict object of the event, might be NULL. + +.. c:function:: PyObject* pgEvent_GetType(void) -.. c:type:: pgEvent_Type + Return a python class that is currently set to be the event class - The pygame event object type :py:class:`pygame.event.EventType`. + If the class is not known at the time (called before ``pygame._event.register_event_class``) + this function will return NULL and set the error. .. c:function:: int pgEvent_Check(PyObject *x) Return true if *x* is a pygame event instance Will return false if *x* is a subclass of event. - This is a macro. No check is made that *x* is not ``NULL``. + Will return -1 if python error is set while checking. + No check is made that *x* is not ``NULL``. .. c:function:: PyObject* pgEvent_New(SDL_Event *event) @@ -39,6 +46,25 @@ Header file: src_c/include/pygame.h If *event* is ``NULL`` then create an empty event object. On failure raise a Python exception and return ``NULL``. +.. c:function:: PyObject* pgEvent_FromEventData(pgEventData) + + Return an event object constructed from pgEventData struct. + + On error returns NULL and sets python exception. + +.. c:function:: pgEventData pgEvent_GetEventData(PyObject *) + + Return a pgEventData struct containing information about event extracted from the python object. + + Beware: on error this doesn't retun any special sentiel value if error ocurred, only sets python exception, so use ``PyErr_Ocurred()`` for error checking. + Remember to call :c:func:`pgEvent_FreeEventData` after usage to avoid memory leaks. + +.. c:function:: void pgEvent_FreeEventData(pgEventData) + + Free resources held by pgEventData (decrefs dict). + + .. ## pgEvent_FreeEventData ## + .. c:function:: char* pgEvent_GetKeyDownInfo(void) Return an array of bools (using char) of length SDL_NUM_SCANCODES From 38084e8c60ff34114f4f2b54341212cfac10e64d Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Thu, 8 Aug 2024 18:47:30 +0200 Subject: [PATCH 14/36] Input buffer. --- src_c/_event.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src_c/_event.c b/src_c/_event.c index 4a5a4c70ec..3ec430de94 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -88,10 +88,25 @@ static SDL_Event _pg_repeat_event; static SDL_Event _pg_last_keydown_event = {0}; /* Not used as text, acts as an array of bools */ +/* static char pressed_keys[SDL_NUM_SCANCODES] = {0}; static char released_keys[SDL_NUM_SCANCODES] = {0}; static char pressed_mouse_buttons[5] = {0}; static char released_mouse_buttons[5] = {0}; +*/ + +#define INPUT_BUFFER_SIZE SDL_NUM_SCANCODES + SDL_NUM_SCANCODES + 5 + 5 +#define INPUT_BUFFER_PRESSED_OFFSET 0 +#define INPUT_BUFFER_RELEASED_OFFSET \ + INPUT_BUFFER_PRESSED_OFFSET + SDL_NUM_SCANCODES +#define INPUT_BUFFER_MOUSE_PRESSED_OFFSET \ + INPUT_BUFFER_RELEASED_OFFSET + SDL_NUM_SCANCODES +#define INPUT_BUFFER_MOUSE_RELEASED_OFFSET \ + INPUT_BUFFER_MOUSE_PRESSED_OFFSET + 5 +static_assert(INPUT_BUFFER_MOUSE_RELEASED_OFFSET + 5 == INPUT_BUFFER_SIZE, + "mismatched buffer ranges definition"); + +static char input_buffer[INPUT_BUFFER_SIZE] = {0}; static PyObject *_event_class = NULL; @@ -497,7 +512,8 @@ pg_event_filter(void *_, SDL_Event *event) return 0; PG_LOCK_EVFILTER_MUTEX - pressed_keys[event->key.keysym.scancode] = 1; + input_buffer[INPUT_BUFFER_PRESSED_OFFSET + + event->key.keysym.scancode] = 1; if (pg_key_repeat_delay > 0) { if (_pg_repeat_timer) SDL_RemoveTimer(_pg_repeat_timer); @@ -527,7 +543,8 @@ pg_event_filter(void *_, SDL_Event *event) else if (event->type == SDL_KEYUP) { PG_LOCK_EVFILTER_MUTEX - released_keys[event->key.keysym.scancode] = 1; + input_buffer[INPUT_BUFFER_RELEASED_OFFSET + + event->key.keysym.scancode] = 1; if (_pg_repeat_timer && _pg_repeat_event.key.keysym.scancode == event->key.keysym.scancode) { SDL_RemoveTimer(_pg_repeat_timer); @@ -540,11 +557,13 @@ pg_event_filter(void *_, SDL_Event *event) event->type == SDL_MOUSEBUTTONUP) { if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button - 1 < 5) { - pressed_mouse_buttons[event->button.button - 1] = 1; + input_buffer[INPUT_BUFFER_MOUSE_PRESSED_OFFSET + + event->button.button - 1] = 1; } else if (event->type == SDL_MOUSEBUTTONUP && event->button.button - 1 < 5) { - released_mouse_buttons[event->button.button - 1] = 1; + input_buffer[INPUT_BUFFER_MOUSE_RELEASED_OFFSET + + event->button.button - 1] = 1; } if (event->button.button & PGM_BUTTON_KEEP) event->button.button ^= PGM_BUTTON_KEEP; @@ -1326,10 +1345,7 @@ _pg_event_pump(int dopump) if (dopump) { /* This needs to be reset just before calling pump, e.g. on calls to * pygame.event.get(), but not on pygame.event.get(pump=False). */ - memset(pressed_keys, 0, sizeof(pressed_keys)); - memset(released_keys, 0, sizeof(released_keys)); - memset(pressed_mouse_buttons, 0, sizeof(pressed_mouse_buttons)); - memset(released_mouse_buttons, 0, sizeof(released_mouse_buttons)); + memset(input_buffer, 0, sizeof(input_buffer)); SDL_PumpEvents(); } @@ -1516,25 +1532,25 @@ _pg_event_append_to_list(PyObject *list, SDL_Event *event) char * pgEvent_GetKeyDownInfo(void) { - return pressed_keys; + return input_buffer + INPUT_BUFFER_PRESSED_OFFSET; } char * pgEvent_GetKeyUpInfo(void) { - return released_keys; + return input_buffer + INPUT_BUFFER_RELEASED_OFFSET; } char * pgEvent_GetMouseButtonDownInfo(void) { - return pressed_mouse_buttons; + return input_buffer + INPUT_BUFFER_MOUSE_PRESSED_OFFSET; } char * pgEvent_GetMouseButtonUpInfo(void) { - return released_mouse_buttons; + return input_buffer + INPUT_BUFFER_MOUSE_RELEASED_OFFSET; } static PyObject * From 6fd93baf2fd1562ee16eace3b48a1c4f6e93741a Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Fri, 9 Aug 2024 23:08:10 +0200 Subject: [PATCH 15/36] Mark what to do. --- src_c/_event.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src_c/_event.c b/src_c/_event.c index 3ec430de94..4d6ea91ba9 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -31,8 +31,6 @@ #include "doc/event_doc.h" -#include "structmember.h" - // The system message code is only tested on windows, so only // include it there for now. #include @@ -87,14 +85,6 @@ static SDL_TimerID _pg_repeat_timer = 0; static SDL_Event _pg_repeat_event; static SDL_Event _pg_last_keydown_event = {0}; -/* Not used as text, acts as an array of bools */ -/* -static char pressed_keys[SDL_NUM_SCANCODES] = {0}; -static char released_keys[SDL_NUM_SCANCODES] = {0}; -static char pressed_mouse_buttons[5] = {0}; -static char released_mouse_buttons[5] = {0}; -*/ - #define INPUT_BUFFER_SIZE SDL_NUM_SCANCODES + SDL_NUM_SCANCODES + 5 + 5 #define INPUT_BUFFER_PRESSED_OFFSET 0 #define INPUT_BUFFER_RELEASED_OFFSET \ @@ -103,9 +93,11 @@ static char released_mouse_buttons[5] = {0}; INPUT_BUFFER_RELEASED_OFFSET + SDL_NUM_SCANCODES #define INPUT_BUFFER_MOUSE_RELEASED_OFFSET \ INPUT_BUFFER_MOUSE_PRESSED_OFFSET + 5 + static_assert(INPUT_BUFFER_MOUSE_RELEASED_OFFSET + 5 == INPUT_BUFFER_SIZE, "mismatched buffer ranges definition"); +/* Not used as text, acts as an array of bools */ static char input_buffer[INPUT_BUFFER_SIZE] = {0}; static PyObject *_event_class = NULL; @@ -1430,7 +1422,7 @@ pg_event_wait(PyObject *self, PyObject *args, PyObject *kwargs) return pgEvent_New(NULL); return pgEvent_New(&event); } - +// TODO static int _pg_eventtype_from_seq(PyObject *seq, int ind) { @@ -1447,6 +1439,7 @@ _pg_eventtype_from_seq(PyObject *seq, int ind) return val; } +// TODO static PyObject * _pg_eventtype_as_seq(PyObject *obj, Py_ssize_t *len) { @@ -1475,6 +1468,7 @@ _pg_flush_events(Uint32 type) } } +// TODO static PyObject * pg_event_clear(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -1513,6 +1507,7 @@ pg_event_clear(PyObject *self, PyObject *args, PyObject *kwargs) Py_RETURN_NONE; } +// TODO static int _pg_event_append_to_list(PyObject *list, SDL_Event *event) { @@ -1553,6 +1548,7 @@ pgEvent_GetMouseButtonUpInfo(void) return input_buffer + INPUT_BUFFER_MOUSE_RELEASED_OFFSET; } +// TODO static PyObject * _pg_get_all_events_except(PyObject *obj) { @@ -1663,6 +1659,7 @@ _pg_get_all_events_except(PyObject *obj) return NULL; } +// TODO static PyObject * _pg_get_all_events(void) { @@ -1693,6 +1690,7 @@ _pg_get_all_events(void) return NULL; } +// TODO static PyObject * _pg_get_seq_events(PyObject *obj) { @@ -1748,6 +1746,7 @@ _pg_get_seq_events(PyObject *obj) return NULL; } +// TODO static PyObject * pg_event_get(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -1781,6 +1780,7 @@ pg_event_get(PyObject *self, PyObject *args, PyObject *kwargs) } } +// TODO static PyObject * pg_event_peek(PyObject *self, PyObject *args, PyObject *kwargs) { @@ -1873,6 +1873,7 @@ pg_event_post(PyObject *self, PyObject *obj) } } +// TODO static PyObject * pg_event_set_allowed(PyObject *self, PyObject *obj) { @@ -1905,6 +1906,7 @@ pg_event_set_allowed(PyObject *self, PyObject *obj) Py_RETURN_NONE; } +// TODO static PyObject * pg_event_set_blocked(PyObject *self, PyObject *obj) { @@ -1942,6 +1944,7 @@ pg_event_set_blocked(PyObject *self, PyObject *obj) Py_RETURN_NONE; } +// TODO static PyObject * pg_event_get_blocked(PyObject *self, PyObject *obj) { From cba83745f61a0b0de721a7b3c92c8ea3d173b90e Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Sat, 10 Aug 2024 15:35:56 +0200 Subject: [PATCH 16/36] Dopump export. --- buildconfig/stubs/pygame/_event.pyi | 2 +- src_c/_event.c | 9 +++++++-- src_py/event.py | 10 +++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index 03d6076c63..aa95ffbbf7 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -10,7 +10,7 @@ from ._common import Sequence, EventLike _EventTypes = Union[int, Sequence[int]] -def pump() -> None: ... +def pump(dopump: bool) -> None: ... def get( eventtype: Optional[_EventTypes] = None, pump: Any = True, diff --git a/src_c/_event.c b/src_c/_event.c index 4d6ea91ba9..93d1ba2315 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1379,10 +1379,15 @@ _pg_event_wait(SDL_Event *event, int timeout) } static PyObject * -pg_event_pump(PyObject *self, PyObject *_null) +pg_event_pump(PyObject *self, PyObject *obj) { VIDEO_INIT_CHECK(); - _pg_event_pump(1); + int dopump = PyObject_IsTrue(obj); + + if (dopump < 0) + return NULL; + + _pg_event_pump(dopump); Py_RETURN_NONE; } diff --git a/src_py/event.py b/src_py/event.py index 97b031e8c8..21ad13cb74 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1,7 +1,11 @@ from __future__ import annotations from pygame._event import * # pylint: disable=wildcard-import,unused-wildcard-import; lgtm[py/polluting-import] -from pygame._event import _internal_mod_init as _init, _internal_mod_quit as _quit +from pygame._event import ( + _internal_mod_init as _init, + _internal_mod_quit as _quit, + pump as _pump, +) from pygame.constants import USEREVENT, NUMEVENTS from pygame.base import error import pygame as pg @@ -195,3 +199,7 @@ def custom_type(): _custom_event += 1 return _custom_event - 1 + + +def pump(): + return _pump(True) From 0e21061b306f44b17d998ba6a99826831864ac27 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Sat, 10 Aug 2024 22:43:02 +0200 Subject: [PATCH 17/36] Allowed_set/allowed_get. --- buildconfig/stubs/pygame/_event.pyi | 4 ++- src_c/_event.c | 56 +++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index aa95ffbbf7..2ba5a06637 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -10,7 +10,7 @@ from ._common import Sequence, EventLike _EventTypes = Union[int, Sequence[int]] -def pump(dopump: bool) -> None: ... +def pump(dopump: bool, /) -> None: ... def get( eventtype: Optional[_EventTypes] = None, pump: Any = True, @@ -25,5 +25,7 @@ def set_allowed(type: Optional[_EventTypes], /) -> None: ... def get_blocked(type: _EventTypes, /) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... +def allowed_get(type: int, /) -> bool: ... +def allowed_set(type: int, val: bool, /): ... def post(event: EventLike, /) -> bool: ... def register_event_class(cls: Type[EventLike]): ... diff --git a/src_c/_event.c b/src_c/_event.c index 93d1ba2315..5c9246d1ab 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1451,7 +1451,7 @@ _pg_eventtype_as_seq(PyObject *obj, Py_ssize_t *len) *len = 1; if (PySequence_Check(obj)) { *len = PySequence_Size(obj); - /* The returned object gets decref'd later, so incref now */ + // The returned object gets decref'd later, so incref now Py_INCREF(obj); return obj; } @@ -1911,6 +1911,56 @@ pg_event_set_allowed(PyObject *self, PyObject *obj) Py_RETURN_NONE; } +static PyObject * +pg_event_allowed_set(PyObject *self, PyObject *args) +{ + int e_type, e_flag; + PyObject *e_flago; + + if (!PyArg_ParseTuple(args, "iO", &e_type, &e_flago)) + return NULL; + + if (e_type < 0 || e_type >= PG_NUMEVENTS) { + PyErr_SetString(PyExc_ValueError, "event type out of range"); + return NULL; + } + + e_flag = PyObject_IsTrue(e_flago); + Py_DECREF(e_flago); + + if (e_flag < 0) + return NULL; + + PG_SetEventEnabled(_pg_pgevent_proxify(e_type), + e_flag ? SDL_TRUE : SDL_FALSE); + + // Never block events that are needed for proecesing. + if (e_type == SDL_WINDOWEVENT || e_type == PGE_KEYREPEAT) + PG_SetEventEnabled(e_type, SDL_TRUE); + else + PG_SetEventEnabled(e_type, e_flag ? SDL_TRUE : SDL_FALSE); + + Py_RETURN_NONE; +} + +static PyObject * +pg_event_allowed_get(PyObject *self, PyObject *obj) +{ + int e_type = PyLong_AsLong(obj); + + if (PyErr_Occurred()) + return NULL; + + else if (e_type < 0 || e_type >= PG_NUMEVENTS) { + PyErr_SetString(PyExc_ValueError, "event type out of range"); + return NULL; + } + + if (PG_EventEnabled(e_type) == SDL_TRUE) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + // TODO static PyObject * pg_event_set_blocked(PyObject *self, PyObject *obj) @@ -2007,7 +2057,7 @@ static PyMethodDef _event_methods[] = { {"set_grab", set_grab, METH_O, DOC_EVENT_SETGRAB}, {"get_grab", (PyCFunction)get_grab, METH_NOARGS, DOC_EVENT_GETGRAB}, - {"pump", (PyCFunction)pg_event_pump, METH_NOARGS, DOC_EVENT_PUMP}, + {"pump", (PyCFunction)pg_event_pump, METH_O, DOC_EVENT_PUMP}, {"wait", (PyCFunction)pg_event_wait, METH_VARARGS | METH_KEYWORDS, DOC_EVENT_WAIT}, {"poll", (PyCFunction)pg_event_poll, METH_NOARGS, DOC_EVENT_POLL}, @@ -2025,6 +2075,8 @@ static PyMethodDef _event_methods[] = { DOC_EVENT_SETBLOCKED}, {"get_blocked", (PyCFunction)pg_event_get_blocked, METH_O, DOC_EVENT_GETBLOCKED}, + {"allowed_get", (PyCFunction)pg_event_allowed_get, METH_O}, + {"allowed_set", (PyCFunction)pg_event_allowed_set, METH_VARARGS}, {"register_event_class", (PyCFunction)pg_event_register_event_class, METH_O}, From 2e9eabbed0798d2ba03d490a28126a9c064bea01 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 14 Aug 2024 13:48:30 +0200 Subject: [PATCH 18/36] Move set_allowed, set_blocked, get_blocked to python. --- buildconfig/stubs/pygame/_event.pyi | 31 +++----- buildconfig/stubs/pygame/event.pyi | 30 ++++--- src_c/_event.c | 109 +------------------------- src_py/event.py | 116 ++++++++++++++++++++++++---- 4 files changed, 127 insertions(+), 159 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index 2ba5a06637..bf8fdf87a0 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -1,31 +1,24 @@ -from typing import ( - Any, - List, - Optional, - Union, - Type, -) +from __future__ import annotations from ._common import Sequence, EventLike -_EventTypes = Union[int, Sequence[int]] +_EventTypes = int | Sequence[int] def pump(dopump: bool, /) -> None: ... def get( - eventtype: Optional[_EventTypes] = None, - pump: Any = True, - exclude: Optional[_EventTypes] = None, -) -> List[EventLike]: ... + eventtype: _EventTypes | None = None, + pump: bool = True, + exclude: _EventTypes | None = None, +) -> list[EventLike]: ... def poll() -> EventLike: ... def wait(timeout: int = 0) -> EventLike: ... -def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... -def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... -def set_blocked(type: Optional[_EventTypes], /) -> None: ... -def set_allowed(type: Optional[_EventTypes], /) -> None: ... -def get_blocked(type: _EventTypes, /) -> bool: ... +def peek(eventtype: _EventTypes | None = None, pump: bool = True) -> bool: ... +def clear(eventtype: _EventTypes | None = None, pump: bool = True) -> None: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def allowed_get(type: int, /) -> bool: ... -def allowed_set(type: int, val: bool, /): ... +def allowed_set(type: int, val: bool, /) -> None: ... def post(event: EventLike, /) -> bool: ... -def register_event_class(cls: Type[EventLike]): ... +def register_event_class(cls: type[EventLike]) -> None: ... +def _internal_mod_init() -> None: ... +def _internal_mod_quit() -> None: ... diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 7a73c6ea6c..38ba2462a4 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -1,37 +1,33 @@ -from typing import ( - Any, - Dict, - List, - Optional, - Union, -) +from __future__ import annotations + +from typing import Any from ._common import Sequence, EventLike class Event(EventLike): ... -_EventTypes = Union[int, Sequence[int]] +_EventTypes = int | Sequence[int] def pump() -> None: ... def get( - eventtype: Optional[_EventTypes] = None, + eventtype: _EventTypes | None = None, pump: Any = True, - exclude: Optional[_EventTypes] = None, -) -> List[Event]: ... + exclude: _EventTypes | None = None, +) -> list[Event]: ... def poll() -> Event: ... def wait(timeout: int = 0) -> Event: ... -def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... -def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... +def peek(eventtype: _EventTypes | None = None, pump: Any = True) -> bool: ... +def clear(eventtype: _EventTypes | None = None, pump: Any = True) -> None: ... def event_name(type: int) -> str: ... -def set_blocked(type: Optional[_EventTypes], /) -> None: ... -def set_allowed(type: Optional[_EventTypes], /) -> None: ... +def set_blocked(type: _EventTypes | None, /) -> None: ... +def set_allowed(type: _EventTypes | None, /) -> None: ... def get_blocked(type: _EventTypes, /) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def post(event: Event, /) -> bool: ... def custom_type() -> int: ... -def init(): ... -def quit(): ... +def init() -> None: ... +def quit() -> None: ... EventType = Event diff --git a/src_c/_event.c b/src_c/_event.c index 5c9246d1ab..8b3936a5eb 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1878,42 +1878,11 @@ pg_event_post(PyObject *self, PyObject *obj) } } -// TODO static PyObject * -pg_event_set_allowed(PyObject *self, PyObject *obj) +pg_event_allowed_set(PyObject *self, PyObject *args) { - Py_ssize_t len; - int loop, type; - PyObject *seq; VIDEO_INIT_CHECK(); - if (obj == Py_None) { - int i; - for (i = SDL_FIRSTEVENT; i < SDL_LASTEVENT; i++) { - PG_SetEventEnabled(i, SDL_TRUE); - } - } - else { - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) - return NULL; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) { - Py_DECREF(seq); - return NULL; - } - PG_SetEventEnabled(_pg_pgevent_proxify(type), SDL_TRUE); - } - Py_DECREF(seq); - } - Py_RETURN_NONE; -} - -static PyObject * -pg_event_allowed_set(PyObject *self, PyObject *args) -{ int e_type, e_flag; PyObject *e_flago; @@ -1946,6 +1915,8 @@ pg_event_allowed_set(PyObject *self, PyObject *args) static PyObject * pg_event_allowed_get(PyObject *self, PyObject *obj) { + VIDEO_INIT_CHECK(); + int e_type = PyLong_AsLong(obj); if (PyErr_Occurred()) @@ -1961,74 +1932,6 @@ pg_event_allowed_get(PyObject *self, PyObject *obj) Py_RETURN_FALSE; } -// TODO -static PyObject * -pg_event_set_blocked(PyObject *self, PyObject *obj) -{ - Py_ssize_t len; - int loop, type; - PyObject *seq; - VIDEO_INIT_CHECK(); - - if (obj == Py_None) { - int i; - /* Start at PGPOST_EVENTBEGIN */ - for (i = PGPOST_EVENTBEGIN; i < SDL_LASTEVENT; i++) { - PG_SetEventEnabled(i, SDL_FALSE); - } - } - else { - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) - return NULL; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) { - Py_DECREF(seq); - return NULL; - } - PG_SetEventEnabled(_pg_pgevent_proxify(type), SDL_FALSE); - } - Py_DECREF(seq); - } - /* Never block SDL_WINDOWEVENT, we need them for translation */ - PG_SetEventEnabled(SDL_WINDOWEVENT, SDL_TRUE); - /* Never block PGE_KEYREPEAT too, its needed for pygame internal use */ - PG_SetEventEnabled(PGE_KEYREPEAT, SDL_TRUE); - Py_RETURN_NONE; -} - -// TODO -static PyObject * -pg_event_get_blocked(PyObject *self, PyObject *obj) -{ - Py_ssize_t len; - int loop, type, isblocked = 0; - PyObject *seq; - - VIDEO_INIT_CHECK(); - - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) - return NULL; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) { - Py_DECREF(seq); - return NULL; - } - if (PG_EventEnabled(_pg_pgevent_proxify(type)) == SDL_FALSE) { - isblocked = 1; - break; - } - } - - Py_DECREF(seq); - return PyBool_FromLong(isblocked); -} - static PyObject * pg_event_register_event_class(PyObject *self, PyObject *obj) { @@ -2069,12 +1972,6 @@ static PyMethodDef _event_methods[] = { DOC_EVENT_PEEK}, {"post", (PyCFunction)pg_event_post, METH_O, DOC_EVENT_POST}, - {"set_allowed", (PyCFunction)pg_event_set_allowed, METH_O, - DOC_EVENT_SETALLOWED}, - {"set_blocked", (PyCFunction)pg_event_set_blocked, METH_O, - DOC_EVENT_SETBLOCKED}, - {"get_blocked", (PyCFunction)pg_event_get_blocked, METH_O, - DOC_EVENT_GETBLOCKED}, {"allowed_get", (PyCFunction)pg_event_allowed_get, METH_O}, {"allowed_set", (PyCFunction)pg_event_allowed_set, METH_VARARGS}, {"register_event_class", (PyCFunction)pg_event_register_event_class, diff --git a/src_py/event.py b/src_py/event.py index 21ad13cb74..24692efc90 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1,18 +1,31 @@ from __future__ import annotations -from pygame._event import * # pylint: disable=wildcard-import,unused-wildcard-import; lgtm[py/polluting-import] +from collections.abc import Iterable +from typing import Any + from pygame._event import ( _internal_mod_init as _init, _internal_mod_quit as _quit, pump as _pump, + register_event_class as _register_event_class, + allowed_get as _allowed_get, + allowed_set as _allowed_set, + post, + get_grab, + set_grab, + clear, + peek, + wait, + poll, + get, ) -from pygame.constants import USEREVENT, NUMEVENTS + from pygame.base import error import pygame as pg _is_init = False -_custom_event = USEREVENT + 1 +_custom_event = pg.USEREVENT + 1 _NAMES_MAPPING = { pg.ACTIVEEVENT: "ActiveEvent", pg.APP_TERMINATING: "AppTerminating", @@ -92,7 +105,7 @@ def event_name(type: int) -> str: if type in _NAMES_MAPPING: return _NAMES_MAPPING[type] - if USEREVENT <= type < NUMEVENTS: + if pg.USEREVENT <= type < pg.NUMEVENTS: return "UserEvent" return "Unknown" @@ -104,14 +117,11 @@ class Event: pygame object for representing events """ - type: int - dict: dict[str, ...] - - def __init__(self, type: int, dict: dict[str, ...] | None = None, **kwargs): + def __init__(self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any): if not isinstance(type, int): raise TypeError("event type must be an integer") - if not 0 <= type < NUMEVENTS: + if not 0 <= type < pg.NUMEVENTS: raise ValueError("event type out of range") dict = dict if dict is not None else {} @@ -123,7 +133,7 @@ def __init__(self, type: int, dict: dict[str, ...] | None = None, **kwargs): self._type = type self._dict = dict - def __new__(cls, *args, **kwargs): + def __new__(cls, *args: Any, **kwargs: Any): if "type" in kwargs: raise ValueError("redundant type field in event dict") return super().__new__(cls) @@ -134,7 +144,7 @@ def __int__(self): def __bool__(self): return self.type != pg.NOEVENT - def __eq__(self, other: Event): + def __eq__(self, other: Any): if not isinstance(other, Event): return NotImplemented return self.type == other.type and self.dict == other.dict @@ -150,23 +160,26 @@ def dict(self): def type(self): return self._type - def __getattr__(self, name): + def __getattr__(self, name: str) -> Any: return self._dict[name] - def __getattribute__(self, name): + def __getattribute__(self, name: str): if name == "__dict__": return super().__getattribute__("_dict") return super().__getattribute__(name) - def __setattr__(self, name, value): + def __setattr__(self, name: str, value: Any): if name in ("_type", "_dict", "type", "dict"): super().__setattr__(name, value) else: self._dict[name] = value + def __delattr__(self, name: str) -> None: + del self._dict[name] + EventType = Event -register_event_class(Event) +_register_event_class(Event) def init(): @@ -184,7 +197,7 @@ def quit(): # so we can have a unit test that checks if pygame.event.custom_type() stops # returning new types when they are finished, # without that test preventing further tests from getting a custom event type. - _custom_event = USEREVENT + 1 + _custom_event = pg.USEREVENT + 1 _quit() _is_init = False @@ -194,7 +207,7 @@ def custom_type(): """custom_type() -> int\nmake custom user event type""" global _custom_event - if _custom_event >= NUMEVENTS: + if _custom_event >= pg.NUMEVENTS: raise error("pygame.event.custom_type made too many event types.") _custom_event += 1 @@ -203,3 +216,72 @@ def custom_type(): def pump(): return _pump(True) + + +def _setter(val: bool, type: int | Iterable[int] | None, *args: int): + if type is None: + if args: + raise ValueError("Args aren't supported for type==None.") + for ev in range(pg.NUMEVENTS): + _allowed_set(ev, val) + return + + types: list[int] = [] + + if isinstance(type, Iterable): + types.extend(type) + else: + types.append(type) + + if args: + types.extend(args) + + for t in types: + _allowed_set(t, val) + + +def set_blocked(type: int | Iterable[int] | None, *args: int): + _setter(False, type, *args) + + +def set_allowed(type: int | Iterable[int] | None, *args: int): + _setter(True, type, *args) + + +def get_blocked(type: int | Iterable[int], *args: int): + types: list[int] = [] + + if isinstance(type, Iterable): + types.extend(type) + else: + types.append(type) + + if args: + types.extend(args) + + for t in types: + if not _allowed_get(t): + return True + return False + + +__all__ = [ + "Event", + "EventType", + "pump", + "get", + "poll", + "wait", + "peek", + "clear", + "event_name", + "set_blocked", + "set_allowed", + "get_blocked", + "set_grab", + "get_grab", + "post", + "custom_type", + "init", + "quit", +] From 8a2253a18e21a0e999a1ed1f47a0daf79cf8415b Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:08:52 +0200 Subject: [PATCH 19/36] Bugfix. --- buildconfig/stubs/pygame/event.pyi | 6 +++--- src_c/_event.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 38ba2462a4..8713f79afe 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -20,9 +20,9 @@ def wait(timeout: int = 0) -> Event: ... def peek(eventtype: _EventTypes | None = None, pump: Any = True) -> bool: ... def clear(eventtype: _EventTypes | None = None, pump: Any = True) -> None: ... def event_name(type: int) -> str: ... -def set_blocked(type: _EventTypes | None, /) -> None: ... -def set_allowed(type: _EventTypes | None, /) -> None: ... -def get_blocked(type: _EventTypes, /) -> bool: ... +def set_blocked(type: _EventTypes | None, *args: int) -> None: ... +def set_allowed(type: _EventTypes | None, *args: int) -> None: ... +def get_blocked(type: _EventTypes, *args: int) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def post(event: Event, /) -> bool: ... diff --git a/src_c/_event.c b/src_c/_event.c index 8b3936a5eb..4ac0243f5f 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1884,7 +1884,7 @@ pg_event_allowed_set(PyObject *self, PyObject *args) VIDEO_INIT_CHECK(); int e_type, e_flag; - PyObject *e_flago; + PyObject *e_flago = NULL; if (!PyArg_ParseTuple(args, "iO", &e_type, &e_flago)) return NULL; @@ -1895,7 +1895,6 @@ pg_event_allowed_set(PyObject *self, PyObject *args) } e_flag = PyObject_IsTrue(e_flago); - Py_DECREF(e_flago); if (e_flag < 0) return NULL; From 900a4b67f59773865e526ce3d3b7606b2b2abb6a Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:55:37 +0200 Subject: [PATCH 20/36] Add docstrings. --- src_py/event.py | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/src_py/event.py b/src_py/event.py index 24692efc90..5ece5ec6ae 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -1,3 +1,5 @@ +"pygame module for interacting with events and queues" + from __future__ import annotations from collections.abc import Iterable @@ -103,6 +105,8 @@ def event_name(type: int) -> str: + "event_name(type, /) -> string\nget the string name from an event id" + if type in _NAMES_MAPPING: return _NAMES_MAPPING[type] if pg.USEREVENT <= type < pg.NUMEVENTS: @@ -215,16 +219,12 @@ def custom_type(): def pump(): + "pump() -> None\ninternally process pygame event handlers" return _pump(True) -def _setter(val: bool, type: int | Iterable[int] | None, *args: int): - if type is None: - if args: - raise ValueError("Args aren't supported for type==None.") - for ev in range(pg.NUMEVENTS): - _allowed_set(ev, val) - return +def _parse(type: int | Iterable[int], args: tuple[int, ...]) -> list[int]: + types = [] types: list[int] = [] @@ -236,30 +236,37 @@ def _setter(val: bool, type: int | Iterable[int] | None, *args: int): if args: types.extend(args) - for t in types: + return types + + +def _setter(val: bool, type: int | Iterable[int] | None, *args: int): + if type is None: + if args: + raise ValueError("Args aren't supported for type==None.") + for ev in range(pg.NUMEVENTS): + _allowed_set(ev, val) + return + + for t in _parse(type, args): _allowed_set(t, val) def set_blocked(type: int | Iterable[int] | None, *args: int): + "set_blocked(type, /) -> None\nset_blocked(typelist, /) -> None\nset_blocked(None) -> None\ncontrol which events are blocked on the queue" + _setter(False, type, *args) def set_allowed(type: int | Iterable[int] | None, *args: int): + "set_allowed(type, /) -> None\nset_allowed(typelist, /) -> None\nset_allowed(None) -> None\ncontrol which events are allowed on the queue" + _setter(True, type, *args) def get_blocked(type: int | Iterable[int], *args: int): - types: list[int] = [] - - if isinstance(type, Iterable): - types.extend(type) - else: - types.append(type) - - if args: - types.extend(args) + "get_blocked(type, /) -> bool\nget_blocked(typelist, /) -> bool\ntest if a type of event is blocked from the queue" - for t in types: + for t in _parse(type, args): if not _allowed_get(t): return True return False From fed5617998ae117fd270154ed8fcf4c878078e94 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:26:34 +0200 Subject: [PATCH 21/36] Remove _pgEventDictProxy and use a naive multiple Py_INCREF-s approach instead. --- docs/reST/c_api/event.rst | 50 +++-------- src_c/_event.c | 179 +++++++++++++------------------------- src_c/_pygame.h | 19 +--- src_c/include/_pygame.h | 21 ++--- src_c/time.c | 74 ++++++---------- 5 files changed, 109 insertions(+), 234 deletions(-) diff --git a/docs/reST/c_api/event.rst b/docs/reST/c_api/event.rst index 5f36b4850f..3a484f675e 100644 --- a/docs/reST/c_api/event.rst +++ b/docs/reST/c_api/event.rst @@ -13,18 +13,6 @@ The extension module :py:mod:`pygame.event`. Header file: src_c/include/pygame.h -.. c:type:: pgEventData - - Struct holding information about the event object. - - .. c:member:: int type - - The event type code. - - .. c:member:: PyObject* dict - - Dict object of the event, might be NULL. - .. c:function:: PyObject* pgEvent_GetType(void) Return a python class that is currently set to be the event class @@ -46,24 +34,20 @@ Header file: src_c/include/pygame.h If *event* is ``NULL`` then create an empty event object. On failure raise a Python exception and return ``NULL``. -.. c:function:: PyObject* pgEvent_FromEventData(pgEventData) + .. note:: + This is a destructive operation, so don't use passed SDL_Event afterwards. - Return an event object constructed from pgEventData struct. +.. c:function:: PyObject* pgEvent_FromTypeAndDict(int e_type, PyObject *dict) + + Instantiates a new Event object created from the given event type and a dict. On error returns NULL and sets python exception. -.. c:function:: pgEventData pgEvent_GetEventData(PyObject *) - - Return a pgEventData struct containing information about event extracted from the python object. - - Beware: on error this doesn't retun any special sentiel value if error ocurred, only sets python exception, so use ``PyErr_Ocurred()`` for error checking. - Remember to call :c:func:`pgEvent_FreeEventData` after usage to avoid memory leaks. +.. c:function:: int pgEvent_GetEventType(PyObject *) -.. c:function:: void pgEvent_FreeEventData(pgEventData) + Returns an event type extracted from the python object. - Free resources held by pgEventData (decrefs dict). - - .. ## pgEvent_FreeEventData ## + On error this retuns -1. .. c:function:: char* pgEvent_GetKeyDownInfo(void) @@ -85,23 +69,11 @@ Header file: src_c/include/pygame.h Return an array of bools (using char) of length 5 with the most recent button releases. -.. c:function:: int pg_post_event(Uint32 type, PyObject *dict) +.. c:function:: int pg_post_event(Uint32 type, PyObject *obj) Posts a pygame event that is an ``SDL_USEREVENT`` on the SDL side. This - function takes a python dict, which can be NULL too. - This function does not need GIL to be held if dict is NULL, but needs GIL + function takes a python dict or Event instance, which can be NULL too. + This function does not need GIL to be held if obj is NULL, but needs GIL otherwise. Just like the SDL ``SDL_PushEvent`` function, returns 1 on success, 0 if the event was not posted due to it being blocked, and -1 on failure. - -.. c:function:: int pg_post_event_dictproxy(Uint32 type, pgEventDictProxy *dict_proxy) - - Posts a pygame event that is an ``SDL_USEREVENT`` on the SDL side, can also - optionally take a dictproxy instance. Using this dictproxy API is especially - useful when multiple events that need to be posted share the same dict - attribute, like in the case of event timers. This way, the number of python - increfs and decrefs are reduced, and callers of this function don't need to - hold GIL for every event posted, the GIL only needs to be held during the - creation of the dictproxy instance, and when it is freed. - Just like the SDL ``SDL_PushEvent`` function, returns 1 on success, 0 if the - event was not posted due to it being blocked, and -1 on failure. diff --git a/src_c/_event.c b/src_c/_event.c index 4ac0243f5f..e2e6902d03 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -671,65 +671,28 @@ pgEvent_AutoInit(PyObject *self, PyObject *_null) Py_RETURN_NONE; } -/* Posts a pygame event that is an ``SDL_USEREVENT`` on the SDL side, can also - * optionally take a dictproxy instance. Using this dictproxy API is especially - * useful when multiple events that need to be posted share the same dict - * attribute, like in the case of event timers. This way, the number of python - * increfs and decrefs are reduced, and callers of this function don't need to - * hold GIL for every event posted, the GIL only needs to be held during the - * creation of the dictproxy instance, and when it is freed. - * Just like the SDL ``SDL_PushEvent`` function, returns 1 on success, 0 if the - * event was not posted due to it being blocked, and -1 on failure. */ +/* This function posts an SDL "UserEvent" event, can also optionally take a + * dict or an Event instance. This function does not need GIL to be held if obj + * is NULL, but needs GIL otherwise + */ static int -pg_post_event_dictproxy(Uint32 type, pgEventDictProxy *dict_proxy) +pg_post_event(int type, PyObject *obj) { - int ret; SDL_Event event = {0}; event.type = _pg_pgevent_proxify(type); - event.user.data1 = (void *)dict_proxy; - - ret = SDL_PushEvent(&event); - if (ret == 1 && dict_proxy) { - /* successfully posted event with dictproxy */ - SDL_AtomicLock(&dict_proxy->lock); - dict_proxy->num_on_queue++; - SDL_AtomicUnlock(&dict_proxy->lock); - } - return ret; -} + if (event.type == -1) + return -1; -/* This function posts an SDL "UserEvent" event, can also optionally take a - * python dict. This function does not need GIL to be held if dict is NULL, but - * needs GIL otherwise */ -static int -pg_post_event(Uint32 type, PyObject *dict) -{ - int ret; - if (!dict) { - return pg_post_event_dictproxy(type, NULL); - } + if (obj) + Py_INCREF(obj); - pgEventDictProxy *dict_proxy = - (pgEventDictProxy *)malloc(sizeof(pgEventDictProxy)); - if (!dict_proxy) { - return SDL_SetError("insufficient memory (internal malloc failed)"); - } + event.user.data1 = (void *)obj; + int ret = SDL_PushEvent(&event); - Py_INCREF(dict); - dict_proxy->dict = dict; - /* initially set to 0 - unlocked state */ - dict_proxy->lock = 0; - dict_proxy->num_on_queue = 0; - /* So that event function handling this frees it */ - dict_proxy->do_free_at_end = 1; - - ret = pg_post_event_dictproxy(type, dict_proxy); - if (ret != 1) { - Py_DECREF(dict); - free(dict_proxy); - } + if (ret != 1 && obj) + Py_DECREF(obj); return ret; } @@ -762,7 +725,7 @@ get_joy_device_index(int instance_id) } static PyObject * -dict_from_event(SDL_Event *event) +dict_or_obj_from_event(SDL_Event *event) { PyObject *dict = NULL, *tuple, *obj; int hx, hy; @@ -771,27 +734,8 @@ dict_from_event(SDL_Event *event) /* check if a proxy event or userevent was posted */ if (event->type >= PGPOST_EVENTBEGIN) { - int to_free; - pgEventDictProxy *dict_proxy = (pgEventDictProxy *)event->user.data1; - if (!dict_proxy) { - /* the field being NULL implies empty dict */ - return PyDict_New(); - } - - /* spinlocks must be held and released as quickly as possible */ - SDL_AtomicLock(&dict_proxy->lock); - dict = dict_proxy->dict; - dict_proxy->num_on_queue--; - to_free = dict_proxy->num_on_queue <= 0 && dict_proxy->do_free_at_end; - SDL_AtomicUnlock(&dict_proxy->lock); - - if (to_free) { - free(dict_proxy); - } - else { - Py_INCREF(dict); - } - return dict; + // This steals reference to obj from SDL_Event. + return (PyObject *)event->user.data1; } dict = PyDict_New(); @@ -1194,20 +1138,20 @@ pgEvent_GetType(void) } static PyObject * -pgEvent_FromEventData(pgEventData e_data) +pgEvent_FromTypeAndDict(int e_type, PyObject *dict) { PyObject *ret = NULL; PyObject *args = NULL; - PyObject *e_type = pgEvent_GetType(); - if (!e_type) + PyObject *e_typeo = pgEvent_GetType(); + if (!e_typeo) return NULL; - PyObject *num = PyLong_FromLong(e_data.type); + PyObject *num = PyLong_FromLong(e_type); if (!num) goto finalize; - if (e_data.dict) + if (dict) args = PyTuple_New(2); else args = PyTuple_New(1); @@ -1218,64 +1162,67 @@ pgEvent_FromEventData(pgEventData e_data) } PyTuple_SetItem(args, 0, num); - if (e_data.dict) { - Py_INCREF(e_data.dict); - PyTuple_SetItem(args, 1, e_data.dict); + if (dict) { + Py_INCREF(dict); + PyTuple_SetItem(args, 1, dict); } - ret = PyObject_Call(e_type, args, NULL); + ret = PyObject_Call(e_typeo, args, NULL); finalize: - Py_DECREF(e_type); + Py_DECREF(e_typeo); Py_XDECREF(args); return ret; } -static pgEventData -pgEvent_GetEventData(PyObject *event) +static int +pgEvent_GetEventType(PyObject *event) { - pgEventData data = {0}; - data.dict = PyObject_GetAttrString(event, "dict"); - - if (PyErr_Occurred()) - PyErr_Clear(); - PyObject *e_typeo = PyObject_GetAttrString(event, "type"); + if (!e_typeo) { - Py_XDECREF(data.dict); - data.dict = NULL; - goto finalize; + return -1; } - data.type = PyLong_AsLong(e_typeo); + long e_type = PyLong_AsLong(e_typeo); + Py_DECREF(e_typeo); -finalize: - return data; -} + if (PyErr_Occurred()) { + return -1; + } -static void -pgEvent_FreeEventData(pgEventData e_data) -{ - Py_XDECREF(e_data.dict); - e_data.dict = NULL; + if (e_type < 0 || e_type >= PG_NUMEVENTS) { + RAISERETURN(PyExc_ValueError, "event type out of range", -1) + } + + return e_type; } static PyObject * pgEvent_New(SDL_Event *event) { - pgEventData e = {0}; + Uint32 e_type; + PyObject *obj_or_dict = NULL; if (event) { - e.type = _pg_pgevent_deproxify(event->type); - e.dict = dict_from_event(event); + e_type = _pg_pgevent_deproxify(event->type); + obj_or_dict = dict_or_obj_from_event(event); } else { - e.type = SDL_NOEVENT; + e_type = SDL_NOEVENT; } - PyObject *ret = pgEvent_FromEventData(e); - pgEvent_FreeEventData(e); - return ret; + if (!obj_or_dict || + PyObject_IsInstance(obj_or_dict, (PyObject *)&PyDict_Type)) { + if (PyErr_Occurred()) + return NULL; + + PyObject *ret = pgEvent_FromTypeAndDict(e_type, obj_or_dict); + Py_XDECREF(obj_or_dict); + return ret; + } + + return obj_or_dict; } static int @@ -1860,13 +1807,12 @@ pg_event_post(PyObject *self, PyObject *obj) else if (!is_event) return RAISE(PyExc_TypeError, "argument must be an Event object"); - pgEventData e = pgEvent_GetEventData(obj); + int e_type = pgEvent_GetEventType(obj); if (PyErr_Occurred()) return NULL; - int res = pg_post_event(e.type, e.dict); - pgEvent_FreeEventData(e); + int res = pg_post_event(e_type, obj); switch (res) { case 0: @@ -2008,11 +1954,11 @@ MODINIT_DEFINE(_event) } /* export the c api */ - assert(PYGAMEAPI_EVENT_NUMSLOTS == 14); + assert(PYGAMEAPI_EVENT_NUMSLOTS == 13); c_api[0] = pgEvent_GetType; c_api[1] = pgEvent_New; c_api[2] = pg_post_event; - c_api[3] = pg_post_event_dictproxy; + // c_api[3] = pg_post_event_steal; c_api[4] = pg_EnableKeyRepeat; c_api[5] = pg_GetKeyRepeat; c_api[6] = pgEvent_GetKeyDownInfo; @@ -2020,9 +1966,8 @@ MODINIT_DEFINE(_event) c_api[8] = pgEvent_GetMouseButtonDownInfo; c_api[9] = pgEvent_GetMouseButtonUpInfo; c_api[10] = pgEvent_Check; - c_api[11] = pgEvent_FromEventData; - c_api[12] = pgEvent_GetEventData; - c_api[13] = pgEvent_FreeEventData; + c_api[11] = pgEvent_FromTypeAndDict; + c_api[12] = pgEvent_GetEventType; apiobj = encapsulate_api(c_api, "_event"); if (PyModule_AddObject(module, PYGAMEAPI_LOCAL_ENTRY, apiobj)) { diff --git a/src_c/_pygame.h b/src_c/_pygame.h index 21e3d1c4d0..0457d29a46 100644 --- a/src_c/_pygame.h +++ b/src_c/_pygame.h @@ -212,19 +212,6 @@ struct SDL_BlitMap { #endif -/* DictProxy is useful for event posting with an arbitrary dict. Maintains - * state of number of events on queue and whether the owner of this struct - * wants this dict freed. This DictProxy is only to be freed when there are no - * more instances of this DictProxy on the event queue. Access to this is - * safeguarded with a per-proxy spinlock, which is more optimal than having - * to hold GIL in case of event timers */ -typedef struct _pgEventDictProxy { - PyObject *dict; - SDL_SpinLock lock; - int num_on_queue; - Uint8 do_free_at_end; -} pgEventDictProxy; - /* SDL 1.2 constants removed from SDL 2 */ typedef enum { SDL_HWSURFACE = 0, @@ -468,10 +455,6 @@ typedef enum { /* * event module internals */ -struct pgEventData { - int type; - PyObject *dict; -}; /* * surface module internals @@ -526,7 +509,7 @@ typedef enum { #define PYGAMEAPI_COLOR_NUMSLOTS 5 #define PYGAMEAPI_MATH_NUMSLOTS 2 #define PYGAMEAPI_BASE_NUMSLOTS 29 -#define PYGAMEAPI_EVENT_NUMSLOTS 14 +#define PYGAMEAPI_EVENT_NUMSLOTS 13 #define PYGAMEAPI_WINDOW_NUMSLOTS 1 #define PYGAMEAPI_GEOMETRY_NUMSLOTS 1 diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 7e74e0e827..2deed6c076 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -378,8 +378,6 @@ typedef struct { /* * EVENT module */ -typedef struct pgEventData pgEventData; - #ifndef PYGAMEAPI_EVENT_INTERNAL #define pgEvent_GetType (*(PyObject * (*)(void)) PYGAMEAPI_GET_SLOT(_event, 0)) @@ -387,10 +385,10 @@ typedef struct pgEventData pgEventData; (*(PyObject * (*)(SDL_Event *)) PYGAMEAPI_GET_SLOT(_event, 1)) #define pg_post_event \ - (*(int (*)(Uint32, PyObject *))PYGAMEAPI_GET_SLOT(_event, 2)) - -#define pg_post_event_dictproxy \ - (*(int (*)(Uint32, pgEventDictProxy *))PYGAMEAPI_GET_SLOT(_event, 3)) + (*(int (*)(int, PyObject *))PYGAMEAPI_GET_SLOT(_event, 2)) +// TODO +#define pg_post_event_steal \ + (*(int (*)(int, PyObject *))PYGAMEAPI_GET_SLOT(_event, 3)) #define pg_EnableKeyRepeat (*(int (*)(int, int))PYGAMEAPI_GET_SLOT(_event, 4)) @@ -410,14 +408,11 @@ typedef struct pgEventData pgEventData; #define pgEvent_Check (*(int (*)(PyObject *))PYGAMEAPI_GET_SLOT(_event, 10)) -#define pgEvent_FromEventData \ - (*(PyObject * (*)(pgEventData)) PYGAMEAPI_GET_SLOT(_event, 11)) - -#define pgEvent_GetEventData \ - (*(pgEventData(*)(PyObject *))PYGAMEAPI_GET_SLOT(_event, 12)) +#define pgEvent_FromTypeAndDict \ + (*(PyObject * (*)(int, PyObject *)) PYGAMEAPI_GET_SLOT(_event, 11)) -#define pgEvent_FreeEventData \ - (*(void (*)(pgEventData))PYGAMEAPI_GET_SLOT(_event, 13)) +#define pgEvent_GetEventType \ + (*(int (*)(PyObject *))PYGAMEAPI_GET_SLOT(_event, 12)) #define import_pygame_event() IMPORT_PYGAME_MODULE(_event) #endif diff --git a/src_c/time.c b/src_c/time.c index 5abf959c4a..1c71cced75 100644 --- a/src_c/time.c +++ b/src_c/time.c @@ -52,7 +52,7 @@ typedef struct pgEventTimer { intptr_t timer_id; /* A dictproxy instance */ - pgEventDictProxy *dict_proxy; + PyObject *obj; /* event type of the associated event */ int event_type; @@ -137,27 +137,10 @@ _pg_timer_free(pgEventTimer *timer) } } - if (timer->dict_proxy) { - int is_fully_freed = 0; - - SDL_AtomicLock(&timer->dict_proxy->lock); - /* Fully free dict and dict_proxy only if there are no references to it - * on the event queue. If there are any references, event functions - * will handle cleanups */ - if (timer->dict_proxy->num_on_queue <= 0) { - is_fully_freed = 1; - } - else { - timer->dict_proxy->do_free_at_end = 1; - } - SDL_AtomicUnlock(&timer->dict_proxy->lock); - - if (is_fully_freed) { - PyGILState_STATE gstate = PyGILState_Ensure(); - Py_DECREF(timer->dict_proxy->dict); - PyGILState_Release(gstate); - free(timer->dict_proxy); - } + if (timer->obj) { + PyGILState_STATE gstate = PyGILState_Ensure(); + Py_DECREF(timer->obj); + PyGILState_Release(gstate); } free(timer); } @@ -197,29 +180,23 @@ pg_time_autoinit(PyObject *self, PyObject *_null) * but this function can internally hold GIL if needed. * Returns pgSetTimerErr error codes */ static pgSetTimerErr -_pg_add_event_timer(int ev_type, PyObject *ev_dict, int repeat) +_pg_add_event_timer(int ev_type, PyObject *ev_obj, int repeat) { pgEventTimer *new = (pgEventTimer *)malloc(sizeof(pgEventTimer)); if (!new) { return PG_TIMER_MEMORY_ERROR; } - if (ev_dict) { - new->dict_proxy = (pgEventDictProxy *)malloc(sizeof(pgEventDictProxy)); - if (!new->dict_proxy) { - free(new); - return PG_TIMER_MEMORY_ERROR; - } + new->obj = ev_obj; + + if (ev_obj) { PyGILState_STATE gstate = PyGILState_Ensure(); - Py_INCREF(ev_dict); + Py_INCREF(ev_obj); PyGILState_Release(gstate); - new->dict_proxy->dict = ev_dict; - new->dict_proxy->lock = 0; - new->dict_proxy->num_on_queue = 0; - new->dict_proxy->do_free_at_end = 0; - } - else { - new->dict_proxy = NULL; + // new->dict_proxy->dict = ev_dict; + // new->dict_proxy->lock = 0; + // new->dict_proxy->num_on_queue = 0; + // new->dict_proxy->do_free_at_end = 0; } /* insert the timer into the doubly linked list at the first index */ @@ -287,8 +264,15 @@ timer_callback(Uint32 interval, void *param) } else { if (SDL_WasInit(SDL_INIT_VIDEO)) { - pg_post_event_dictproxy((Uint32)evtimer->event_type, - evtimer->dict_proxy); + PyGILState_STATE gstate; + + if (evtimer->obj) + gstate = PyGILState_Ensure(); + + pg_post_event((Uint32)evtimer->event_type, evtimer->obj); + + if (evtimer->obj) + PyGILState_Release(gstate); } else { evtimer->repeat = 0; @@ -393,9 +377,8 @@ static PyObject * time_set_timer(PyObject *self, PyObject *args, PyObject *kwargs) { int ticks, loops = 0; - PyObject *obj, *ev_dict = NULL; + PyObject *obj, *ev_obj = NULL; int ev_type; - pgEventData e; pgSetTimerErr ecode = PG_TIMER_NO_ERROR; static char *kwids[] = {"event", "millis", "loops", NULL}; @@ -424,15 +407,12 @@ time_set_timer(PyObject *self, PyObject *args, PyObject *kwargs) else { int is_event = pgEvent_Check(obj); if (is_event) { - e = pgEvent_GetEventData(obj); + ev_type = pgEvent_GetEventType(obj); if (PyErr_Occurred()) return NULL; - ev_type = e.type; - ev_dict = e.dict; - - pgEvent_FreeEventData(e); + ev_obj = obj; } else { return RAISE( @@ -473,7 +453,7 @@ time_set_timer(PyObject *self, PyObject *args, PyObject *kwargs) } } - ecode = _pg_add_event_timer(ev_type, ev_dict, loops); + ecode = _pg_add_event_timer(ev_type, ev_obj, loops); if (ecode != PG_TIMER_NO_ERROR) { goto end; } From 86800246166268f1eccfe0f3a3b7c86f1522184b Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:44:55 +0200 Subject: [PATCH 22/36] Fix formatting for docs and bugfixes. --- buildconfig/stubs/pygame/_event.pyi | 4 +-- buildconfig/stubs/pygame/event.pyi | 42 ++++++++--------------------- buildconfig/stubs/pygame/typing.pyi | 23 ++++++++++++++-- src_c/_event.c | 6 ++--- src_c/time.c | 2 +- src_py/event.py | 42 ++++++++++++++++++++++++----- src_py/typing.py | 34 +++++++++++++++++++++-- 7 files changed, 106 insertions(+), 47 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index bf8fdf87a0..b656b0f3a0 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -1,8 +1,8 @@ from __future__ import annotations -from ._common import Sequence, EventLike +from .typing import SequenceLike, EventLike -_EventTypes = int | Sequence[int] +_EventTypes = int | SequenceLike[int] def pump(dopump: bool, /) -> None: ... def get( diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index e8e3f800b5..6ee1109866 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -1,46 +1,26 @@ -from __future__ import annotations +from typing import Any, Union, Optional -from typing import Any, Protocol +from pygame.typing import SequenceLike, EventLike -from pygame.typing import SequenceLike -# TODO: Should this be moved to "pygame.typing"? +class Event(EventLike): + def __new__(cls, *args: Any, **kwargs: Any) -> "Event": ... -class _EventLike(Protocol): - def __init__( - self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any - ) -> None: ... - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - def __int__(self) -> int: ... - def __bool__(self) -> bool: ... - def __eq__(self, other: Any) -> bool: ... - - @property - def type(self) -> int: ... - @property - def dict(self) -> dict[str, Any]: ... - - -class Event(_EventLike): - ... - -_EventTypes = int | SequenceLike[int] +_EventTypes = Union[int, SequenceLike[int]] def pump() -> None: ... def get( - eventtype: _EventTypes | None = None, + eventtype: Optional[_EventTypes] = None, pump: Any = True, - exclude: _EventTypes | None = None, + exclude: Optional[_EventTypes] = None, ) -> list[Event]: ... def poll() -> Event: ... def wait(timeout: int = 0) -> Event: ... -def peek(eventtype: _EventTypes | None = None, pump: Any = True) -> bool: ... -def clear(eventtype: _EventTypes | None = None, pump: Any = True) -> None: ... +def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... +def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... def event_name(type: int) -> str: ... -def set_blocked(type: _EventTypes | None, *args: int) -> None: ... -def set_allowed(type: _EventTypes | None, *args: int) -> None: ... +def set_blocked(type: Optional[_EventTypes], *args: int) -> None: ... +def set_allowed(type: Optional[_EventTypes], *args: int) -> None: ... def get_blocked(type: _EventTypes, *args: int) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index 883fe2def1..593e73a615 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -4,7 +4,7 @@ # Use the command `python buildconfig/stubs/gen_stubs.py` to copy typing.py to typing.pyi import sys -from typing import IO, Callable, Tuple, Union, TypeVar, Protocol, SupportsIndex +from typing import IO, Callable, Tuple, Dict, Union, Optional, TypeVar, Protocol, SupportsIndex, Any if sys.version_info >= (3, 9): from os import PathLike as _PathProtocol @@ -55,5 +55,24 @@ class _HasRectAttribute(Protocol): RectLike = Union[_CanBeRect, _HasRectAttribute] + +class EventLike(Protocol): + def __init__( + self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... + def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... + def __getattribute__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + def __delattr__(self, name: str) -> None: ... + def __int__(self) -> int: ... + def __bool__(self) -> bool: ... + def __eq__(self, other: Any) -> bool: ... + + @property + def type(self) -> int: ... + @property + def dict(self) -> Dict[str, Any]: ... + + # cleanup namespace -del sys, IO, Callable, Tuple, Union, TypeVar, Protocol, SupportsIndex +del sys, IO, Callable, Tuple, Dict, Union, TypeVar, Protocol, SupportsIndex diff --git a/src_c/_event.c b/src_c/_event.c index e2e6902d03..56cc83a4e7 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -678,13 +678,13 @@ pgEvent_AutoInit(PyObject *self, PyObject *_null) static int pg_post_event(int type, PyObject *obj) { + if (type == -1) + return -1; + SDL_Event event = {0}; event.type = _pg_pgevent_proxify(type); - if (event.type == -1) - return -1; - if (obj) Py_INCREF(obj); diff --git a/src_c/time.c b/src_c/time.c index 1c71cced75..e6d81990f0 100644 --- a/src_c/time.c +++ b/src_c/time.c @@ -264,7 +264,7 @@ timer_callback(Uint32 interval, void *param) } else { if (SDL_WasInit(SDL_INIT_VIDEO)) { - PyGILState_STATE gstate; + PyGILState_STATE gstate = 0; if (evtimer->obj) gstate = PyGILState_Ensure(); diff --git a/src_py/event.py b/src_py/event.py index 5ece5ec6ae..98a5533841 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -105,7 +105,11 @@ def event_name(type: int) -> str: - "event_name(type, /) -> string\nget the string name from an event id" + """ + event_name(type, /) -> string + + get the string name from an event id + """ if type in _NAMES_MAPPING: return _NAMES_MAPPING[type] @@ -118,6 +122,7 @@ class Event: """ Event(type, dict) -> Event Event(type, **attributes) -> Event + pygame object for representing events """ @@ -208,7 +213,11 @@ def quit(): def custom_type(): - """custom_type() -> int\nmake custom user event type""" + """ + custom_type() -> int + + make custom user event type + """ global _custom_event if _custom_event >= pg.NUMEVENTS: @@ -219,7 +228,11 @@ def custom_type(): def pump(): - "pump() -> None\ninternally process pygame event handlers" + """ + pump() -> None + + internally process pygame event handlers + """ return _pump(True) @@ -252,19 +265,36 @@ def _setter(val: bool, type: int | Iterable[int] | None, *args: int): def set_blocked(type: int | Iterable[int] | None, *args: int): - "set_blocked(type, /) -> None\nset_blocked(typelist, /) -> None\nset_blocked(None) -> None\ncontrol which events are blocked on the queue" + """ + set_blocked(type, /) -> None + set_blocked(typelist, /) -> None + set_blocked(None) -> None + + control which events are blocked on the queue + """ _setter(False, type, *args) def set_allowed(type: int | Iterable[int] | None, *args: int): - "set_allowed(type, /) -> None\nset_allowed(typelist, /) -> None\nset_allowed(None) -> None\ncontrol which events are allowed on the queue" + """ + set_allowed(type, /) -> None + set_allowed(typelist, /) -> None + set_allowed(None) -> None + + control which events are allowed on the queue + """ _setter(True, type, *args) def get_blocked(type: int | Iterable[int], *args: int): - "get_blocked(type, /) -> bool\nget_blocked(typelist, /) -> bool\ntest if a type of event is blocked from the queue" + """ + get_blocked(type, /) -> bool + get_blocked(typelist, /) -> bool + + test if a type of event is blocked from the queue + """ for t in _parse(type, args): if not _allowed_get(t): diff --git a/src_py/typing.py b/src_py/typing.py index 883fe2def1..9eacdce067 100644 --- a/src_py/typing.py +++ b/src_py/typing.py @@ -4,7 +4,18 @@ # Use the command `python buildconfig/stubs/gen_stubs.py` to copy typing.py to typing.pyi import sys -from typing import IO, Callable, Tuple, Union, TypeVar, Protocol, SupportsIndex +from typing import ( + IO, + Callable, + Tuple, + Dict, + Union, + Optional, + TypeVar, + Protocol, + SupportsIndex, + Any, +) if sys.version_info >= (3, 9): from os import PathLike as _PathProtocol @@ -55,5 +66,24 @@ class _HasRectAttribute(Protocol): RectLike = Union[_CanBeRect, _HasRectAttribute] + +class EventLike(Protocol): + def __init__( + self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... + def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... + def __getattribute__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + def __delattr__(self, name: str) -> None: ... + def __int__(self) -> int: ... + def __bool__(self) -> bool: ... + def __eq__(self, other: Any) -> bool: ... + + @property + def type(self) -> int: ... + @property + def dict(self) -> Dict[str, Any]: ... + + # cleanup namespace -del sys, IO, Callable, Tuple, Union, TypeVar, Protocol, SupportsIndex +del sys, IO, Callable, Tuple, Dict, Union, TypeVar, Protocol, SupportsIndex From 9c539c674d98b32609260a663b67d9038233f511 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:09:51 +0200 Subject: [PATCH 23/36] Buffered INCREFs and DECREFs for timers. --- buildconfig/stubs/pygame/typing.pyi | 15 +++++++++-- docs/reST/c_api/event.rst | 9 +++++++ src_c/_event.c | 24 ++++++++++------- src_c/include/_pygame.h | 2 +- src_c/time.c | 41 +++++++++++++++++++---------- src_py/typing.py | 14 +++++++++- 6 files changed, 77 insertions(+), 28 deletions(-) diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index 593e73a615..16dea1390c 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -4,7 +4,18 @@ # Use the command `python buildconfig/stubs/gen_stubs.py` to copy typing.py to typing.pyi import sys -from typing import IO, Callable, Tuple, Dict, Union, Optional, TypeVar, Protocol, SupportsIndex, Any +from typing import ( + IO, + Callable, + Tuple, + Dict, + Union, + Optional, + TypeVar, + Protocol, + SupportsIndex, + Any, +) if sys.version_info >= (3, 9): from os import PathLike as _PathProtocol @@ -75,4 +86,4 @@ class EventLike(Protocol): # cleanup namespace -del sys, IO, Callable, Tuple, Dict, Union, TypeVar, Protocol, SupportsIndex +del sys, IO, Callable, Tuple, Dict, Union, Optional, TypeVar, Protocol, SupportsIndex, Any diff --git a/docs/reST/c_api/event.rst b/docs/reST/c_api/event.rst index 3a484f675e..f80a0fd397 100644 --- a/docs/reST/c_api/event.rst +++ b/docs/reST/c_api/event.rst @@ -77,3 +77,12 @@ Header file: src_c/include/pygame.h otherwise. Just like the SDL ``SDL_PushEvent`` function, returns 1 on success, 0 if the event was not posted due to it being blocked, and -1 on failure. + + .. ## pg_post_event ## + +.. c:function:: int pg_post_event_steal(Uint32 type, PyObject *obj) + + Nearly the same as :c:func:`pg_post_event`, but with two differences. + 1) This doesn't need GIL held at all when called. + 2) This steals the reference to obj, instead of borrowing it. + diff --git a/src_c/_event.c b/src_c/_event.c index 56cc83a4e7..9e5ef293f0 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -671,6 +671,17 @@ pgEvent_AutoInit(PyObject *self, PyObject *_null) Py_RETURN_NONE; } +/* Similar to pg_post_event, but it steals the reference to obj and doesn't + * need GLI held at all.*/ +static int +pg_post_event_steal(int type, PyObject *obj) +{ + SDL_Event event = {0}; + event.type = _pg_pgevent_proxify(type); + event.user.data1 = (void *)obj; + return SDL_PushEvent(&event); +} + /* This function posts an SDL "UserEvent" event, can also optionally take a * dict or an Event instance. This function does not need GIL to be held if obj * is NULL, but needs GIL otherwise @@ -678,21 +689,14 @@ pgEvent_AutoInit(PyObject *self, PyObject *_null) static int pg_post_event(int type, PyObject *obj) { - if (type == -1) - return -1; - - SDL_Event event = {0}; - - event.type = _pg_pgevent_proxify(type); - if (obj) Py_INCREF(obj); - event.user.data1 = (void *)obj; - int ret = SDL_PushEvent(&event); + int ret = pg_post_event_steal(type, obj); if (ret != 1 && obj) Py_DECREF(obj); + return ret; } @@ -1958,7 +1962,7 @@ MODINIT_DEFINE(_event) c_api[0] = pgEvent_GetType; c_api[1] = pgEvent_New; c_api[2] = pg_post_event; - // c_api[3] = pg_post_event_steal; + c_api[3] = pg_post_event_steal; c_api[4] = pg_EnableKeyRepeat; c_api[5] = pg_GetKeyRepeat; c_api[6] = pgEvent_GetKeyDownInfo; diff --git a/src_c/include/_pygame.h b/src_c/include/_pygame.h index 2deed6c076..8b1c01789d 100644 --- a/src_c/include/_pygame.h +++ b/src_c/include/_pygame.h @@ -386,7 +386,7 @@ typedef struct { #define pg_post_event \ (*(int (*)(int, PyObject *))PYGAMEAPI_GET_SLOT(_event, 2)) -// TODO + #define pg_post_event_steal \ (*(int (*)(int, PyObject *))PYGAMEAPI_GET_SLOT(_event, 3)) diff --git a/src_c/time.c b/src_c/time.c index e6d81990f0..92ee5c399e 100644 --- a/src_c/time.c +++ b/src_c/time.c @@ -27,6 +27,9 @@ #include "doc/time_doc.h" #define WORST_CLOCK_ACCURACY 12 +#define TIMER_REF_STEP 1000 + +#define PG_CHANGE_REFCNT(obj, by) Py_SET_REFCNT(obj, Py_REFCNT(obj) + by) /* Enum containing some error codes used by timer related functions */ typedef enum { @@ -51,8 +54,9 @@ typedef struct pgEventTimer { * instance from the linked list */ intptr_t timer_id; - /* A dictproxy instance */ + /* A python object reference */ PyObject *obj; + int owned_refs; /* event type of the associated event */ int event_type; @@ -140,6 +144,9 @@ _pg_timer_free(pgEventTimer *timer) if (timer->obj) { PyGILState_STATE gstate = PyGILState_Ensure(); Py_DECREF(timer->obj); + if (timer->owned_refs > 0) { + PG_CHANGE_REFCNT(timer->obj, -timer->owned_refs); + } PyGILState_Release(gstate); } free(timer); @@ -189,14 +196,17 @@ _pg_add_event_timer(int ev_type, PyObject *ev_obj, int repeat) new->obj = ev_obj; + if (repeat >= 0) { + new->owned_refs = repeat + 1; + } + else { + new->owned_refs = TIMER_REF_STEP; + } + if (ev_obj) { PyGILState_STATE gstate = PyGILState_Ensure(); - Py_INCREF(ev_obj); + PG_CHANGE_REFCNT(ev_obj, new->owned_refs); PyGILState_Release(gstate); - // new->dict_proxy->dict = ev_dict; - // new->dict_proxy->lock = 0; - // new->dict_proxy->num_on_queue = 0; - // new->dict_proxy->do_free_at_end = 0; } /* insert the timer into the doubly linked list at the first index */ @@ -264,15 +274,18 @@ timer_callback(Uint32 interval, void *param) } else { if (SDL_WasInit(SDL_INIT_VIDEO)) { - PyGILState_STATE gstate = 0; - - if (evtimer->obj) - gstate = PyGILState_Ensure(); - - pg_post_event((Uint32)evtimer->event_type, evtimer->obj); + if (evtimer->owned_refs == 0) { + evtimer->owned_refs = TIMER_REF_STEP; + + if (evtimer->obj) { + PyGILState_STATE gstate = PyGILState_Ensure(); + PG_CHANGE_REFCNT(evtimer->obj, evtimer->owned_refs); + PyGILState_Release(gstate); + } + } - if (evtimer->obj) - PyGILState_Release(gstate); + pg_post_event_steal((Uint32)evtimer->event_type, evtimer->obj); + evtimer->owned_refs -= 1; } else { evtimer->repeat = 0; diff --git a/src_py/typing.py b/src_py/typing.py index 9eacdce067..f22f47b830 100644 --- a/src_py/typing.py +++ b/src_py/typing.py @@ -86,4 +86,16 @@ def dict(self) -> Dict[str, Any]: ... # cleanup namespace -del sys, IO, Callable, Tuple, Dict, Union, TypeVar, Protocol, SupportsIndex +del ( + sys, + IO, + Callable, + Tuple, + Dict, + Union, + Optional, + TypeVar, + Protocol, + SupportsIndex, + Any, +) From 246a3058b6551b61505c15c5da918d4879dbe4d1 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 28 Aug 2024 02:32:22 +0200 Subject: [PATCH 24/36] Fix 2 old memory leaks and remove negative decref bug. --- src_c/_event.c | 41 ----------------------------------------- src_c/time.c | 15 ++++++++++----- src_py/event.py | 34 ++++++++++++++++++++++++---------- src_py/typing.py | 7 +++++-- 4 files changed, 39 insertions(+), 58 deletions(-) diff --git a/src_c/_event.c b/src_c/_event.c index 9e5ef293f0..b6497a333b 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1424,45 +1424,6 @@ _pg_flush_events(Uint32 type) } } -// TODO -static PyObject * -pg_event_clear(PyObject *self, PyObject *args, PyObject *kwargs) -{ - Py_ssize_t len; - int loop, type; - PyObject *seq, *obj = NULL; - int dopump = 1; - - static char *kwids[] = {"eventtype", "pump", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Op", kwids, &obj, - &dopump)) - return NULL; - - VIDEO_INIT_CHECK(); - _pg_event_pump(dopump); - - if (obj == NULL || obj == Py_None) { - _pg_flush_events(MAX_UINT32); - } - else { - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) /* error aldready set */ - return NULL; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) { - Py_DECREF(seq); - return NULL; /* PyErr aldready set */ - } - _pg_flush_events(type); - } - Py_DECREF(seq); - } - Py_RETURN_NONE; -} - // TODO static int _pg_event_append_to_list(PyObject *list, SDL_Event *event) @@ -1913,8 +1874,6 @@ static PyMethodDef _event_methods[] = { {"wait", (PyCFunction)pg_event_wait, METH_VARARGS | METH_KEYWORDS, DOC_EVENT_WAIT}, {"poll", (PyCFunction)pg_event_poll, METH_NOARGS, DOC_EVENT_POLL}, - {"clear", (PyCFunction)pg_event_clear, METH_VARARGS | METH_KEYWORDS, - DOC_EVENT_CLEAR}, {"get", (PyCFunction)pg_event_get, METH_VARARGS | METH_KEYWORDS, DOC_EVENT_GET}, {"peek", (PyCFunction)pg_event_peek, METH_VARARGS | METH_KEYWORDS, diff --git a/src_c/time.c b/src_c/time.c index 92ee5c399e..a50c5bb94e 100644 --- a/src_c/time.c +++ b/src_c/time.c @@ -27,7 +27,7 @@ #include "doc/time_doc.h" #define WORST_CLOCK_ACCURACY 12 -#define TIMER_REF_STEP 1000 +#define TIMER_REF_STEP 1 #define PG_CHANGE_REFCNT(obj, by) Py_SET_REFCNT(obj, Py_REFCNT(obj) + by) @@ -196,8 +196,8 @@ _pg_add_event_timer(int ev_type, PyObject *ev_obj, int repeat) new->obj = ev_obj; - if (repeat >= 0) { - new->owned_refs = repeat + 1; + if (repeat > 0) { + new->owned_refs = repeat; } else { new->owned_refs = TIMER_REF_STEP; @@ -205,6 +205,7 @@ _pg_add_event_timer(int ev_type, PyObject *ev_obj, int repeat) if (ev_obj) { PyGILState_STATE gstate = PyGILState_Ensure(); + Py_INCREF(ev_obj); // Own reference. PG_CHANGE_REFCNT(ev_obj, new->owned_refs); PyGILState_Release(gstate); } @@ -284,8 +285,12 @@ timer_callback(Uint32 interval, void *param) } } - pg_post_event_steal((Uint32)evtimer->event_type, evtimer->obj); - evtimer->owned_refs -= 1; + /* TODO: When error handling is created for SDL callbacks, + * update this to support the case of -1. */ + if (pg_post_event_steal((Uint32)evtimer->event_type, + evtimer->obj) == 1) { + evtimer->owned_refs -= 1; + } } else { evtimer->repeat = 0; diff --git a/src_py/event.py b/src_py/event.py index 98a5533841..213be4e11c 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -2,8 +2,10 @@ from __future__ import annotations -from collections.abc import Iterable +import gc + from typing import Any +from .typing import IterableLike from pygame._event import ( _internal_mod_init as _init, @@ -15,7 +17,6 @@ post, get_grab, set_grab, - clear, peek, wait, poll, @@ -202,6 +203,10 @@ def init(): def quit(): global _is_init, _custom_event + # Clear event queue to avoid memory leak when SDL tries to clear it + # without freeing our resources. + clear(pump=False) + # The main reason for _custom_event to be reset here is # so we can have a unit test that checks if pygame.event.custom_type() stops # returning new types when they are finished, @@ -236,15 +241,15 @@ def pump(): return _pump(True) -def _parse(type: int | Iterable[int], args: tuple[int, ...]) -> list[int]: +def _parse(type: int | IterableLike[int], args: tuple[int, ...]) -> list[int]: types = [] types: list[int] = [] - if isinstance(type, Iterable): - types.extend(type) - else: + if isinstance(type, int): types.append(type) + else: + types.extend(iter(type)) if args: types.extend(args) @@ -252,7 +257,7 @@ def _parse(type: int | Iterable[int], args: tuple[int, ...]) -> list[int]: return types -def _setter(val: bool, type: int | Iterable[int] | None, *args: int): +def _setter(val: bool, type: int | IterableLike[int] | None, *args: int): if type is None: if args: raise ValueError("Args aren't supported for type==None.") @@ -264,7 +269,7 @@ def _setter(val: bool, type: int | Iterable[int] | None, *args: int): _allowed_set(t, val) -def set_blocked(type: int | Iterable[int] | None, *args: int): +def set_blocked(type: int | IterableLike[int] | None, *args: int): """ set_blocked(type, /) -> None set_blocked(typelist, /) -> None @@ -276,7 +281,7 @@ def set_blocked(type: int | Iterable[int] | None, *args: int): _setter(False, type, *args) -def set_allowed(type: int | Iterable[int] | None, *args: int): +def set_allowed(type: int | IterableLike[int] | None, *args: int): """ set_allowed(type, /) -> None set_allowed(typelist, /) -> None @@ -288,7 +293,7 @@ def set_allowed(type: int | Iterable[int] | None, *args: int): _setter(True, type, *args) -def get_blocked(type: int | Iterable[int], *args: int): +def get_blocked(type: int | IterableLike[int], *args: int): """ get_blocked(type, /) -> bool get_blocked(typelist, /) -> bool @@ -302,6 +307,15 @@ def get_blocked(type: int | Iterable[int], *args: int): return False +def clear(eventtype: int | IterableLike[int] | None = None, pump: bool = True): + if eventtype is None or isinstance(eventtype, int): + get(eventtype, pump) + else: + get(list(iter(eventtype)), pump) + + gc.collect() + + __all__ = [ "Event", "EventType", diff --git a/src_py/typing.py b/src_py/typing.py index f22f47b830..8e44d8d963 100644 --- a/src_py/typing.py +++ b/src_py/typing.py @@ -15,6 +15,7 @@ Protocol, SupportsIndex, Any, + Iterable, ) if sys.version_info >= (3, 9): @@ -36,11 +37,13 @@ def __fspath__(self) -> _T: ... class SequenceLike(Protocol[_T_co]): """ - Variant of the standard `Sequence` ABC that only requires `__getitem__` and `__len__`. + Variant of the standard `Sequence` ABC that only requires `__getitem__`. """ def __getitem__(self, __i: SupportsIndex) -> _T_co: ... - def __len__(self) -> int: ... + + +IterableLike = Union[SequenceLike[_T_co], Iterable[_T_co]] # Modify typehints when it is possible to annotate sizes From ba48bcc24967e4ed305a45da14adc01ff3d8f0f8 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:41:42 +0200 Subject: [PATCH 25/36] Fix a typing issue. --- buildconfig/stubs/pygame/_event.pyi | 1 - buildconfig/stubs/pygame/typing.pyi | 1 - 2 files changed, 2 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index b656b0f3a0..46bcff15eb 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -13,7 +13,6 @@ def get( def poll() -> EventLike: ... def wait(timeout: int = 0) -> EventLike: ... def peek(eventtype: _EventTypes | None = None, pump: bool = True) -> bool: ... -def clear(eventtype: _EventTypes | None = None, pump: bool = True) -> None: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def allowed_get(type: int, /) -> bool: ... diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index fe9affab8e..48600c38a4 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -82,7 +82,6 @@ class _HasRectAttribute(Protocol): RectLike = Union[SequenceLike[float], SequenceLike[Coordinate], _HasRectAttribute] - class EventLike(Protocol): def __init__( self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any From 3cb18db4bc18141c32d36695f8524bbfac4b673f Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:21:04 +0200 Subject: [PATCH 26/36] Improve documentation. --- buildconfig/stubs/pygame/event.pyi | 11 ++++++++++- buildconfig/stubs/pygame/typing.pyi | 8 +------- docs/reST/ref/typing.rst | 14 ++++++++++++++ src_c/_event.c | 11 ++--------- src_c/doc/typing_doc.h | 1 + src_py/event.py | 6 ++++-- src_py/typing.py | 10 +--------- 7 files changed, 33 insertions(+), 28 deletions(-) diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 6ee1109866..8ed7108462 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -1,10 +1,19 @@ -from typing import Any, Union, Optional +from typing import Any, Union, Optional, Dict from pygame.typing import SequenceLike, EventLike class Event(EventLike): + def __init__( + self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... def __new__(cls, *args: Any, **kwargs: Any) -> "Event": ... + def __getattribute__(self, name: str) -> Any: ... + def __setattr__(self, name: str, value: Any) -> None: ... + def __delattr__(self, name: str) -> None: ... + def __int__(self) -> int: ... + def __bool__(self) -> bool: ... + def __eq__(self, other: Any) -> bool: ... _EventTypes = Union[int, SequenceLike[int]] diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index 48600c38a4..2b829a6fdb 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -84,15 +84,9 @@ RectLike = Union[SequenceLike[float], SequenceLike[Coordinate], _HasRectAttribut class EventLike(Protocol): def __init__( - self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + self, type: int, /, **kwargs: Any ) -> None: ... def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - def __int__(self) -> int: ... - def __bool__(self) -> bool: ... - def __eq__(self, other: Any) -> bool: ... @property def type(self) -> int: ... diff --git a/docs/reST/ref/typing.rst b/docs/reST/ref/typing.rst index 764f05a28d..49235e7eb8 100644 --- a/docs/reST/ref/typing.rst +++ b/docs/reST/ref/typing.rst @@ -81,5 +81,19 @@ type aliases for proper typehint annotations. * ``pygame.Rect(RectLike)`` * Any object with a ``.rect`` attribute which is a ``RectLike`` or a function returning a ``RectLike`` + + .. data:: EventLike + + A protocol representing an event that is undestood by pygame-ce. + + * ``pygame.Event(type, dict, arg=val)`` + * A python class that implements EventLike protocol: + + :: + + class MyEvent: + def __init__(self, type: int, **kwargs): + self.type = type + self.dict = kwargs .. ## pygame.typing ## diff --git a/src_c/_event.c b/src_c/_event.c index 03d385fad0..84cd4c5c81 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1155,10 +1155,7 @@ pgEvent_FromTypeAndDict(int e_type, PyObject *dict) if (!num) goto finalize; - if (dict) - args = PyTuple_New(2); - else - args = PyTuple_New(1); + args = PyTuple_New(1); if (!args) { Py_DECREF(num); @@ -1166,12 +1163,8 @@ pgEvent_FromTypeAndDict(int e_type, PyObject *dict) } PyTuple_SetItem(args, 0, num); - if (dict) { - Py_INCREF(dict); - PyTuple_SetItem(args, 1, dict); - } - ret = PyObject_Call(e_typeo, args, NULL); + ret = PyObject_Call(e_typeo, args, dict); finalize: Py_DECREF(e_typeo); diff --git a/src_c/doc/typing_doc.h b/src_c/doc/typing_doc.h index ed354c8439..40f361efa6 100644 --- a/src_c/doc/typing_doc.h +++ b/src_c/doc/typing_doc.h @@ -8,3 +8,4 @@ #define DOC_TYPING_RGBATUPLE "" #define DOC_TYPING_COLORLIKE "" #define DOC_TYPING_RECTLIKE "" +#define DOC_TYPING_EVENTLIKE "" diff --git a/src_py/event.py b/src_py/event.py index 213be4e11c..31b7b62112 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -134,8 +134,10 @@ def __init__(self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any) if not 0 <= type < pg.NUMEVENTS: raise ValueError("event type out of range") - dict = dict if dict is not None else {} - dict.update(kwargs) + if dict is None: + dict = kwargs + else: + dict.update(kwargs) if "type" in dict: raise ValueError("redundant type field in event dict") diff --git a/src_py/typing.py b/src_py/typing.py index 48600c38a4..16c939efeb 100644 --- a/src_py/typing.py +++ b/src_py/typing.py @@ -83,16 +83,8 @@ def rect(self) -> Union["RectLike", Callable[[], "RectLike"]]: ... class EventLike(Protocol): - def __init__( - self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any - ) -> None: ... + def __init__(self, type: int, /, **kwargs: Any) -> None: ... def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... - def __getattribute__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... - def __delattr__(self, name: str) -> None: ... - def __int__(self) -> int: ... - def __bool__(self) -> bool: ... - def __eq__(self, other: Any) -> bool: ... @property def type(self) -> int: ... From 46f17c4f8504a6e38a63b2eb7b9da4ebe4be65a6 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 11 Sep 2024 19:59:38 +0200 Subject: [PATCH 27/36] Move get() to python. --- buildconfig/stubs/pygame/_event.pyi | 8 +- src_c/_event.c | 265 +++------------------------- src_py/event.py | 110 ++++++++++-- 3 files changed, 129 insertions(+), 254 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index 46bcff15eb..db29c39c01 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -5,11 +5,9 @@ from .typing import SequenceLike, EventLike _EventTypes = int | SequenceLike[int] def pump(dopump: bool, /) -> None: ... -def get( - eventtype: _EventTypes | None = None, - pump: bool = True, - exclude: _EventTypes | None = None, -) -> list[EventLike]: ... +def _get(type: int, /) -> EventLike | None: ... +def _proxify_event_type(type: int, /) -> int: ... +def video_check() -> None: ... def poll() -> EventLike: ... def wait(timeout: int = 0) -> EventLike: ... def peek(eventtype: _EventTypes | None = None, pump: bool = True) -> bool: ... diff --git a/src_c/_event.c b/src_c/_event.c index 84cd4c5c81..1a3a7b2772 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -671,8 +671,8 @@ pgEvent_AutoInit(PyObject *self, PyObject *_null) Py_RETURN_NONE; } -/* Similar to pg_post_event, but it steals the reference to obj and doesn't - * need GLI held at all.*/ +/* Similar to pg_post_event, but it steals the reference to obj and does not + * need GIL to be held at all.*/ static int pg_post_event_steal(int type, PyObject *obj) { @@ -1406,23 +1406,6 @@ _pg_eventtype_as_seq(PyObject *obj, Py_ssize_t *len) "event type must be numeric or a sequence"); } -// TODO -static int -_pg_event_append_to_list(PyObject *list, SDL_Event *event) -{ - /* The caller of this function must handle decref of list on error */ - PyObject *e = pgEvent_New(event); - if (!e) /* Exception already set. */ - return 0; - - if (PyList_Append(list, e)) { - Py_DECREF(e); - return 0; /* Exception already set. */ - } - Py_DECREF(e); - return 1; -} - char * pgEvent_GetKeyDownInfo(void) { @@ -1447,236 +1430,43 @@ pgEvent_GetMouseButtonUpInfo(void) return input_buffer + INPUT_BUFFER_MOUSE_RELEASED_OFFSET; } -// TODO -static PyObject * -_pg_get_all_events_except(PyObject *obj) -{ - SDL_Event event; - Py_ssize_t len; - int loop, type, ret; - PyObject *seq, *list; - - SDL_Event *filtered_events; - int filtered_index = 0; - int filtered_events_len = 16; - - SDL_Event eventbuf[PG_GET_LIST_LEN]; - - filtered_events = malloc(sizeof(SDL_Event) * filtered_events_len); - if (!filtered_events) - return PyErr_NoMemory(); - - list = PyList_New(0); - if (!list) { - free(filtered_events); - return PyErr_NoMemory(); - } - - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) - goto error; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) - goto error; - - do { - ret = PG_PEEP_EVENT(&event, 1, SDL_GETEVENT, type); - if (ret < 0) { - PyErr_SetString(pgExc_SDLError, SDL_GetError()); - goto error; - } - else if (ret > 0) { - if (filtered_index == filtered_events_len) { - SDL_Event *new_filtered_events = - malloc(sizeof(SDL_Event) * filtered_events_len * 4); - if (new_filtered_events == NULL) { - goto error; - } - memcpy(new_filtered_events, filtered_events, - sizeof(SDL_Event) * filtered_events_len); - filtered_events_len *= 4; - free(filtered_events); - filtered_events = new_filtered_events; - } - filtered_events[filtered_index] = event; - filtered_index++; - } - } while (ret); - do { - ret = PG_PEEP_EVENT(&event, 1, SDL_GETEVENT, - _pg_pgevent_proxify(type)); - if (ret < 0) { - PyErr_SetString(pgExc_SDLError, SDL_GetError()); - goto error; - } - else if (ret > 0) { - if (filtered_index == filtered_events_len) { - SDL_Event *new_filtered_events = - malloc(sizeof(SDL_Event) * filtered_events_len * 4); - if (new_filtered_events == NULL) { - free(filtered_events); - goto error; - } - memcpy(new_filtered_events, filtered_events, - sizeof(SDL_Event) * filtered_events_len); - filtered_events_len *= 4; - free(filtered_events); - filtered_events = new_filtered_events; - } - filtered_events[filtered_index] = event; - filtered_index++; - } - } while (ret); - } - - do { - len = PG_PEEP_EVENT_ALL(eventbuf, PG_GET_LIST_LEN, SDL_GETEVENT); - if (len == -1) { - PyErr_SetString(pgExc_SDLError, SDL_GetError()); - goto error; - } - - for (loop = 0; loop < len; loop++) { - if (!_pg_event_append_to_list(list, &eventbuf[loop])) - goto error; - } - } while (len == PG_GET_LIST_LEN); - - PG_PEEP_EVENT_ALL(filtered_events, filtered_index, SDL_ADDEVENT); - - free(filtered_events); - Py_DECREF(seq); - return list; - -error: - /* While doing a goto here, PyErr must be set */ - free(filtered_events); - Py_DECREF(list); - Py_XDECREF(seq); - return NULL; -} - -// TODO static PyObject * -_pg_get_all_events(void) +_pg_get_event(PyObject *self, PyObject *obj) { - SDL_Event eventbuf[PG_GET_LIST_LEN]; - PyObject *list; - int loop, len = PG_GET_LIST_LEN; - - list = PyList_New(0); - if (!list) - return PyErr_NoMemory(); - - while (len == PG_GET_LIST_LEN) { - len = PG_PEEP_EVENT_ALL(eventbuf, PG_GET_LIST_LEN, SDL_GETEVENT); - if (len == -1) { - PyErr_SetString(pgExc_SDLError, SDL_GetError()); - goto error; - } + SDL_Event ev; + int e_type = PyLong_AsLong(obj); - for (loop = 0; loop < len; loop++) { - if (!_pg_event_append_to_list(list, &eventbuf[loop])) - goto error; - } - } - return list; + if (e_type == -1 && PyErr_Occurred()) + return NULL; -error: - Py_DECREF(list); - return NULL; + int ret; + if (e_type == -1) + ret = PG_PEEP_EVENT_ALL(&ev, 1, SDL_GETEVENT); + else + ret = PG_PEEP_EVENT(&ev, 1, SDL_GETEVENT, e_type); + if (ret == -1) + return RAISE(pgExc_SDLError, SDL_GetError()); + else if (ret == 0) + Py_RETURN_NONE; + return pgEvent_New(&ev); } -// TODO static PyObject * -_pg_get_seq_events(PyObject *obj) +_pg_video_check(PyObject *self, PyObject *_null) { - Py_ssize_t len; - SDL_Event event; - int loop, type, ret; - PyObject *seq, *list; - - list = PyList_New(0); - if (!list) - return PyErr_NoMemory(); - - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) - goto error; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) - goto error; - - do { - ret = PG_PEEP_EVENT(&event, 1, SDL_GETEVENT, type); - if (ret < 0) { - PyErr_SetString(pgExc_SDLError, SDL_GetError()); - goto error; - } - else if (ret > 0) { - if (!_pg_event_append_to_list(list, &event)) - goto error; - } - } while (ret); - do { - ret = PG_PEEP_EVENT(&event, 1, SDL_GETEVENT, - _pg_pgevent_proxify(type)); - if (ret < 0) { - PyErr_SetString(pgExc_SDLError, SDL_GetError()); - goto error; - } - else if (ret > 0) { - if (!_pg_event_append_to_list(list, &event)) - goto error; - } - } while (ret); - } - Py_DECREF(seq); - return list; - -error: - /* While doing a goto here, PyErr must be set */ - Py_DECREF(list); - Py_XDECREF(seq); - return NULL; + VIDEO_INIT_CHECK(); + Py_RETURN_NONE; } -// TODO static PyObject * -pg_event_get(PyObject *self, PyObject *args, PyObject *kwargs) +_pg_proxify_event_type(PyObject *self, PyObject *obj) { - PyObject *obj_evtype = NULL; - PyObject *obj_exclude = NULL; - int dopump = 1; - - static char *kwids[] = {"eventtype", "pump", "exclude", NULL}; + int e_type = PyLong_AsLong(obj); - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OpO", kwids, &obj_evtype, - &dopump, &obj_exclude)) + if (e_type == -1 && PyErr_Occurred()) return NULL; - VIDEO_INIT_CHECK(); - - _pg_event_pump(dopump); - - if (obj_evtype == NULL || obj_evtype == Py_None) { - if (obj_exclude != NULL && obj_exclude != Py_None) { - return _pg_get_all_events_except(obj_exclude); - } - return _pg_get_all_events(); - } - else { - if (obj_exclude != NULL && obj_exclude != Py_None) { - return RAISE( - pgExc_SDLError, - "Invalid combination of excluded and included event type"); - } - return _pg_get_seq_events(obj_evtype); - } + return PyLong_FromLong(_pg_pgevent_proxify((Uint32)e_type)); } // TODO @@ -1856,8 +1646,8 @@ static PyMethodDef _event_methods[] = { {"wait", (PyCFunction)pg_event_wait, METH_VARARGS | METH_KEYWORDS, DOC_EVENT_WAIT}, {"poll", (PyCFunction)pg_event_poll, METH_NOARGS, DOC_EVENT_POLL}, - {"get", (PyCFunction)pg_event_get, METH_VARARGS | METH_KEYWORDS, - DOC_EVENT_GET}, + // {"get", (PyCFunction)pg_event_get, METH_VARARGS | METH_KEYWORDS, + // DOC_EVENT_GET}, {"peek", (PyCFunction)pg_event_peek, METH_VARARGS | METH_KEYWORDS, DOC_EVENT_PEEK}, {"post", (PyCFunction)pg_event_post, METH_O, DOC_EVENT_POST}, @@ -1866,6 +1656,9 @@ static PyMethodDef _event_methods[] = { {"allowed_set", (PyCFunction)pg_event_allowed_set, METH_VARARGS}, {"register_event_class", (PyCFunction)pg_event_register_event_class, METH_O}, + {"video_check", (PyCFunction)_pg_video_check, METH_NOARGS}, + {"_get", (PyCFunction)_pg_get_event, METH_O}, + {"_proxify_event_type", (PyCFunction)_pg_proxify_event_type, METH_O}, {NULL, NULL, 0, NULL}}; diff --git a/src_py/event.py b/src_py/event.py index 31b7b62112..37c8844817 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -14,13 +14,15 @@ register_event_class as _register_event_class, allowed_get as _allowed_get, allowed_set as _allowed_set, + video_check as _video_check, post, get_grab, set_grab, peek, wait, poll, - get, + _get, + _proxify_event_type, ) from pygame.base import error @@ -107,7 +109,7 @@ def event_name(type: int) -> str: """ - event_name(type, /) -> string + event_name(type) -> string get the string name from an event id """ @@ -119,6 +121,14 @@ def event_name(type: int) -> str: return "Unknown" +def _check_ev_type(ev_type): + if not isinstance(ev_type, int): + raise TypeError("event type must be an integer") + + if not 0 <= ev_type < pg.NUMEVENTS: + raise ValueError("event type out of range") + + class Event: """ Event(type, dict) -> Event @@ -128,11 +138,7 @@ class Event: """ def __init__(self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any): - if not isinstance(type, int): - raise TypeError("event type must be an integer") - - if not 0 <= type < pg.NUMEVENTS: - raise ValueError("event type out of range") + _check_ev_type(type) if dict is None: dict = kwargs @@ -268,13 +274,14 @@ def _setter(val: bool, type: int | IterableLike[int] | None, *args: int): return for t in _parse(type, args): + _check_ev_type(t) _allowed_set(t, val) def set_blocked(type: int | IterableLike[int] | None, *args: int): """ - set_blocked(type, /) -> None - set_blocked(typelist, /) -> None + set_blocked(type: int, *args) -> None + set_blocked(type: list) -> None set_blocked(None) -> None control which events are blocked on the queue @@ -285,8 +292,8 @@ def set_blocked(type: int | IterableLike[int] | None, *args: int): def set_allowed(type: int | IterableLike[int] | None, *args: int): """ - set_allowed(type, /) -> None - set_allowed(typelist, /) -> None + set_allowed(type: int, *args) -> None + set_allowed(type: list) -> None set_allowed(None) -> None control which events are allowed on the queue @@ -297,19 +304,26 @@ def set_allowed(type: int | IterableLike[int] | None, *args: int): def get_blocked(type: int | IterableLike[int], *args: int): """ - get_blocked(type, /) -> bool - get_blocked(typelist, /) -> bool + get_blocked(type: int, *args) -> bool + get_blocked(type: list) -> bool test if a type of event is blocked from the queue """ for t in _parse(type, args): + _check_ev_type(t) if not _allowed_get(t): return True return False def clear(eventtype: int | IterableLike[int] | None = None, pump: bool = True): + """ + clear(eventtype=None) -> None + clear(eventtype=None, pump=True) -> None + + remove all events from the queue + """ if eventtype is None or isinstance(eventtype, int): get(eventtype, pump) else: @@ -318,6 +332,76 @@ def clear(eventtype: int | IterableLike[int] | None = None, pump: bool = True): gc.collect() +def _get_many(ev_type: int, to: list | None = None, proxify: bool = True): + if to is None: + to = [] + + ev = _get(ev_type) + + while ev: + to.append(ev) + ev = _get(ev_type) + + if proxify: + to = _get_many(_proxify_event_type(ev_type), to, False) + + return to + + +def get( + eventtype: int | IterableLike[int] | None = None, + pump: bool = True, + exclude: int | IterableLike[int] | None = None, +): + """ + get(eventtype=None) -> Eventlist + get(eventtype=None, pump=True) -> Eventlist + get(eventtype=None, pump=True, exclude=None) -> Eventlist + + get events from the queue + """ + _video_check() + _pump(pump) + + if isinstance(eventtype, int): + eventtype = [eventtype] + + if isinstance(exclude, int): + exclude = [exclude] + + if eventtype is None: + if exclude is None: + # Get all events + return _get_many(-1, proxify=False) + + # Get all events except + excluded = [] + + for ev_type in exclude: + _check_ev_type(ev_type) + _get_many(ev_type, excluded) + + ret = _get_many(-1, proxify=False) + + for ev in excluded: + post(ev) + + del excluded + return ret + + if exclude is not None: + raise pg.error("Invalid combination of excluded and included event type") + + # Get all events of type + ret = [] + + for ev_type in eventtype: + _check_ev_type(ev_type) + _get_many(ev_type, ret) + + return ret + + __all__ = [ "Event", "EventType", From 55220a4d70a9ea679f9a85df41f1e3fd983fd09a Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Mon, 23 Sep 2024 17:52:49 +0200 Subject: [PATCH 28/36] Format --- buildconfig/stubs/pygame/typing.pyi | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index 2abf5b7a46..e77a167763 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -82,9 +82,7 @@ RectLike = Union[SequenceLike[float], SequenceLike[Coordinate], _HasRectAttribut class EventLike(Protocol): - def __init__( - self, type: int, /, **kwargs: Any - ) -> None: ... + def __init__(self, type: int, /, **kwargs: Any) -> None: ... def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... @property From 8a62db99dd094c609ba58a051fca4f80ef604eb6 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:27:25 +0200 Subject: [PATCH 29/36] Move peek() to python. --- buildconfig/stubs/pygame/_event.pyi | 2 +- buildconfig/stubs/pygame/event.pyi | 10 +-- docs/reST/ref/event.rst | 10 ++- src_c/_event.c | 118 +++++----------------------- src_c/doc/event_doc.h | 2 +- src_py/event.py | 48 +++++++++-- 6 files changed, 78 insertions(+), 112 deletions(-) diff --git a/buildconfig/stubs/pygame/_event.pyi b/buildconfig/stubs/pygame/_event.pyi index db29c39c01..e01d6db86b 100644 --- a/buildconfig/stubs/pygame/_event.pyi +++ b/buildconfig/stubs/pygame/_event.pyi @@ -6,11 +6,11 @@ _EventTypes = int | SequenceLike[int] def pump(dopump: bool, /) -> None: ... def _get(type: int, /) -> EventLike | None: ... +def _peek(type: int, /) -> bool: ... def _proxify_event_type(type: int, /) -> int: ... def video_check() -> None: ... def poll() -> EventLike: ... def wait(timeout: int = 0) -> EventLike: ... -def peek(eventtype: _EventTypes | None = None, pump: bool = True) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def allowed_get(type: int, /) -> bool: ... diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 8ed7108462..3ce4731876 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -22,10 +22,10 @@ def get( eventtype: Optional[_EventTypes] = None, pump: Any = True, exclude: Optional[_EventTypes] = None, -) -> list[Event]: ... -def poll() -> Event: ... -def wait(timeout: int = 0) -> Event: ... -def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> bool: ... +) -> list[EventLike]: ... +def poll() -> EventLike: ... +def wait(timeout: int = 0) -> EventLike: ... +def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> Union[bool, EventLike]: ... def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... def event_name(type: int) -> str: ... def set_blocked(type: Optional[_EventTypes], *args: int) -> None: ... @@ -33,7 +33,7 @@ def set_allowed(type: Optional[_EventTypes], *args: int) -> None: ... def get_blocked(type: _EventTypes, *args: int) -> bool: ... def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... -def post(event: Event, /) -> bool: ... +def post(event: EventLike, /) -> bool: ... def custom_type() -> int: ... def init() -> None: ... def quit() -> None: ... diff --git a/docs/reST/ref/event.rst b/docs/reST/ref/event.rst index 89562572a9..c7e6305acc 100644 --- a/docs/reST/ref/event.rst +++ b/docs/reST/ref/event.rst @@ -317,8 +317,9 @@ On Android, the following events can be generated .. function:: peek | :sl:`test if event types are waiting on the queue` - | :sg:`peek(eventtype=None) -> bool` - | :sg:`peek(eventtype=None, pump=True) -> bool` + | :sg:`peek() -> Event instance` + | :sg:`peek(eventtype) -> bool` + | :sg:`peek(eventtype, pump=True) -> bool` Returns ``True`` if there are any events of the given type waiting on the queue. If a sequence of event types is passed, this will return ``True`` if @@ -326,6 +327,11 @@ On Android, the following events can be generated If ``pump`` is ``True`` (the default), then :func:`pygame.event.pump()` will be called. + If ``eventtype`` is unspecified, or ``None``, then this function will return the top-most event instead. + + .. note:: + There is no guarantee that the event got with :func:`pygame.event.get()` immediately after calling this function will be the same. + .. versionchangedold:: 1.9.5 Added ``pump`` argument .. ## pygame.event.peek ## diff --git a/src_c/_event.c b/src_c/_event.c index 84529dbb19..2208fbc72a 100644 --- a/src_c/_event.c +++ b/src_c/_event.c @@ -1372,40 +1372,6 @@ pg_event_wait(PyObject *self, PyObject *args, PyObject *kwargs) return pgEvent_New(NULL); return pgEvent_New(&event); } -// TODO -static int -_pg_eventtype_from_seq(PyObject *seq, int ind) -{ - int val = 0; - if (!pg_IntFromObjIndex(seq, ind, &val)) { - PyErr_SetString(PyExc_TypeError, - "type sequence must contain valid event types"); - return -1; - } - if (val < 0 || val >= PG_NUMEVENTS) { - PyErr_SetString(PyExc_ValueError, "event type out of range"); - return -1; - } - return val; -} - -// TODO -static PyObject * -_pg_eventtype_as_seq(PyObject *obj, Py_ssize_t *len) -{ - *len = 1; - if (PySequence_Check(obj)) { - *len = PySequence_Size(obj); - // The returned object gets decref'd later, so incref now - Py_INCREF(obj); - return obj; - } - else if (PyLong_Check(obj)) - return Py_BuildValue("(O)", obj); - else - return RAISE(PyExc_TypeError, - "event type must be numeric or a sequence"); -} char * pgEvent_GetKeyDownInfo(void) @@ -1452,6 +1418,25 @@ _pg_get_event(PyObject *self, PyObject *obj) return pgEvent_New(&ev); } +static PyObject * +_pg_peek_event(PyObject *self, PyObject *obj) +{ + SDL_Event ev; + int e_type = PyLong_AsLong(obj); + + if (e_type == -1 && PyErr_Occurred()) + return NULL; + + int ret; + if (e_type == -1) + ret = PG_PEEP_EVENT_ALL(&ev, 1, SDL_PEEKEVENT); + else + ret = PG_PEEP_EVENT(&ev, 1, SDL_PEEKEVENT, e_type); + if (ret == -1) + return RAISE(pgExc_SDLError, SDL_GetError()); + return PyBool_FromLong(ret); +} + static PyObject * _pg_video_check(PyObject *self, PyObject *_null) { @@ -1470,66 +1455,6 @@ _pg_proxify_event_type(PyObject *self, PyObject *obj) return PyLong_FromLong(_pg_pgevent_proxify((Uint32)e_type)); } -// TODO -static PyObject * -pg_event_peek(PyObject *self, PyObject *args, PyObject *kwargs) -{ - SDL_Event event; - Py_ssize_t len; - int type, loop, res; - PyObject *seq, *obj = NULL; - int dopump = 1; - - static char *kwids[] = {"eventtype", "pump", NULL}; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Op", kwids, &obj, - &dopump)) - return NULL; - - VIDEO_INIT_CHECK(); - - _pg_event_pump(dopump); - - if (obj == NULL || obj == Py_None) { - res = PG_PEEP_EVENT_ALL(&event, 1, SDL_PEEKEVENT); - if (res < 0) - return RAISE(pgExc_SDLError, SDL_GetError()); - return pgEvent_New(res ? &event : NULL); - } - else { - seq = _pg_eventtype_as_seq(obj, &len); - if (!seq) - return NULL; - - for (loop = 0; loop < len; loop++) { - type = _pg_eventtype_from_seq(seq, loop); - if (type == -1) { - Py_DECREF(seq); - return NULL; - } - res = PG_PEEP_EVENT(&event, 1, SDL_PEEKEVENT, type); - if (res) { - Py_DECREF(seq); - - if (res < 0) - return RAISE(pgExc_SDLError, SDL_GetError()); - Py_RETURN_TRUE; - } - res = PG_PEEP_EVENT(&event, 1, SDL_PEEKEVENT, - _pg_pgevent_proxify(type)); - if (res) { - Py_DECREF(seq); - - if (res < 0) - return RAISE(pgExc_SDLError, SDL_GetError()); - Py_RETURN_TRUE; - } - } - Py_DECREF(seq); - Py_RETURN_FALSE; /* No event type match. */ - } -} - /* You might notice how we do event blocking stuff on proxy events and * not the real SDL events. We do this because we want SDL events to pass * through our event filter, to do emulation stuff correctly. Then the @@ -1647,10 +1572,6 @@ static PyMethodDef _event_methods[] = { {"wait", (PyCFunction)pg_event_wait, METH_VARARGS | METH_KEYWORDS, DOC_EVENT_WAIT}, {"poll", (PyCFunction)pg_event_poll, METH_NOARGS, DOC_EVENT_POLL}, - // {"get", (PyCFunction)pg_event_get, METH_VARARGS | METH_KEYWORDS, - // DOC_EVENT_GET}, - {"peek", (PyCFunction)pg_event_peek, METH_VARARGS | METH_KEYWORDS, - DOC_EVENT_PEEK}, {"post", (PyCFunction)pg_event_post, METH_O, DOC_EVENT_POST}, {"allowed_get", (PyCFunction)pg_event_allowed_get, METH_O}, @@ -1659,6 +1580,7 @@ static PyMethodDef _event_methods[] = { METH_O}, {"video_check", (PyCFunction)_pg_video_check, METH_NOARGS}, {"_get", (PyCFunction)_pg_get_event, METH_O}, + {"_peek", (PyCFunction)_pg_peek_event, METH_O}, {"_proxify_event_type", (PyCFunction)_pg_proxify_event_type, METH_O}, {NULL, NULL, 0, NULL}}; diff --git a/src_c/doc/event_doc.h b/src_c/doc/event_doc.h index 16b6f1b0a1..fc283d6eee 100644 --- a/src_c/doc/event_doc.h +++ b/src_c/doc/event_doc.h @@ -4,7 +4,7 @@ #define DOC_EVENT_GET "get(eventtype=None) -> Eventlist\nget(eventtype=None, pump=True) -> Eventlist\nget(eventtype=None, pump=True, exclude=None) -> Eventlist\nget events from the queue" #define DOC_EVENT_POLL "poll() -> Event instance\nget a single event from the queue" #define DOC_EVENT_WAIT "wait() -> Event instance\nwait(timeout) -> Event instance\nwait for a single event from the queue" -#define DOC_EVENT_PEEK "peek(eventtype=None) -> bool\npeek(eventtype=None, pump=True) -> bool\ntest if event types are waiting on the queue" +#define DOC_EVENT_PEEK "peek() -> Event instance\npeek(eventtype) -> bool\npeek(eventtype, pump=True) -> bool\ntest if event types are waiting on the queue" #define DOC_EVENT_CLEAR "clear(eventtype=None) -> None\nclear(eventtype=None, pump=True) -> None\nremove all events from the queue" #define DOC_EVENT_EVENTNAME "event_name(type, /) -> string\nget the string name from an event id" #define DOC_EVENT_SETBLOCKED "set_blocked(type, /) -> None\nset_blocked(typelist, /) -> None\nset_blocked(None) -> None\ncontrol which events are blocked on the queue" diff --git a/src_py/event.py b/src_py/event.py index 37c8844817..e718b9eced 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -4,8 +4,8 @@ import gc -from typing import Any -from .typing import IterableLike +from typing import Any, Union +from .typing import EventLike, IterableLike from pygame._event import ( _internal_mod_init as _init, @@ -18,10 +18,10 @@ post, get_grab, set_grab, - peek, wait, poll, _get, + _peek, _proxify_event_type, ) @@ -332,7 +332,9 @@ def clear(eventtype: int | IterableLike[int] | None = None, pump: bool = True): gc.collect() -def _get_many(ev_type: int, to: list | None = None, proxify: bool = True): +def _get_many( + ev_type: int, to: list | None = None, proxify: bool = True +) -> list[EventLike]: if to is None: to = [] @@ -352,7 +354,7 @@ def get( eventtype: int | IterableLike[int] | None = None, pump: bool = True, exclude: int | IterableLike[int] | None = None, -): +) -> list[EventLike]: """ get(eventtype=None) -> Eventlist get(eventtype=None, pump=True) -> Eventlist @@ -402,6 +404,42 @@ def get( return ret +def peek( + eventtype: int | IterableLike[int] | None = None, + pump: bool = True, +) -> Union[EventLike, bool]: + """ + peek() -> Event instance + peek(eventtype) -> bool + peek(eventtype, pump=True) -> bool + + test if event types are waiting on the queue + """ + _video_check() + _pump(pump) + + if isinstance(eventtype, int): + eventtype = [eventtype] + + if eventtype is None: + # Can't seek without mutating event queue here, + # Due to PgEvent_New being a destructive operation. + ret = _get(-1) + + if ret is None: + return Event(0) + + post(ret) + return ret + + for ev_type in eventtype: + _check_ev_type(ev_type) + + if _peek(ev_type) or _peek(_proxify_event_type(ev_type)): + return True + return False + + __all__ = [ "Event", "EventType", From a62e5297cd968ba42e257f1450cc94bb0c87b367 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 24 Sep 2024 20:37:37 +0200 Subject: [PATCH 30/36] Run updated pre-commit. --- docs/reST/c_api/event.rst | 3 +-- docs/reST/ref/typing.rst | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/reST/c_api/event.rst b/docs/reST/c_api/event.rst index 1568c295bc..bdca06f6d6 100644 --- a/docs/reST/c_api/event.rst +++ b/docs/reST/c_api/event.rst @@ -40,7 +40,7 @@ Header file: src_c/include/pygame.h .. c:function:: PyObject* pgEvent_FromTypeAndDict(int e_type, PyObject *dict) Instantiates a new Event object created from the given event type and a dict. - + On error returns NULL and sets python exception. .. c:function:: int pgEvent_GetEventType(PyObject *) @@ -85,4 +85,3 @@ Header file: src_c/include/pygame.h Nearly the same as :c:func:`pg_post_event`, but with two differences. 1) This doesn't need GIL held at all when called. 2) This steals the reference to obj, instead of borrowing it. - diff --git a/docs/reST/ref/typing.rst b/docs/reST/ref/typing.rst index 51a05ca4da..a08eeed8d7 100644 --- a/docs/reST/ref/typing.rst +++ b/docs/reST/ref/typing.rst @@ -74,7 +74,7 @@ type aliases for proper typehint annotations. * ``pygame.Rect(RectLike)`` * Any object with a ``.rect`` attribute which is a ``RectLike`` or a function returning a ``RectLike`` - + .. data:: EventLike A protocol representing an event that is undestood by pygame-ce. From 8a990c21f18781fa82adb9da6614b6d35b0abf14 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:10:35 +0200 Subject: [PATCH 31/36] Events as types in python. --- buildconfig/stubs/pygame/event.pyi | 417 +++++++++++++++++++++++++++- buildconfig/stubs/pygame/typing.pyi | 7 +- docs/reST/ref/event.rst | 94 +++++++ src_py/event.py | 412 +++++++++++++++++++++------ src_py/typing.py | 7 +- test/event_test.py | 46 ++- test/midi_test.py | 5 +- 7 files changed, 880 insertions(+), 108 deletions(-) diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 3ce4731876..67e502872b 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -1,20 +1,27 @@ -from typing import Any, Union, Optional, Dict +from typing import Any, Type, Union, Optional, Dict, overload, Tuple from pygame.typing import SequenceLike, EventLike +from pygame import Window, constants as _c class Event(EventLike): + type: int + + @overload def __init__( self, type: int, dict: Optional[Dict[str, Any]] = None, **kwargs: Any ) -> None: ... - def __new__(cls, *args: Any, **kwargs: Any) -> "Event": ... + @overload + def __init__( + self, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... + def __getattribute__(self, name: str) -> Any: ... def __setattr__(self, name: str, value: Any) -> None: ... def __delattr__(self, name: str) -> None: ... def __int__(self) -> int: ... def __bool__(self) -> bool: ... def __eq__(self, other: Any) -> bool: ... - _EventTypes = Union[int, SequenceLike[int]] def pump() -> None: ... @@ -35,7 +42,411 @@ def set_grab(grab: bool, /) -> None: ... def get_grab() -> bool: ... def post(event: EventLike, /) -> bool: ... def custom_type() -> int: ... +def event_class(type: int) -> Type[Event]: ... def init() -> None: ... def quit() -> None: ... EventType = Event + +# BEGIN Event subclasses +class ActiveEvent(Event): + type: int = _c.ACTIVEEVENT + gain: int + state: int + +class AppTerminating(Event): + type: int = _c.APP_TERMINATING + +class AppLowMemory(Event): + type: int = _c.APP_LOWMEMORY + +class AppWillEnterBackground(Event): + type: int = _c.APP_WILLENTERBACKGROUND + +class AppDidEnterBackground(Event): + type: int = _c.APP_DIDENTERBACKGROUND + +class AppWillEnterForeground(Event): + type: int = _c.APP_WILLENTERFOREGROUND + +class AppDidEnterForeground(Event): + type: int = _c.APP_DIDENTERFOREGROUND + +class ClipboardUpdate(Event): + type: int = _c.CLIPBOARDUPDATE + +class KeyDown(Event): + type: int = _c.KEYDOWN + unicode: str + key: int + mod: int + scancode: int + window: Optional[Window] + +class KeyUp(Event): + type: int = _c.KEYUP + unicode: str + key: int + mod: int + scancode: int + window: Optional[Window] + +class KeyMapChanged(Event): + type: int = _c.KEYMAPCHANGED + +class LocaleChanged(Event): + """Only for SDL 2.0.14+""" + type: int = _c.LOCALECHANGED + +class MouseMotion(Event): + type: int = _c.MOUSEMOTION + pos: Tuple[int, int] + rel: Tuple[int, int] + buttons: tuple[int, int, int] + touch: bool + window: Optional[Window] + +class MouseButtonDown(Event): + type: int = _c.MOUSEBUTTONDOWN + pos: Tuple[int, int] + button: int + touch: bool + window: Optional[Window] + +class MouseButtonUp(Event): + type: int = _c.MOUSEBUTTONUP + pos: Tuple[int, int] + button: int + touch: bool + window: Optional[Window] + +class JoyAxisMotion(Event): + """Attribute "joy" is depracated, use "instance_id".""" + type: int = _c.JOYAXISMOTION + joy: int + instance_id: int + axis: int + value: float + +class JoyBallMotion(Event): + """Attribute "joy" is depracated, use "instance_id".""" + type: int = _c.JOYBALLMOTION + joy: int + instance_id: int + ball: int + rel: Tuple[int, int] + +class JoyHatMotion(Event): + """Attribute "joy" is depracated, use "instance_id".""" + type: int = _c.JOYHATMOTION + joy: int + instance_id: int + hat: int + value: Tuple[int, int] + +class JoyButtonUp(Event): + """Attribute "joy" is depracated, use "instance_id".""" + type: int = _c.JOYBUTTONUP + joy: int + instance_id: int + button: int + +class JoyButtonDown(Event): + """Attribute "joy" is depracated, use "instance_id".""" + type: int = _c.JOYBUTTONDOWN + joy: int + instance_id: int + button: int + +class Quit(Event): + type: int = _c.QUIT + +class SysWMEvent(Event): + """ + Attributes are OS-depended: + hwnd, msg, wparam, lparam - Windows. + event - Unix / OpenBSD + For other OSes and in some cases for Unix / OpenBSD + this event won't have any attributes. + """ + type: int = _c.SYSWMEVENT + hwnd: int + msg: int + wparam: int + lparam: int + event: bytes + +class VideoResize(Event): + type: int = _c.VIDEORESIZE + size: Tuple[int, int] + w: int + h: int + +class VideoExpose(Event): + type: int = _c.VIDEOEXPOSE + +class MidiIn(Event): + type: int = _c.MIDIIN + +class MidiOut(Event): + type: int = _c.MIDIOUT + +class NoEvent(Event): + type: int = _c.NOEVENT + +class FingerMotion(Event): + """Attribute "window" avalible only for SDL 2.0.14+""" + type: int = _c.FINGERMOTION + touch_id: int + finger_id: int + x: float + y: float + dx: float + dy: float + pressure: float + window: Optional[Window] + +class FingerDown(Event): + """Attribute "window" avalible only for SDL 2.0.14+""" + type: int = _c.FINGERDOWN + touch_id: int + finger_id: int + x: float + y: float + dx: float + dy: float + pressure: float + window: Optional[Window] + +class FingerUp(Event): + """Attribute "window" avalible only for SDL 2.0.14+""" + type: int = _c.FINGERUP + touch_id: int + finger_id: int + x: float + y: float + dx: float + dy: float + pressure: float + window: Optional[Window] + +class MultiGesture(Event): + type: int = _c.MULTIGESTURE + touch_id: int + x: float + y: float + rotated: float + pinched: float + num_fingers: int + +class MouseWheel(Event): + type: int = _c.MOUSEWHEEL + flipped: bool + x: int + y: int + precise_x: float + precise_y: float + touch: bool + window: Optional[Window] + +class TextInput(Event): + type: int = _c.TEXTINPUT + text: str + window: Optional[Window] + +class TextEditing(Event): + type: int = _c.TEXTEDITING + text: str + start: int + length: int + window: Optional[Window] + +class DropFile(Event): + type: int = _c.DROPFILE + file: str + window: Optional[Window] + +class DropText(Event): + type: int = _c.DROPTEXT + text: str + window: Optional[Window] + +class DropBegin(Event): + type: int = _c.DROPBEGIN + window: Optional[Window] + +class DropComplete(Event): + type: int = _c.DROPCOMPLETE + window: Optional[Window] + +class ControllerAxisMotion(Event): + type: int = _c.CONTROLLERAXISMOTION + instance_id: int + axis: int + value: int + +class ControllerButtonDown(Event): + type: int = _c.CONTROLLERBUTTONDOWN + instance_id: int + button: int + +class ControllerButtonUp(Event): + type: int = _c.CONTROLLERBUTTONUP + instance_id: int + button: int + +class ControllerDeviceAdded(Event): + type: int = _c.CONTROLLERDEVICEADDED + device_index: int + guid: str + +class ControllerDeviceRemoved(Event): + type: int = _c.CONTROLLERDEVICEREMOVED + instance_id: int + +class ControllerDeviceMapped(Event): + type: int = _c.CONTROLLERDEVICEREMAPPED + instance_id: int + +class JoyDeviceAdded(Event): + type: int = _c.JOYDEVICEADDED + device_index: int + guid: str + +class JoyDeviceRemoved(Event): + type: int = _c.JOYDEVICEREMOVED + instance_id: int + +class ControllerTouchpadDown(Event): + """Only for SDL 2.0.14+""" + type: int = _c.CONTROLLERTOUCHPADDOWN + instance_id: int + touch_id: int + finger_id: int + x: float + y: float + pressure: float + +class ControllerTouchpadMotion(Event): + """Only for SDL 2.0.14+""" + type: int = _c.CONTROLLERTOUCHPADMOTION + instance_id: int + touch_id: int + finger_id: int + x: float + y: float + pressure: float + +class ControllerTouchpadUp(Event): + """Only for SDL 2.0.14+""" + type: int = _c.CONTROLLERTOUCHPADUP + instance_id: int + touch_id: int + finger_id: int + x: float + y: float + pressure: float + +class ControllerSensorUpdate(Event): + """Only for SDL 2.0.14+""" + type: int = _c.CONTROLLERSENSORUPDATE + +class AudioDeviceAdded(Event): + type: int = _c.AUDIODEVICEADDED + which: int + iscapture: int + +class AudioDeviceRemoved(Event): + type: int = _c.AUDIODEVICEREMOVED + which: int + iscapture: int + +class RenderTargetsReset(Event): + type: int = _c.RENDER_TARGETS_RESET + +class RenderDeviceReset(Event): + type: int = _c.RENDER_DEVICE_RESET + +class WindowShown(Event): + type: int = _c.WINDOWSHOWN + window: Optional[Window] + +class WindowHidden(Event): + type: int = _c.WINDOWHIDDEN + window: Optional[Window] + +class WindowExposed(Event): + type: int = _c.WINDOWEXPOSED + window: Optional[Window] + +class WindowMoved(Event): + type: int = _c.WINDOWMOVED + x: int + y: int + window: Optional[Window] + +class WindowResized(Event): + type: int = _c.WINDOWRESIZED + x: int + y: int + window: Optional[Window] + +class WindowSizeChanged(Event): + type: int = _c.WINDOWSIZECHANGED + x: int + y: int + window: Optional[Window] + +class WindowMinimized(Event): + type: int = _c.WINDOWMINIMIZED + window: Optional[Window] + +class WindowMaximized(Event): + type: int = _c.WINDOWMAXIMIZED + window: Optional[Window] + +class WindowRestored(Event): + type: int = _c.WINDOWRESTORED + window: Optional[Window] + +class WindowEnter(Event): + type: int = _c.WINDOWENTER + window: Optional[Window] + +class WindowLeave(Event): + type: int = _c.WINDOWLEAVE + window: Optional[Window] + +class WindowFocusGained(Event): + type: int = _c.WINDOWFOCUSGAINED + window: Optional[Window] + +class WindowFocusLost(Event): + type: int = _c.WINDOWFOCUSLOST + window: Optional[Window] + +class WindowClose(Event): + type: int = _c.WINDOWCLOSE + window: Optional[Window] + +class WindowTakeFocus(Event): + type: int = _c.WINDOWTAKEFOCUS + window: Optional[Window] + +class WindowHitTest(Event): + type: int = _c.WINDOWHITTEST + window: Optional[Window] + +class WindowICCProfChanged(Event): + type: int = _c.WINDOWICCPROFCHANGED + window: Optional[Window] + +class WindowDisplayChanged(Event): + type: int = _c.WINDOWDISPLAYCHANGED + display_index: int + window: Optional[Window] + +class UserEvent(Event): + type: int = _c.USEREVENT + +# END Event subclasses diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index 5ca99e6d26..6452aed800 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -79,11 +79,10 @@ RectLike = Union[SequenceLike[float], SequenceLike[Coordinate], _HasRectAttribut class EventLike(Protocol): - def __init__(self, type: int, /, **kwargs: Any) -> None: ... - def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... + type: int + + def __init__(self, dict: dict[str, Any] | None, **kwargs: Any) -> None: ... - @property - def type(self) -> int: ... @property def dict(self) -> Dict[str, Any]: ... diff --git a/docs/reST/ref/event.rst b/docs/reST/ref/event.rst index 2dc09c449f..cef587cf2d 100644 --- a/docs/reST/ref/event.rst +++ b/docs/reST/ref/event.rst @@ -469,6 +469,18 @@ On Android, the following events can be generated .. ## pygame.event.custom_type ## +.. function:: event_class + + | :sl:`returns related event class to event type` + | :sg:`event_class(type: int, /) -> type[Event]` + Returns an event class that is correlated with the given event type. If the class to a given event type is not found, + but the type is within the range of valid values for the event type, instead of a ``pygame.event.Event`` subclass, + ``pygame.event.Event`` itself will be returned, so don't rely on the retuned class having ``type`` attribute equal to a number. + This happens for example, with user event types that weren't created by subclassing ``pygame.event.Event``. + + .. versionadded:: 2.5.2 + .. ## pygame.event.event_class ## + .. class:: Event | :sl:`pygame object for representing events` @@ -484,6 +496,8 @@ On Android, the following events can be generated .. versionchanged:: 2.1.4 This class is also available through the ``pygame.Event`` alias. + .. versionchanged:: 2.5.0 This class can be subclassed to create user-defined event types. + .. note:: From version 2.1.3 ``EventType`` is an alias for ``Event``. Beforehand, ``Event`` was a function that returned ``EventType`` instances. Use of @@ -521,4 +535,84 @@ On Android, the following events can be generated .. ## pygame.event.Event ## +.. table:: List of Event subclasses available as aliases for event types importable as ``pygame.event.{Class name}``. + :widths: auto + + ============================= ============================= ========================== + Class name Alias to Notes + ============================= ============================= ========================== + ``ActiveEvent`` ``ACTIVEEVENT`` + ``AppTerminating`` ``APP_TERMINATING`` + ``AppLowMemory`` ``APP_LOWMEMORY`` + ``AppWillEnterBackground`` ``APP_WILLENTERBACKGROUND`` + ``AppDidEnterBackground`` ``APP_DIDENTERBACKGROUND`` + ``AppWillEnterForeground`` ``APP_WILLENTERFOREGROUND`` + ``AppDidEnterForeground`` ``APP_DIDENTERFOREGROUND`` + ``ClipboardUpdate`` ``CLIPBOARDUPDATE`` + ``KeyDown`` ``KEYDOWN`` + ``KeyUp`` ``KEYUP`` + ``KeyMapChanged`` ``KEYMAPCHANGED`` + ``LocaleChanged`` ``LOCALECHANGED`` Only for SDL 2.0.14+ + ``MouseMotion`` ``MOUSEMOTION`` + ``MouseButtonDown`` ``MOUSEBUTTONDOWN`` + ``MouseButtonUp`` ``MOUSEBUTTONUP`` + ``JoyAxisMotion`` ``JOYAXISMOTION`` + ``JoyBallMotion`` ``JOYBALLMOTION`` + ``JoyHatMotion`` ``JOYHATMOTION`` + ``JoyButtonUp`` ``JOYBUTTONUP`` + ``JoyButtonDown`` ``JOYBUTTONDOWN`` + ``Quit`` ``QUIT`` + ``SysWMEvent`` ``SYSWMEVENT`` + ``VideoResize`` ``VIDEORESIZE`` + ``VideoExpose`` ``VIDEOEXPOSE`` + ``MidiIn`` ``MIDIIN`` + ``MidiOut`` ``MIDIOUT`` + ``NoEvent`` ``NOEVENT`` + ``FingerMotion`` ``FINGERMOTION`` + ``FingerDown`` ``FINGERDOWN`` + ``FingerUp`` ``FINGERUP`` + ``MultiGesture`` ``MULTIGESTURE`` + ``MouseWheel`` ``MOUSEWHEEL`` + ``TextInput`` ``TEXTINPUT`` + ``TextEditing`` ``TEXTEDITING`` + ``DropFile`` ``DROPFILE`` + ``DropText`` ``DROPTEXT`` + ``DropBegin`` ``DROPBEGIN`` + ``DropComplete`` ``DROPCOMPLETE`` + ``ControllerAxisMotion`` ``CONTROLLERAXISMOTION`` + ``ControllerButtonDown`` ``CONTROLLERBUTTONDOWN`` + ``ControllerButtonUp`` ``CONTROLLERBUTTONUP`` + ``ControllerDeviceAdded`` ``CONTROLLERDEVICEADDED`` + ``ControllerDeviceRemoved`` ``CONTROLLERDEVICEREMOVED`` + ``ControllerDeviceMapped`` ``CONTROLLERDEVICEREMAPPED`` + ``JoyDeviceAdded`` ``JOYDEVICEADDED`` + ``JoyDeviceRemoved`` ``JOYDEVICEREMOVED`` + ``ControllerTouchpadDown`` ``CONTROLLERTOUCHPADDOWN`` Only for SDL 2.0.14+ + ``ControllerTouchpadMotion`` ``CONTROLLERTOUCHPADMOTION`` Only for SDL 2.0.14+ + ``ControllerTouchpadUp`` ``CONTROLLERTOUCHPADUP`` Only for SDL 2.0.14+ + ``ControllerSensorUpdate`` ``CONTROLLERSENSORUPDATE`` Only for SDL 2.0.14+ + ``AudioDeviceAdded`` ``AUDIODEVICEADDED`` + ``AudioDeviceRemoved`` ``AUDIODEVICEREMOVED`` + ``RenderTargetsReset`` ``RENDER_TARGETS_RESET`` + ``RenderDeviceReset`` ``RENDER_DEVICE_RESET`` + ``WindowShown`` ``WINDOWSHOWN`` + ``WindowHidden`` ``WINDOWHIDDEN`` + ``WindowExposed`` ``WINDOWEXPOSED`` + ``WindowMoved`` ``WINDOWMOVED`` + ``WindowResized`` ``WINDOWRESIZED`` + ``WindowSizeChanged`` ``WINDOWSIZECHANGED`` + ``WindowMinimized`` ``WINDOWMINIMIZED`` + ``WindowMaximized`` ``WINDOWMAXIMIZED`` + ``WindowRestored`` ``WINDOWRESTORED`` + ``WindowEnter`` ``WINDOWENTER`` + ``WindowLeave`` ``WINDOWLEAVE`` + ``WindowFocusGained`` ``WINDOWFOCUSGAINED`` + ``WindowFocusLost`` ``WINDOWFOCUSLOST`` + ``WindowClose`` ``WINDOWCLOSE`` + ``WindowTakeFocus`` ``WINDOWTAKEFOCUS`` + ``WindowHitTest`` ``WINDOWHITTEST`` + ``WindowICCProfChanged`` ``WINDOWICCPROFCHANGED`` + ``WindowDisplayChanged`` ``WINDOWDISPLAYCHANGED`` + ============================= ============================= ========================== + .. ## pygame.event ## diff --git a/src_py/event.py b/src_py/event.py index e718b9eced..9ab1915300 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -4,8 +4,8 @@ import gc -from typing import Any, Union -from .typing import EventLike, IterableLike +from typing import Any, Dict, Type, Union, overload +from pygame.typing import EventLike, IterableLike from pygame._event import ( _internal_mod_init as _init, @@ -31,80 +31,6 @@ _is_init = False _custom_event = pg.USEREVENT + 1 -_NAMES_MAPPING = { - pg.ACTIVEEVENT: "ActiveEvent", - pg.APP_TERMINATING: "AppTerminating", - pg.APP_LOWMEMORY: "AppLowMemory", - pg.APP_WILLENTERBACKGROUND: "AppWillEnterBackground", - pg.APP_DIDENTERBACKGROUND: "AppDidEnterBackground", - pg.APP_WILLENTERFOREGROUND: "AppWillEnterForeground", - pg.APP_DIDENTERFOREGROUND: "AppDidEnterForeground", - pg.CLIPBOARDUPDATE: "ClipboardUpdate", - pg.KEYDOWN: "KeyDown", - pg.KEYUP: "KeyUp", - pg.KEYMAPCHANGED: "KeyMapChanged", - pg.LOCALECHANGED: "LocaleChanged", - pg.MOUSEMOTION: "MouseMotion", - pg.MOUSEBUTTONDOWN: "MouseButtonDown", - pg.MOUSEBUTTONUP: "MouseButtonUp", - pg.JOYAXISMOTION: "JoyAxisMotion", - pg.JOYBALLMOTION: "JoyBallMotion", - pg.JOYHATMOTION: "JoyHatMotion", - pg.JOYBUTTONUP: "JoyButtonUp", - pg.JOYBUTTONDOWN: "JoyButtonDown", - pg.QUIT: "Quit", - pg.SYSWMEVENT: "SysWMEvent", - pg.VIDEORESIZE: "VideoResize", - pg.VIDEOEXPOSE: "VideoExpose", - pg.MIDIIN: "MidiIn", - pg.MIDIOUT: "MidiOut", - pg.NOEVENT: "NoEvent", - pg.FINGERMOTION: "FingerMotion", - pg.FINGERDOWN: "FingerDown", - pg.FINGERUP: "FingerUp", - pg.MULTIGESTURE: "MultiGesture", - pg.MOUSEWHEEL: "MouseWheel", - pg.TEXTINPUT: "TextInput", - pg.TEXTEDITING: "TextEditing", - pg.DROPFILE: "DropFile", - pg.DROPTEXT: "DropText", - pg.DROPBEGIN: "DropBegin", - pg.DROPCOMPLETE: "DropComplete", - pg.CONTROLLERAXISMOTION: "ControllerAxisMotion", - pg.CONTROLLERBUTTONDOWN: "ControllerButtonDown", - pg.CONTROLLERBUTTONUP: "ControllerButtonUp", - pg.CONTROLLERDEVICEADDED: "ControllerDeviceAdded", - pg.CONTROLLERDEVICEREMOVED: "ControllerDeviceRemoved", - pg.CONTROLLERDEVICEREMAPPED: "ControllerDeviceMapped", - pg.JOYDEVICEADDED: "JoyDeviceAdded", - pg.JOYDEVICEREMOVED: "JoyDeviceRemoved", - pg.CONTROLLERTOUCHPADDOWN: "ControllerTouchpadDown", - pg.CONTROLLERTOUCHPADMOTION: "ControllerTouchpadMotion", - pg.CONTROLLERTOUCHPADUP: "ControllerTouchpadUp", - pg.CONTROLLERSENSORUPDATE: "ControllerSensorUpdate", - pg.AUDIODEVICEADDED: "AudioDeviceAdded", - pg.AUDIODEVICEREMOVED: "AudioDeviceRemoved", - pg.RENDER_TARGETS_RESET: "RenderTargetsReset", - pg.RENDER_DEVICE_RESET: "RenderDeviceReset", - pg.WINDOWSHOWN: "WindowShown", - pg.WINDOWHIDDEN: "WindowHidden", - pg.WINDOWEXPOSED: "WindowExposed", - pg.WINDOWMOVED: "WindowMoved", - pg.WINDOWRESIZED: "WindowResized", - pg.WINDOWSIZECHANGED: "WindowSizeChanged", - pg.WINDOWMINIMIZED: "WindowMinimized", - pg.WINDOWMAXIMIZED: "WindowMaximized", - pg.WINDOWRESTORED: "WindowRestored", - pg.WINDOWENTER: "WindowEnter", - pg.WINDOWLEAVE: "WindowLeave", - pg.WINDOWFOCUSGAINED: "WindowFocusGained", - pg.WINDOWFOCUSLOST: "WindowFocusLost", - pg.WINDOWCLOSE: "WindowClose", - pg.WINDOWTAKEFOCUS: "WindowTakeFocus", - pg.WINDOWHITTEST: "WindowHitTest", - pg.WINDOWICCPROFCHANGED: "WindowICCProfChanged", - pg.WINDOWDISPLAYCHANGED: "WindowDisplayChanged", -} def event_name(type: int) -> str: @@ -114,8 +40,8 @@ def event_name(type: int) -> str: get the string name from an event id """ - if type in _NAMES_MAPPING: - return _NAMES_MAPPING[type] + if type in _events_map: + return _events_map[type].__name__ if pg.USEREVENT <= type < pg.NUMEVENTS: return "UserEvent" return "Unknown" @@ -129,7 +55,44 @@ def _check_ev_type(ev_type): raise ValueError("event type out of range") -class Event: +_events_map: Dict[int, Type["Event"]] = {} + + +def _unknown_event_factory(ev_type: int) -> Type[Event]: + if ev_type >= pg.USEREVENT: + + class UserEvent(Event): + type = ev_type + _unknown: bool = True + + return UserEvent + + class UnknownEvent(Event): + type = ev_type + _unknown: bool = True + + return UnknownEvent + + +def event_class(ev_type: int) -> type[EventLike]: + _check_ev_type(ev_type) + + if ev_type not in _events_map: + _events_map[ev_type] = _unknown_event_factory(ev_type) + return _events_map[ev_type] + + +class _EventMeta(type): + def _create_of_type(self, type: int, *args, **kwds): + return event_class(type)(*args, **kwds) + + def __call__(self, *args: Any, **kwds: Any) -> Any: + if self is Event: + return self._create_of_type(*args, **kwds) + return super(_EventMeta, self).__call__(*args, **kwds) + + +class Event(metaclass=_EventMeta): """ Event(type, dict) -> Event Event(type, **attributes) -> Event @@ -137,9 +100,19 @@ class Event: pygame object for representing events """ - def __init__(self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any): - _check_ev_type(type) + type: int = -1 + + @overload + def __init__( + self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any + ): ... + @overload + def __init__(self, dict: dict[str, Any] | None = None, **kwargs: Any): ... + def __init__(self, *args, **kwargs) -> None: + self.__init(*args, **kwargs) + + def __init(self, dict: dict[str, Any] | None = None, **kwargs: Any): if dict is None: dict = kwargs else: @@ -148,13 +121,12 @@ def __init__(self, type: int, dict: dict[str, Any] | None = None, **kwargs: Any) if "type" in dict: raise ValueError("redundant type field in event dict") - self._type = type self._dict = dict - def __new__(cls, *args: Any, **kwargs: Any): - if "type" in kwargs: - raise ValueError("redundant type field in event dict") - return super().__new__(cls) + def __init_subclass__(cls) -> None: + if getattr(cls, "type", -1) == -1: + cls.type = custom_type() + _events_map[cls.type] = cls def __int__(self): return self.type @@ -168,17 +140,19 @@ def __eq__(self, other: Any): return self.type == other.type and self.dict == other.dict def __repr__(self): - return f".{type(self).__name__}({self.type} {self.dict})>" + return f"<{type(self).__module__}.{type(self).__qualname__}({self.dict})>" @property def dict(self): return self._dict - @property - def type(self): - return self._type - def __getattr__(self, name: str) -> Any: + if name not in self._dict: + raise AttributeError( + f"{self.__class__.__name__} object has no attribute {name!r}" + ) return self._dict[name] def __getattribute__(self, name: str): @@ -187,7 +161,11 @@ def __getattribute__(self, name: str): return super().__getattribute__(name) def __setattr__(self, name: str, value: Any): - if name in ("_type", "_dict", "type", "dict"): + if name == "type": + raise AttributeError( + f"attribute 'type' of 'Event' or its subclass object is protected" + ) + elif name in ("_dict", "dict"): super().__setattr__(name, value) else: self._dict[name] = value @@ -220,6 +198,8 @@ def quit(): # returning new types when they are finished, # without that test preventing further tests from getting a custom event type. _custom_event = pg.USEREVENT + 1 + _events_map.clear() + _events_map.update(_extra_cache) _quit() _is_init = False @@ -440,9 +420,184 @@ def peek( return False +_extra_cache = {} + + +def _create_class(type: int, name: str, note: str | None): + ret = _EventMeta(name, (Event,), {"type": type, "__doc__": note}) + _extra_cache[type] = ret + return ret + + +# To regenerate class definitions: +# 1) Paste class declarations from buildconfig/stubs/pygame/event.pyi above +# 2) Run the code below and paste the output. + +# def const_find(vl, guess: str): +# keys: list[str] = [key for key, value in pg.constants.__dict__.items() if value == vl] +# if len(keys) < 1: +# raise ValueError(f"{guess}:{vl} not found int the dict") +# elif len(keys) == 1: +# return keys[0] +# for k in keys: +# if k.replace("_", "") == guess.upper(): +# return k +# raise ValueError(f"{guess}:{vl} unresolved: {keys}") + +# for cls in _events_map.values(): +# if cls.__name__ == "UserEvent": continue +# print(f"{cls.__name__} = _create_class(pg.{const_find(cls.type, cls.__name__)}, {cls.__qualname__!r}, {cls.__doc__!r})") + + +ActiveEvent = _create_class(pg.ACTIVEEVENT, 'ActiveEvent', None) +AppTerminating = _create_class(pg.APP_TERMINATING, 'AppTerminating', None) +AppLowMemory = _create_class(pg.APP_LOWMEMORY, 'AppLowMemory', None) +AppWillEnterBackground = _create_class( + pg.APP_WILLENTERBACKGROUND, 'AppWillEnterBackground', None +) +AppDidEnterBackground = _create_class( + pg.APP_DIDENTERBACKGROUND, 'AppDidEnterBackground', None +) +AppWillEnterForeground = _create_class( + pg.APP_WILLENTERFOREGROUND, 'AppWillEnterForeground', None +) +AppDidEnterForeground = _create_class( + pg.APP_DIDENTERFOREGROUND, 'AppDidEnterForeground', None +) +ClipboardUpdate = _create_class(pg.CLIPBOARDUPDATE, 'ClipboardUpdate', None) +KeyDown = _create_class(pg.KEYDOWN, 'KeyDown', None) +KeyUp = _create_class(pg.KEYUP, 'KeyUp', None) +KeyMapChanged = _create_class(pg.KEYMAPCHANGED, 'KeyMapChanged', None) +LocaleChanged = _create_class(pg.LOCALECHANGED, 'LocaleChanged', 'Only for SDL 2.0.14+') +MouseMotion = _create_class(pg.MOUSEMOTION, 'MouseMotion', None) +MouseButtonDown = _create_class(pg.MOUSEBUTTONDOWN, 'MouseButtonDown', None) +MouseButtonUp = _create_class(pg.MOUSEBUTTONUP, 'MouseButtonUp', None) +JoyAxisMotion = _create_class( + pg.JOYAXISMOTION, + 'JoyAxisMotion', + 'Attribute "joy" is depracated, use "instance_id".', +) +JoyBallMotion = _create_class( + pg.JOYBALLMOTION, + 'JoyBallMotion', + 'Attribute "joy" is depracated, use "instance_id".', +) +JoyHatMotion = _create_class( + pg.JOYHATMOTION, 'JoyHatMotion', 'Attribute "joy" is depracated, use "instance_id".' +) +JoyButtonUp = _create_class( + pg.JOYBUTTONUP, 'JoyButtonUp', 'Attribute "joy" is depracated, use "instance_id".' +) +JoyButtonDown = _create_class( + pg.JOYBUTTONDOWN, + 'JoyButtonDown', + 'Attribute "joy" is depracated, use "instance_id".', +) +Quit = _create_class(pg.QUIT, 'Quit', None) +SysWMEvent = _create_class( + pg.SYSWMEVENT, + 'SysWMEvent', + "\n Attributes are OS-depended:\n hwnd, msg, wparam, lparam - Windows.\n event - Unix / OpenBSD\n For other OSes and in some cases for Unix / OpenBSD\n this event won't have any attributes.\n ", +) +VideoResize = _create_class(pg.VIDEORESIZE, 'VideoResize', None) +VideoExpose = _create_class(pg.VIDEOEXPOSE, 'VideoExpose', None) +MidiIn = _create_class(pg.MIDIIN, 'MidiIn', None) +MidiOut = _create_class(pg.MIDIOUT, 'MidiOut', None) +NoEvent = _create_class(pg.NOEVENT, 'NoEvent', None) +FingerMotion = _create_class( + pg.FINGERMOTION, 'FingerMotion', 'Attribute "window" avalible only for SDL 2.0.14+' +) +FingerDown = _create_class( + pg.FINGERDOWN, 'FingerDown', 'Attribute "window" avalible only for SDL 2.0.14+' +) +FingerUp = _create_class( + pg.FINGERUP, 'FingerUp', 'Attribute "window" avalible only for SDL 2.0.14+' +) +MultiGesture = _create_class(pg.MULTIGESTURE, 'MultiGesture', None) +MouseWheel = _create_class(pg.MOUSEWHEEL, 'MouseWheel', None) +TextInput = _create_class(pg.TEXTINPUT, 'TextInput', None) +TextEditing = _create_class(pg.TEXTEDITING, 'TextEditing', None) +DropFile = _create_class(pg.DROPFILE, 'DropFile', None) +DropText = _create_class(pg.DROPTEXT, 'DropText', None) +DropBegin = _create_class(pg.DROPBEGIN, 'DropBegin', None) +DropComplete = _create_class(pg.DROPCOMPLETE, 'DropComplete', None) +ControllerAxisMotion = _create_class( + pg.CONTROLLERAXISMOTION, 'ControllerAxisMotion', None +) +ControllerButtonDown = _create_class( + pg.CONTROLLERBUTTONDOWN, 'ControllerButtonDown', None +) +ControllerButtonUp = _create_class(pg.CONTROLLERBUTTONUP, 'ControllerButtonUp', None) +ControllerDeviceAdded = _create_class( + pg.CONTROLLERDEVICEADDED, 'ControllerDeviceAdded', None +) +ControllerDeviceRemoved = _create_class( + pg.CONTROLLERDEVICEREMOVED, 'ControllerDeviceRemoved', None +) +ControllerDeviceMapped = _create_class( + pg.CONTROLLERDEVICEREMAPPED, 'ControllerDeviceMapped', None +) +JoyDeviceAdded = _create_class(pg.JOYDEVICEADDED, 'JoyDeviceAdded', None) +JoyDeviceRemoved = _create_class(pg.JOYDEVICEREMOVED, 'JoyDeviceRemoved', None) +ControllerTouchpadDown = _create_class( + pg.CONTROLLERTOUCHPADDOWN, 'ControllerTouchpadDown', 'Only for SDL 2.0.14+' +) +ControllerTouchpadMotion = _create_class( + pg.CONTROLLERTOUCHPADMOTION, 'ControllerTouchpadMotion', 'Only for SDL 2.0.14+' +) +ControllerTouchpadUp = _create_class( + pg.CONTROLLERTOUCHPADUP, 'ControllerTouchpadUp', 'Only for SDL 2.0.14+' +) +ControllerSensorUpdate = _create_class( + pg.CONTROLLERSENSORUPDATE, 'ControllerSensorUpdate', 'Only for SDL 2.0.14+' +) +AudioDeviceAdded = _create_class(pg.AUDIODEVICEADDED, 'AudioDeviceAdded', None) +AudioDeviceRemoved = _create_class(pg.AUDIODEVICEREMOVED, 'AudioDeviceRemoved', None) +RenderTargetsReset = _create_class(pg.RENDER_TARGETS_RESET, 'RenderTargetsReset', None) +RenderDeviceReset = _create_class(pg.RENDER_DEVICE_RESET, 'RenderDeviceReset', None) +WindowShown = _create_class(pg.WINDOWSHOWN, 'WindowShown', None) +WindowHidden = _create_class(pg.WINDOWHIDDEN, 'WindowHidden', None) +WindowExposed = _create_class(pg.WINDOWEXPOSED, 'WindowExposed', None) +WindowMoved = _create_class(pg.WINDOWMOVED, 'WindowMoved', None) +WindowResized = _create_class(pg.WINDOWRESIZED, 'WindowResized', None) +WindowSizeChanged = _create_class(pg.WINDOWSIZECHANGED, 'WindowSizeChanged', None) +WindowMinimized = _create_class(pg.WINDOWMINIMIZED, 'WindowMinimized', None) +WindowMaximized = _create_class(pg.WINDOWMAXIMIZED, 'WindowMaximized', None) +WindowRestored = _create_class(pg.WINDOWRESTORED, 'WindowRestored', None) +WindowEnter = _create_class(pg.WINDOWENTER, 'WindowEnter', None) +WindowLeave = _create_class(pg.WINDOWLEAVE, 'WindowLeave', None) +WindowFocusGained = _create_class(pg.WINDOWFOCUSGAINED, 'WindowFocusGained', None) +WindowFocusLost = _create_class(pg.WINDOWFOCUSLOST, 'WindowFocusLost', None) +WindowClose = _create_class(pg.WINDOWCLOSE, 'WindowClose', None) +WindowTakeFocus = _create_class(pg.WINDOWTAKEFOCUS, 'WindowTakeFocus', None) +WindowHitTest = _create_class(pg.WINDOWHITTEST, 'WindowHitTest', None) +WindowICCProfChanged = _create_class( + pg.WINDOWICCPROFCHANGED, 'WindowICCProfChanged', None +) +WindowDisplayChanged = _create_class( + pg.WINDOWDISPLAYCHANGED, 'WindowDisplayChanged', None +) + + +class UserEvent(Event): + """ + User defined event. To create your own, subclass Event instead. + You can test if an event is UserEvent by calling: + isinstance(event, pygame.event.UserEvent) + """ + + type: int = pg.USEREVENT + + def __instancecheck__(self, instance: Any, /) -> bool: + return ( + isinstance(instance, Event) and pg.USEREVENT <= instance.type < pg.NUMEVENTS + ) + + __all__ = [ "Event", "EventType", + "event_class", "pump", "get", "poll", @@ -459,4 +614,77 @@ def peek( "custom_type", "init", "quit", + "ActiveEvent", + "AppTerminating", + "AppLowMemory", + "AppWillEnterBackground", + "AppDidEnterBackground", + "AppWillEnterForeground", + "AppDidEnterForeground", + "ClipboardUpdate", + "KeyDown", + "KeyUp", + "KeyMapChanged", + "LocaleChanged", + "MouseMotion", + "MouseButtonDown", + "MouseButtonUp", + "JoyAxisMotion", + "JoyBallMotion", + "JoyHatMotion", + "JoyButtonUp", + "JoyButtonDown", + "Quit", + "SysWMEvent", + "VideoResize", + "VideoExpose", + "MidiIn", + "MidiOut", + "NoEvent", + "FingerMotion", + "FingerDown", + "FingerUp", + "MultiGesture", + "MouseWheel", + "TextInput", + "TextEditing", + "DropFile", + "DropText", + "DropBegin", + "DropComplete", + "ControllerAxisMotion", + "ControllerButtonDown", + "ControllerButtonUp", + "ControllerDeviceAdded", + "ControllerDeviceRemoved", + "ControllerDeviceMapped", + "JoyDeviceAdded", + "JoyDeviceRemoved", + "ControllerTouchpadDown", + "ControllerTouchpadMotion", + "ControllerTouchpadUp", + "ControllerSensorUpdate", + "AudioDeviceAdded", + "AudioDeviceRemoved", + "RenderTargetsReset", + "RenderDeviceReset", + "WindowShown", + "WindowHidden", + "WindowExposed", + "WindowMoved", + "WindowResized", + "WindowSizeChanged", + "WindowMinimized", + "WindowMaximized", + "WindowRestored", + "WindowEnter", + "WindowLeave", + "WindowFocusGained", + "WindowFocusLost", + "WindowClose", + "WindowTakeFocus", + "WindowHitTest", + "WindowICCProfChanged", + "WindowDisplayChanged", + "UserEvent", ] diff --git a/src_py/typing.py b/src_py/typing.py index 5ca99e6d26..6452aed800 100644 --- a/src_py/typing.py +++ b/src_py/typing.py @@ -79,11 +79,10 @@ def rect(self) -> Union["RectLike", Callable[[], "RectLike"]]: ... class EventLike(Protocol): - def __init__(self, type: int, /, **kwargs: Any) -> None: ... - def __new__(cls, *args: Any, **kwargs: Any) -> "EventLike": ... + type: int + + def __init__(self, dict: dict[str, Any] | None, **kwargs: Any) -> None: ... - @property - def type(self) -> int: ... @property def dict(self) -> Dict[str, Any]: ... diff --git a/test/event_test.py b/test/event_test.py index 43e4783eee..0870f403f4 100644 --- a/test/event_test.py +++ b/test/event_test.py @@ -118,7 +118,7 @@ def test_Event(self): self.assertIn(attr, d) # redundant type field as kwarg - self.assertRaises(ValueError, pygame.event.Event, 10, type=100) + self.assertRaises(TypeError, pygame.event.Event, 10, type=100) def test_as_str(self): # Bug reported on Pygame mailing list July 24, 2011: @@ -294,6 +294,20 @@ def test_custom_type(self): self.assertEqual(len(queue), 1) self.assertEqual(queue[0].type, atype) + def test_subclass(self): + class MyEvent(pygame.event.Event): + pass + + self.assertEqual(MyEvent.type, pygame.event.custom_type() - 1) + + event = MyEvent() + self.assertEqual(event.type, MyEvent.type) + self.assertIs(MyEvent, pygame.event.event_class(MyEvent.type)) + + d = {"arg": "val"} + event = MyEvent(d) + self.assertIs(event.dict, d) + def test_custom_type__end_boundary(self): """Ensure custom_type() raises error when no more custom types. @@ -485,6 +499,17 @@ def test_post_same_reference(self): self.assertEqual(e.type, event.type) self.assertIs(e.dict, event.dict) + def test_post_same_object(self): + pygame.event.clear() + + for ev_type in EVENT_TYPES: + event = pygame.event.Event(ev_type, EVENT_TEST_PARAMS[ev_type]) + pygame.event.post(event) + + self.assertIs( + pygame.event.get(ev_type)[0], event, race_condition_notification + ) + def test_post_blocked(self): """ Test blocked events are not posted. Also test whether post() @@ -919,6 +944,25 @@ def test_poll(self): self.assertEqual(pygame.event.poll().type, e3.type) self.assertEqual(pygame.event.poll().type, pygame.NOEVENT) + def test_event_class(self): + for ev_type in EVENT_TYPES: + self.assertEqual(pygame.event.event_class(ev_type).type, ev_type) + + classes = [ + (pygame.KEYDOWN, pygame.event.KeyDown), + (pygame.KEYUP, pygame.event.KeyUp), + (pygame.NOEVENT, pygame.event.NoEvent), + ] + + for ev_id, ev_cls in classes: + self.assertIs(ev_cls, pygame.event.event_class(ev_id)) + self.assertEqual(ev_cls.type, ev_id) + + for ev_id in EVENT_TYPES: + self.assertFalse( + getattr(pygame.event.event_class(ev_id), "_unknown", False) + ) + class EventModuleTestsWithTiming(unittest.TestCase): __tags__ = ["timing"] diff --git a/test/midi_test.py b/test/midi_test.py index f4189a2351..2b7bf8c20e 100644 --- a/test/midi_test.py +++ b/test/midi_test.py @@ -398,10 +398,7 @@ def test_midis2events(self): midi_event = midi_events[i] midi_event_data = midi_event[MIDI_DATA] - # Can't directly check event instance as pygame.event.Event is - # a function. - # self.assertIsInstance(pg_event, pygame.event.Event) - self.assertEqual(pg_event.__class__.__name__, "Event") + self.assertIsInstance(pg_event, pygame.event.Event) self.assertEqual(pg_event.type, pygame.MIDIIN) self.assertEqual(pg_event.status, midi_event_data[MD_STATUS]) self.assertEqual(pg_event.data1, midi_event_data[MD_DATA1]) From 537c5274b3a56016142b74e76107d76da828621b Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Wed, 25 Sep 2024 20:35:13 +0200 Subject: [PATCH 32/36] Bugfix. --- docs/reST/ref/event.rst | 5 ++ src_c/doc/event_doc.h | 1 + src_py/event.py | 190 +++++++++++++++++++++------------------- src_py/typing.py | 4 +- 4 files changed, 110 insertions(+), 90 deletions(-) diff --git a/docs/reST/ref/event.rst b/docs/reST/ref/event.rst index cef587cf2d..8e4ea052ff 100644 --- a/docs/reST/ref/event.rst +++ b/docs/reST/ref/event.rst @@ -473,6 +473,7 @@ On Android, the following events can be generated | :sl:`returns related event class to event type` | :sg:`event_class(type: int, /) -> type[Event]` + Returns an event class that is correlated with the given event type. If the class to a given event type is not found, but the type is within the range of valid values for the event type, instead of a ``pygame.event.Event`` subclass, ``pygame.event.Event`` itself will be returned, so don't rely on the retuned class having ``type`` attribute equal to a number. @@ -615,4 +616,8 @@ On Android, the following events can be generated ``WindowDisplayChanged`` ``WINDOWDISPLAYCHANGED`` ============================= ============================= ========================== +.. note:: + + While instantiating these subclasses, don't pass the ``type`` argument to ``{Class name}.__init__()`` + .. ## pygame.event ## diff --git a/src_c/doc/event_doc.h b/src_c/doc/event_doc.h index fc283d6eee..865d4b23d8 100644 --- a/src_c/doc/event_doc.h +++ b/src_c/doc/event_doc.h @@ -14,6 +14,7 @@ #define DOC_EVENT_GETGRAB "get_grab() -> bool\ntest if the program is sharing input devices" #define DOC_EVENT_POST "post(event, /) -> bool\nplace a new event on the queue" #define DOC_EVENT_CUSTOMTYPE "custom_type() -> int\nmake custom user event type" +#define DOC_EVENT_EVENTCLASS "event_class(type: int, /) -> type[Event]\nreturns related event class to event type" #define DOC_EVENT_EVENT "Event(type, dict) -> Event\nEvent(type, **attributes) -> Event\npygame object for representing events" #define DOC_EVENT_EVENT_TYPE "type -> int\nevent type identifier." #define DOC_EVENT_EVENT_DICT "__dict__ -> dict\nevent attribute dictionary" diff --git a/src_py/event.py b/src_py/event.py index 9ab1915300..185217cdf6 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -25,7 +25,6 @@ _proxify_event_type, ) -from pygame.base import error import pygame as pg @@ -74,22 +73,22 @@ class UnknownEvent(Event): return UnknownEvent -def event_class(ev_type: int) -> type[EventLike]: - _check_ev_type(ev_type) +def event_class(type: int) -> type[EventLike]: + _check_ev_type(type) - if ev_type not in _events_map: - _events_map[ev_type] = _unknown_event_factory(ev_type) - return _events_map[ev_type] + if type not in _events_map: + _events_map[type] = _unknown_event_factory(type) + return _events_map[type] class _EventMeta(type): - def _create_of_type(self, type: int, *args, **kwds): + def _create_of_type(cls, type: int, *args, **kwds): return event_class(type)(*args, **kwds) - def __call__(self, *args: Any, **kwds: Any) -> Any: - if self is Event: - return self._create_of_type(*args, **kwds) - return super(_EventMeta, self).__call__(*args, **kwds) + def __call__(cls, *args: Any, **kwds: Any) -> Any: + if cls is Event: + return cls._create_of_type(*args, **kwds) + return super(_EventMeta, cls).__call__(*args, **kwds) class Event(metaclass=_EventMeta): @@ -141,7 +140,11 @@ def __eq__(self, other: Any): def __repr__(self): if getattr(self, "_unknown", False): - return f"<{type(self).__module__}..{type(self).__name__}({self.type} {self.dict})>" + return ( + f"<{type(self).__module__}.." + f"{type(self).__name__}({self.type} {self.dict})>" + ) + return f"<{type(self).__module__}.{type(self).__qualname__}({self.dict})>" @property @@ -163,9 +166,10 @@ def __getattribute__(self, name: str): def __setattr__(self, name: str, value: Any): if name == "type": raise AttributeError( - f"attribute 'type' of 'Event' or its subclass object is protected" + "attribute 'type' of 'Event' or its subclass object is protected" ) - elif name in ("_dict", "dict"): + + if name in ("_dict", "dict"): super().__setattr__(name, value) else: self._dict[name] = value @@ -214,7 +218,7 @@ def custom_type(): global _custom_event if _custom_event >= pg.NUMEVENTS: - raise error("pygame.event.custom_type made too many event types.") + raise pg.error("pygame.event.custom_type made too many event types.") _custom_event += 1 return _custom_event - 1 @@ -446,136 +450,141 @@ def _create_class(type: int, name: str, note: str | None): # for cls in _events_map.values(): # if cls.__name__ == "UserEvent": continue -# print(f"{cls.__name__} = _create_class(pg.{const_find(cls.type, cls.__name__)}, {cls.__qualname__!r}, {cls.__doc__!r})") +# print( +# f"{cls.__name__} = _create_class(pg.{const_find(cls.type, cls.__name__)}, " \ +# f"{cls.__qualname__!r}, {cls.__doc__!r})" +# ) -ActiveEvent = _create_class(pg.ACTIVEEVENT, 'ActiveEvent', None) -AppTerminating = _create_class(pg.APP_TERMINATING, 'AppTerminating', None) -AppLowMemory = _create_class(pg.APP_LOWMEMORY, 'AppLowMemory', None) +ActiveEvent = _create_class(pg.ACTIVEEVENT, "ActiveEvent", None) +AppTerminating = _create_class(pg.APP_TERMINATING, "AppTerminating", None) +AppLowMemory = _create_class(pg.APP_LOWMEMORY, "AppLowMemory", None) AppWillEnterBackground = _create_class( - pg.APP_WILLENTERBACKGROUND, 'AppWillEnterBackground', None + pg.APP_WILLENTERBACKGROUND, "AppWillEnterBackground", None ) AppDidEnterBackground = _create_class( - pg.APP_DIDENTERBACKGROUND, 'AppDidEnterBackground', None + pg.APP_DIDENTERBACKGROUND, "AppDidEnterBackground", None ) AppWillEnterForeground = _create_class( - pg.APP_WILLENTERFOREGROUND, 'AppWillEnterForeground', None + pg.APP_WILLENTERFOREGROUND, "AppWillEnterForeground", None ) AppDidEnterForeground = _create_class( - pg.APP_DIDENTERFOREGROUND, 'AppDidEnterForeground', None + pg.APP_DIDENTERFOREGROUND, "AppDidEnterForeground", None ) -ClipboardUpdate = _create_class(pg.CLIPBOARDUPDATE, 'ClipboardUpdate', None) -KeyDown = _create_class(pg.KEYDOWN, 'KeyDown', None) -KeyUp = _create_class(pg.KEYUP, 'KeyUp', None) -KeyMapChanged = _create_class(pg.KEYMAPCHANGED, 'KeyMapChanged', None) -LocaleChanged = _create_class(pg.LOCALECHANGED, 'LocaleChanged', 'Only for SDL 2.0.14+') -MouseMotion = _create_class(pg.MOUSEMOTION, 'MouseMotion', None) -MouseButtonDown = _create_class(pg.MOUSEBUTTONDOWN, 'MouseButtonDown', None) -MouseButtonUp = _create_class(pg.MOUSEBUTTONUP, 'MouseButtonUp', None) +ClipboardUpdate = _create_class(pg.CLIPBOARDUPDATE, "ClipboardUpdate", None) +KeyDown = _create_class(pg.KEYDOWN, "KeyDown", None) +KeyUp = _create_class(pg.KEYUP, "KeyUp", None) +KeyMapChanged = _create_class(pg.KEYMAPCHANGED, "KeyMapChanged", None) +LocaleChanged = _create_class(pg.LOCALECHANGED, "LocaleChanged", "Only for SDL 2.0.14+") +MouseMotion = _create_class(pg.MOUSEMOTION, "MouseMotion", None) +MouseButtonDown = _create_class(pg.MOUSEBUTTONDOWN, "MouseButtonDown", None) +MouseButtonUp = _create_class(pg.MOUSEBUTTONUP, "MouseButtonUp", None) JoyAxisMotion = _create_class( pg.JOYAXISMOTION, - 'JoyAxisMotion', + "JoyAxisMotion", 'Attribute "joy" is depracated, use "instance_id".', ) JoyBallMotion = _create_class( pg.JOYBALLMOTION, - 'JoyBallMotion', + "JoyBallMotion", 'Attribute "joy" is depracated, use "instance_id".', ) JoyHatMotion = _create_class( - pg.JOYHATMOTION, 'JoyHatMotion', 'Attribute "joy" is depracated, use "instance_id".' + pg.JOYHATMOTION, "JoyHatMotion", 'Attribute "joy" is depracated, use "instance_id".' ) JoyButtonUp = _create_class( - pg.JOYBUTTONUP, 'JoyButtonUp', 'Attribute "joy" is depracated, use "instance_id".' + pg.JOYBUTTONUP, "JoyButtonUp", 'Attribute "joy" is depracated, use "instance_id".' ) JoyButtonDown = _create_class( pg.JOYBUTTONDOWN, - 'JoyButtonDown', + "JoyButtonDown", 'Attribute "joy" is depracated, use "instance_id".', ) -Quit = _create_class(pg.QUIT, 'Quit', None) +Quit = _create_class(pg.QUIT, "Quit", None) SysWMEvent = _create_class( pg.SYSWMEVENT, - 'SysWMEvent', - "\n Attributes are OS-depended:\n hwnd, msg, wparam, lparam - Windows.\n event - Unix / OpenBSD\n For other OSes and in some cases for Unix / OpenBSD\n this event won't have any attributes.\n ", + "SysWMEvent", + "\n Attributes are OS-depended:\n hwnd, msg, wparam, lparam - Windows.\n" + " event - Unix / OpenBSD\n For other OSes and in some cases for Unix / OpenBSD\n" + " this event won't have any attributes.\n ", ) -VideoResize = _create_class(pg.VIDEORESIZE, 'VideoResize', None) -VideoExpose = _create_class(pg.VIDEOEXPOSE, 'VideoExpose', None) -MidiIn = _create_class(pg.MIDIIN, 'MidiIn', None) -MidiOut = _create_class(pg.MIDIOUT, 'MidiOut', None) -NoEvent = _create_class(pg.NOEVENT, 'NoEvent', None) +VideoResize = _create_class(pg.VIDEORESIZE, "VideoResize", None) +VideoExpose = _create_class(pg.VIDEOEXPOSE, "VideoExpose", None) +MidiIn = _create_class(pg.MIDIIN, "MidiIn", None) +MidiOut = _create_class(pg.MIDIOUT, "MidiOut", None) +NoEvent = _create_class(pg.NOEVENT, "NoEvent", None) FingerMotion = _create_class( - pg.FINGERMOTION, 'FingerMotion', 'Attribute "window" avalible only for SDL 2.0.14+' + pg.FINGERMOTION, "FingerMotion", 'Attribute "window" avalible only for SDL 2.0.14+' ) FingerDown = _create_class( - pg.FINGERDOWN, 'FingerDown', 'Attribute "window" avalible only for SDL 2.0.14+' + pg.FINGERDOWN, "FingerDown", 'Attribute "window" avalible only for SDL 2.0.14+' ) FingerUp = _create_class( - pg.FINGERUP, 'FingerUp', 'Attribute "window" avalible only for SDL 2.0.14+' + pg.FINGERUP, "FingerUp", 'Attribute "window" avalible only for SDL 2.0.14+' ) -MultiGesture = _create_class(pg.MULTIGESTURE, 'MultiGesture', None) -MouseWheel = _create_class(pg.MOUSEWHEEL, 'MouseWheel', None) -TextInput = _create_class(pg.TEXTINPUT, 'TextInput', None) -TextEditing = _create_class(pg.TEXTEDITING, 'TextEditing', None) -DropFile = _create_class(pg.DROPFILE, 'DropFile', None) -DropText = _create_class(pg.DROPTEXT, 'DropText', None) -DropBegin = _create_class(pg.DROPBEGIN, 'DropBegin', None) -DropComplete = _create_class(pg.DROPCOMPLETE, 'DropComplete', None) +MultiGesture = _create_class(pg.MULTIGESTURE, "MultiGesture", None) +MouseWheel = _create_class(pg.MOUSEWHEEL, "MouseWheel", None) +TextInput = _create_class(pg.TEXTINPUT, "TextInput", None) +TextEditing = _create_class(pg.TEXTEDITING, "TextEditing", None) +DropFile = _create_class(pg.DROPFILE, "DropFile", None) +DropText = _create_class(pg.DROPTEXT, "DropText", None) +DropBegin = _create_class(pg.DROPBEGIN, "DropBegin", None) +DropComplete = _create_class(pg.DROPCOMPLETE, "DropComplete", None) ControllerAxisMotion = _create_class( - pg.CONTROLLERAXISMOTION, 'ControllerAxisMotion', None + pg.CONTROLLERAXISMOTION, "ControllerAxisMotion", None ) ControllerButtonDown = _create_class( - pg.CONTROLLERBUTTONDOWN, 'ControllerButtonDown', None + pg.CONTROLLERBUTTONDOWN, "ControllerButtonDown", None ) -ControllerButtonUp = _create_class(pg.CONTROLLERBUTTONUP, 'ControllerButtonUp', None) +ControllerButtonUp = _create_class(pg.CONTROLLERBUTTONUP, "ControllerButtonUp", None) ControllerDeviceAdded = _create_class( - pg.CONTROLLERDEVICEADDED, 'ControllerDeviceAdded', None + pg.CONTROLLERDEVICEADDED, "ControllerDeviceAdded", None ) ControllerDeviceRemoved = _create_class( - pg.CONTROLLERDEVICEREMOVED, 'ControllerDeviceRemoved', None + pg.CONTROLLERDEVICEREMOVED, "ControllerDeviceRemoved", None ) ControllerDeviceMapped = _create_class( - pg.CONTROLLERDEVICEREMAPPED, 'ControllerDeviceMapped', None + pg.CONTROLLERDEVICEREMAPPED, "ControllerDeviceMapped", None ) -JoyDeviceAdded = _create_class(pg.JOYDEVICEADDED, 'JoyDeviceAdded', None) -JoyDeviceRemoved = _create_class(pg.JOYDEVICEREMOVED, 'JoyDeviceRemoved', None) +JoyDeviceAdded = _create_class(pg.JOYDEVICEADDED, "JoyDeviceAdded", None) +JoyDeviceRemoved = _create_class(pg.JOYDEVICEREMOVED, "JoyDeviceRemoved", None) ControllerTouchpadDown = _create_class( - pg.CONTROLLERTOUCHPADDOWN, 'ControllerTouchpadDown', 'Only for SDL 2.0.14+' + pg.CONTROLLERTOUCHPADDOWN, "ControllerTouchpadDown", "Only for SDL 2.0.14+" ) ControllerTouchpadMotion = _create_class( - pg.CONTROLLERTOUCHPADMOTION, 'ControllerTouchpadMotion', 'Only for SDL 2.0.14+' + pg.CONTROLLERTOUCHPADMOTION, "ControllerTouchpadMotion", "Only for SDL 2.0.14+" ) ControllerTouchpadUp = _create_class( - pg.CONTROLLERTOUCHPADUP, 'ControllerTouchpadUp', 'Only for SDL 2.0.14+' + pg.CONTROLLERTOUCHPADUP, "ControllerTouchpadUp", "Only for SDL 2.0.14+" ) ControllerSensorUpdate = _create_class( - pg.CONTROLLERSENSORUPDATE, 'ControllerSensorUpdate', 'Only for SDL 2.0.14+' + pg.CONTROLLERSENSORUPDATE, "ControllerSensorUpdate", "Only for SDL 2.0.14+" ) -AudioDeviceAdded = _create_class(pg.AUDIODEVICEADDED, 'AudioDeviceAdded', None) -AudioDeviceRemoved = _create_class(pg.AUDIODEVICEREMOVED, 'AudioDeviceRemoved', None) -RenderTargetsReset = _create_class(pg.RENDER_TARGETS_RESET, 'RenderTargetsReset', None) -RenderDeviceReset = _create_class(pg.RENDER_DEVICE_RESET, 'RenderDeviceReset', None) -WindowShown = _create_class(pg.WINDOWSHOWN, 'WindowShown', None) -WindowHidden = _create_class(pg.WINDOWHIDDEN, 'WindowHidden', None) -WindowExposed = _create_class(pg.WINDOWEXPOSED, 'WindowExposed', None) -WindowMoved = _create_class(pg.WINDOWMOVED, 'WindowMoved', None) -WindowResized = _create_class(pg.WINDOWRESIZED, 'WindowResized', None) -WindowSizeChanged = _create_class(pg.WINDOWSIZECHANGED, 'WindowSizeChanged', None) -WindowMinimized = _create_class(pg.WINDOWMINIMIZED, 'WindowMinimized', None) -WindowMaximized = _create_class(pg.WINDOWMAXIMIZED, 'WindowMaximized', None) -WindowRestored = _create_class(pg.WINDOWRESTORED, 'WindowRestored', None) -WindowEnter = _create_class(pg.WINDOWENTER, 'WindowEnter', None) -WindowLeave = _create_class(pg.WINDOWLEAVE, 'WindowLeave', None) -WindowFocusGained = _create_class(pg.WINDOWFOCUSGAINED, 'WindowFocusGained', None) -WindowFocusLost = _create_class(pg.WINDOWFOCUSLOST, 'WindowFocusLost', None) -WindowClose = _create_class(pg.WINDOWCLOSE, 'WindowClose', None) -WindowTakeFocus = _create_class(pg.WINDOWTAKEFOCUS, 'WindowTakeFocus', None) -WindowHitTest = _create_class(pg.WINDOWHITTEST, 'WindowHitTest', None) +AudioDeviceAdded = _create_class(pg.AUDIODEVICEADDED, "AudioDeviceAdded", None) +AudioDeviceRemoved = _create_class(pg.AUDIODEVICEREMOVED, "AudioDeviceRemoved", None) +RenderTargetsReset = _create_class(pg.RENDER_TARGETS_RESET, "RenderTargetsReset", None) +RenderDeviceReset = _create_class(pg.RENDER_DEVICE_RESET, "RenderDeviceReset", None) +WindowShown = _create_class(pg.WINDOWSHOWN, "WindowShown", None) +WindowHidden = _create_class(pg.WINDOWHIDDEN, "WindowHidden", None) +WindowExposed = _create_class(pg.WINDOWEXPOSED, "WindowExposed", None) +WindowMoved = _create_class(pg.WINDOWMOVED, "WindowMoved", None) +WindowResized = _create_class(pg.WINDOWRESIZED, "WindowResized", None) +WindowSizeChanged = _create_class(pg.WINDOWSIZECHANGED, "WindowSizeChanged", None) +WindowMinimized = _create_class(pg.WINDOWMINIMIZED, "WindowMinimized", None) +WindowMaximized = _create_class(pg.WINDOWMAXIMIZED, "WindowMaximized", None) +WindowRestored = _create_class(pg.WINDOWRESTORED, "WindowRestored", None) +WindowEnter = _create_class(pg.WINDOWENTER, "WindowEnter", None) +WindowLeave = _create_class(pg.WINDOWLEAVE, "WindowLeave", None) +WindowFocusGained = _create_class(pg.WINDOWFOCUSGAINED, "WindowFocusGained", None) +WindowFocusLost = _create_class(pg.WINDOWFOCUSLOST, "WindowFocusLost", None) +WindowClose = _create_class(pg.WINDOWCLOSE, "WindowClose", None) +WindowTakeFocus = _create_class(pg.WINDOWTAKEFOCUS, "WindowTakeFocus", None) +WindowHitTest = _create_class(pg.WINDOWHITTEST, "WindowHitTest", None) WindowICCProfChanged = _create_class( - pg.WINDOWICCPROFCHANGED, 'WindowICCProfChanged', None + pg.WINDOWICCPROFCHANGED, "WindowICCProfChanged", None ) WindowDisplayChanged = _create_class( - pg.WINDOWDISPLAYCHANGED, 'WindowDisplayChanged', None + pg.WINDOWDISPLAYCHANGED, "WindowDisplayChanged", None ) @@ -594,6 +603,9 @@ def __instancecheck__(self, instance: Any, /) -> bool: ) +_extra_cache[pg.USEREVENT] = UserEvent + + __all__ = [ "Event", "EventType", diff --git a/src_py/typing.py b/src_py/typing.py index 6452aed800..9e44eca7ca 100644 --- a/src_py/typing.py +++ b/src_py/typing.py @@ -81,7 +81,9 @@ def rect(self) -> Union["RectLike", Callable[[], "RectLike"]]: ... class EventLike(Protocol): type: int - def __init__(self, dict: dict[str, Any] | None, **kwargs: Any) -> None: ... + def __init__( + self, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... @property def dict(self) -> Dict[str, Any]: ... From a2d4ca48edcfa2cd3e8c8a5c2cad1f5128ca292d Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:13:44 +0200 Subject: [PATCH 33/36] Dust off the PR. --- buildconfig/stubs/pygame/event.pyi | 2 +- docs/reST/ref/event.rst | 12 ++---------- src_c/doc/event_doc.h | 2 +- src_py/event.py | 13 +------------ 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/buildconfig/stubs/pygame/event.pyi b/buildconfig/stubs/pygame/event.pyi index 67e502872b..231aca5efc 100644 --- a/buildconfig/stubs/pygame/event.pyi +++ b/buildconfig/stubs/pygame/event.pyi @@ -32,7 +32,7 @@ def get( ) -> list[EventLike]: ... def poll() -> EventLike: ... def wait(timeout: int = 0) -> EventLike: ... -def peek(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> Union[bool, EventLike]: ... +def peek(eventtype: _EventTypes, pump: Any = True) -> Union[bool, EventLike]: ... def clear(eventtype: Optional[_EventTypes] = None, pump: Any = True) -> None: ... def event_name(type: int) -> str: ... def set_blocked(type: Optional[_EventTypes], *args: int) -> None: ... diff --git a/docs/reST/ref/event.rst b/docs/reST/ref/event.rst index 8e4ea052ff..91febeea3b 100644 --- a/docs/reST/ref/event.rst +++ b/docs/reST/ref/event.rst @@ -317,7 +317,6 @@ On Android, the following events can be generated .. function:: peek | :sl:`test if event types are waiting on the queue` - | :sg:`peek() -> Event instance` | :sg:`peek(eventtype) -> bool` | :sg:`peek(eventtype, pump=True) -> bool` @@ -327,11 +326,6 @@ On Android, the following events can be generated If ``pump`` is ``True`` (the default), then :func:`pygame.event.pump()` will be called. - If ``eventtype`` is unspecified, or ``None``, then this function will return the top-most event instead. - - .. note:: - There is no guarantee that the event got with :func:`pygame.event.get()` immediately after calling this function will be the same. - .. versionchangedold:: 1.9.5 Added ``pump`` argument .. ## pygame.event.peek ## @@ -475,9 +469,7 @@ On Android, the following events can be generated | :sg:`event_class(type: int, /) -> type[Event]` Returns an event class that is correlated with the given event type. If the class to a given event type is not found, - but the type is within the range of valid values for the event type, instead of a ``pygame.event.Event`` subclass, - ``pygame.event.Event`` itself will be returned, so don't rely on the retuned class having ``type`` attribute equal to a number. - This happens for example, with user event types that weren't created by subclassing ``pygame.event.Event``. + but the type is within the range of valid values for the event type, a new subclass will be created. .. versionadded:: 2.5.2 .. ## pygame.event.event_class ## @@ -497,7 +489,7 @@ On Android, the following events can be generated .. versionchanged:: 2.1.4 This class is also available through the ``pygame.Event`` alias. - .. versionchanged:: 2.5.0 This class can be subclassed to create user-defined event types. + .. versionchanged:: 2.5.2 This class can be subclassed to create user-defined event types. .. note:: From version 2.1.3 ``EventType`` is an alias for ``Event``. Beforehand, diff --git a/src_c/doc/event_doc.h b/src_c/doc/event_doc.h index 865d4b23d8..e294cbae6d 100644 --- a/src_c/doc/event_doc.h +++ b/src_c/doc/event_doc.h @@ -4,7 +4,7 @@ #define DOC_EVENT_GET "get(eventtype=None) -> Eventlist\nget(eventtype=None, pump=True) -> Eventlist\nget(eventtype=None, pump=True, exclude=None) -> Eventlist\nget events from the queue" #define DOC_EVENT_POLL "poll() -> Event instance\nget a single event from the queue" #define DOC_EVENT_WAIT "wait() -> Event instance\nwait(timeout) -> Event instance\nwait for a single event from the queue" -#define DOC_EVENT_PEEK "peek() -> Event instance\npeek(eventtype) -> bool\npeek(eventtype, pump=True) -> bool\ntest if event types are waiting on the queue" +#define DOC_EVENT_PEEK "peek(eventtype) -> bool\npeek(eventtype, pump=True) -> bool\ntest if event types are waiting on the queue" #define DOC_EVENT_CLEAR "clear(eventtype=None) -> None\nclear(eventtype=None, pump=True) -> None\nremove all events from the queue" #define DOC_EVENT_EVENTNAME "event_name(type, /) -> string\nget the string name from an event id" #define DOC_EVENT_SETBLOCKED "set_blocked(type, /) -> None\nset_blocked(typelist, /) -> None\nset_blocked(None) -> None\ncontrol which events are blocked on the queue" diff --git a/src_py/event.py b/src_py/event.py index 185217cdf6..5e1045d993 100644 --- a/src_py/event.py +++ b/src_py/event.py @@ -389,7 +389,7 @@ def get( def peek( - eventtype: int | IterableLike[int] | None = None, + eventtype: int | IterableLike[int], pump: bool = True, ) -> Union[EventLike, bool]: """ @@ -405,17 +405,6 @@ def peek( if isinstance(eventtype, int): eventtype = [eventtype] - if eventtype is None: - # Can't seek without mutating event queue here, - # Due to PgEvent_New being a destructive operation. - ret = _get(-1) - - if ret is None: - return Event(0) - - post(ret) - return ret - for ev_type in eventtype: _check_ev_type(ev_type) From ea8e2b68fa1a509e032105700b5b5cbb52aadb04 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Tue, 22 Oct 2024 22:21:16 +0200 Subject: [PATCH 34/36] Removing tests for pygame.event.peek(None). --- test/event_test.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/event_test.py b/test/event_test.py index 0870f403f4..6d1c71f098 100644 --- a/test/event_test.py +++ b/test/event_test.py @@ -250,13 +250,6 @@ def test_clear(self): self.assertRaises(TypeError, pygame.event.get, ["a", "b", "c"]) def test_peek(self): - pygame.event.peek() - pygame.event.peek(None) - pygame.event.peek(None, True) - - pygame.event.peek(pump=False) - pygame.event.peek(pump=True) - pygame.event.peek(eventtype=None) pygame.event.peek(eventtype=[pygame.KEYUP, pygame.KEYDOWN]) pygame.event.peek(eventtype=pygame.USEREVENT, pump=False) @@ -786,11 +779,6 @@ def test_peek__empty_queue(self): """Ensure peek() works correctly on an empty queue.""" pygame.event.clear() - # Ensure all events can be checked. - peeked = pygame.event.peek() - - self.assertFalse(peeked) - # Ensure events can be checked individually. for event_type in EVENT_TYPES: peeked = pygame.event.peek(event_type) From 48c19918d4dc68e33e3be026a4e1f86b00c34d50 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Mon, 4 Nov 2024 15:58:11 +0100 Subject: [PATCH 35/36] Fix versionadded version note. --- docs/reST/ref/event.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reST/ref/event.rst b/docs/reST/ref/event.rst index 91febeea3b..bac1aca5d2 100644 --- a/docs/reST/ref/event.rst +++ b/docs/reST/ref/event.rst @@ -471,7 +471,7 @@ On Android, the following events can be generated Returns an event class that is correlated with the given event type. If the class to a given event type is not found, but the type is within the range of valid values for the event type, a new subclass will be created. - .. versionadded:: 2.5.2 + .. versionadded:: 2.6.0 .. ## pygame.event.event_class ## .. class:: Event @@ -489,7 +489,7 @@ On Android, the following events can be generated .. versionchanged:: 2.1.4 This class is also available through the ``pygame.Event`` alias. - .. versionchanged:: 2.5.2 This class can be subclassed to create user-defined event types. + .. versionchanged:: 2.6.0 This class can be subclassed to create user-defined event types. .. note:: From version 2.1.3 ``EventType`` is an alias for ``Event``. Beforehand, From ed33f48ff36ccedb0550647cc44848b801583944 Mon Sep 17 00:00:00 2001 From: polastyn <78505251+gresm@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:53:25 +0100 Subject: [PATCH 36/36] Fix stubs. --- buildconfig/stubs/pygame/typing.pyi | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buildconfig/stubs/pygame/typing.pyi b/buildconfig/stubs/pygame/typing.pyi index 830e7b3901..c0b7296d2b 100644 --- a/buildconfig/stubs/pygame/typing.pyi +++ b/buildconfig/stubs/pygame/typing.pyi @@ -87,7 +87,9 @@ RectLike = Union[ class EventLike(Protocol): type: int - def __init__(self, dict: dict[str, Any] | None, **kwargs: Any) -> None: ... + def __init__( + self, dict: Optional[Dict[str, Any]] = None, **kwargs: Any + ) -> None: ... @property def dict(self) -> Dict[str, Any]: ...