diff --git a/buildconfig/stubs/pygame/display.pyi b/buildconfig/stubs/pygame/display.pyi index 5124e92b47..bb37b97e5b 100644 --- a/buildconfig/stubs/pygame/display.pyi +++ b/buildconfig/stubs/pygame/display.pyi @@ -1,4 +1,5 @@ from collections.abc import Iterable +from ctypes import _FuncPointer from typing import Optional, Union, overload, Literal from typing_extensions import deprecated # added in 3.13 @@ -70,6 +71,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) -> _FuncPointer: ... def get_active() -> bool: ... def iconify() -> bool: ... def toggle_fullscreen() -> int: ... diff --git a/docs/reST/ref/display.rst b/docs/reST/ref/display.rst index 310ebb38fa..0e499be690 100644 --- a/docs/reST/ref/display.rst +++ b/docs/reST/ref/display.rst @@ -540,6 +540,13 @@ required). .. ## pygame.display.gl_set_attribute ## +.. function:: gl_get_proc + + | :sl:`Get an OpenGL function by name` + | :sg:`gl_get_proc(proc_name) -> ctypes._FuncPointer` + + .. # pygame.display.gl_get_proc ## + .. function:: get_active | :sl:`Returns True when the display is active on the screen` diff --git a/src_c/display.c b/src_c/display.c index 3ff35d4fa0..eec4754a1f 100644 --- a/src_c/display.c +++ b/src_c/display.c @@ -2938,6 +2938,69 @@ pg_message_box(PyObject *self, PyObject *arg, PyObject *kwargs) return NULL; } +static PyObject *ctypes_functype = 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; + } + +#if SDL_VERSION_ATLEAST(3, 0, 0) + SDL_FunctionPointer proc_addr; +#else + void *proc_addr; +#endif + proc_addr = SDL_GL_GetProcAddress(proc_name); + if (!proc_addr) { + PyErr_Format(pgExc_SDLError, "Unable to get OpenGL function '%s'", + proc_name); + return NULL; + } + PyObject *proc_addr_obj = PyLong_FromVoidPtr(proc_addr); + if (!proc_addr_obj) { + return NULL; + } + + // load ctypes_functype if it's NULL + if (!ctypes_functype) { + PyObject *ctypes_module = PyImport_ImportModule("ctypes"); + if (!ctypes_module) { + return NULL; + } + + PyObject *ctypes_functype_factory; +#if defined(_WIN32) + // gl proc need to be called with WINFUNCTYPE (stdcall) on win32 + ctypes_functype_factory = + PyObject_GetAttrString(ctypes_module, "WINFUNCTYPE"); +#else + ctypes_functype_factory = + PyObject_GetAttrString(ctypes_module, "CFUNCTYPE"); +#endif + Py_DECREF(ctypes_module); + if (!ctypes_functype_factory) { + return NULL; + } + + ctypes_functype = + PyObject_CallOneArg(ctypes_functype_factory, Py_None); + Py_DECREF(ctypes_functype_factory); + if (!ctypes_functype) { + return NULL; + } + } + + PyObject *retv = PyObject_CallOneArg(ctypes_functype, proc_addr_obj); + Py_DECREF(proc_addr_obj); + return retv; +} + 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 +3078,8 @@ 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_DISPLAY_GLGETPROC}, {NULL, NULL, 0, NULL}}; #ifndef PYPY_VERSION diff --git a/src_c/doc/display_doc.h b/src_c/doc/display_doc.h index 17b881bd49..ebcb06694c 100644 --- a/src_c/doc/display_doc.h +++ b/src_c/doc/display_doc.h @@ -15,6 +15,7 @@ #define DOC_DISPLAY_MODEOK "mode_ok(size, flags=0, depth=0, display=0) -> depth\nPick the best color depth for a display mode" #define DOC_DISPLAY_GLGETATTRIBUTE "gl_get_attribute(flag, /) -> value\nGet the value for an OpenGL flag for the current display" #define DOC_DISPLAY_GLSETATTRIBUTE "gl_set_attribute(flag, value, /) -> None\nRequest an OpenGL display attribute for the display mode" +#define DOC_DISPLAY_GLGETPROC "gl_get_proc(proc_name) -> ctypes._FuncPointer\nGet an OpenGL function by name" #define DOC_DISPLAY_GETACTIVE "get_active() -> bool\nReturns True when the display is active on the screen" #define DOC_DISPLAY_ICONIFY "iconify() -> bool\nIconify the display surface" #define DOC_DISPLAY_TOGGLEFULLSCREEN "toggle_fullscreen() -> int\nSwitch between fullscreen and windowed displays"