diff --git a/buildconfig/stubs/pygame/display.pyi b/buildconfig/stubs/pygame/display.pyi index 5124e92b47..ae9a6ff8f7 100644 --- a/buildconfig/stubs/pygame/display.pyi +++ b/buildconfig/stubs/pygame/display.pyi @@ -1,5 +1,5 @@ from collections.abc import Iterable -from typing import Optional, Union, overload, Literal +from typing import Optional, Union, overload, Literal, Any from typing_extensions import deprecated # added in 3.13 from pygame.constants import FULLSCREEN @@ -70,6 +70,7 @@ def mode_ok( ) -> int: ... def gl_get_attribute(flag: int, /) -> int: ... def gl_set_attribute(flag: int, value: int, /) -> None: ... +def gl_get_proc(proc_name: str) -> Any: ... def get_active() -> bool: ... def iconify() -> bool: ... def toggle_fullscreen() -> int: ... diff --git a/src_c/display.c b/src_c/display.c index 3ff35d4fa0..741397a2e7 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -2938,6 +2938,33 @@ pg_message_box(PyObject *self, PyObject *arg, PyObject *kwargs) return NULL; } +static PyObject *pg_proc_from_address = NULL; + +static PyObject * +pg_gl_get_proc(PyObject *self, PyObject *arg) +{ + if (!PyUnicode_Check(arg)) { + return RAISE(PyExc_TypeError, "'proc_name' should be a string"); + } + const char *proc_name = PyUnicode_AsUTF8(arg); + if (!proc_name) { + return NULL; + } + unsigned long long proc_addr = + (unsigned long long)SDL_GL_GetProcAddress(proc_name); + if (!proc_addr) { + return RAISE(pgExc_SDLError, SDL_GetError()); + } + PyObject *proc_from_address_args = Py_BuildValue("(K)", proc_addr); + if (!proc_from_address_args) { + return NULL; + } + if (!pg_proc_from_address) { + return RAISE(PyExc_TypeError, "'_proc_from_address' object is NULL"); + } + return PyObject_CallObject(pg_proc_from_address, proc_from_address_args); +} + static PyMethodDef _pg_display_methods[] = { {"init", (PyCFunction)pg_display_init, METH_NOARGS, DOC_DISPLAY_INIT}, {"quit", (PyCFunction)pg_display_quit, METH_NOARGS, DOC_DISPLAY_QUIT}, @@ -3015,6 +3042,7 @@ static PyMethodDef _pg_display_methods[] = { METH_VARARGS | METH_KEYWORDS, DOC_DISPLAY_SETALLOWSCREENSAVER}, {"message_box", (PyCFunction)pg_message_box, METH_VARARGS | METH_KEYWORDS, DOC_DISPLAY_MESSAGEBOX}, + {"gl_get_proc", (PyCFunction)pg_gl_get_proc, METH_O, "doc"}, {NULL, NULL, 0, NULL}}; #ifndef PYPY_VERSION @@ -3065,6 +3093,19 @@ MODINIT_DEFINE(display) return NULL; } + /* load _ffi module for display.get_gl_proc function */ + PyObject *pg_ffi_module = PyImport_ImportModule("pygame._ffi"); + if (!pg_ffi_module) { + return NULL; + } + + pg_proc_from_address = + PyObject_GetAttrString(pg_ffi_module, "_proc_from_address"); + if (!pg_proc_from_address) { + return NULL; + } + Py_DECREF(pg_ffi_module); + /* create the module */ module = PyModule_Create(&_module); if (module == NULL) { diff --git a/src_py/_ffi.py b/src_py/_ffi.py new file mode 100644 index 0000000000..6a579f6c6c --- /dev/null +++ b/src_py/_ffi.py @@ -0,0 +1,4 @@ +import ctypes + +def _proc_from_address(addr): + return ctypes.CFUNCTYPE(None)(addr) diff --git a/src_py/meson.build b/src_py/meson.build index 541c54cd69..5eba01ced9 100644 --- a/src_py/meson.build +++ b/src_py/meson.build @@ -4,6 +4,7 @@ python_sources = files( '_camera_opencv.py', '_data_classes.py', '_debug.py', + '_ffi.py', '_sprite.py', 'camera.py', 'colordict.py',