From 9cd9041020c7e4ce2ff9a78f77eed2582f9df6fc Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sun, 20 Jul 2025 03:13:06 +0200 Subject: [PATCH 1/6] wasm meson build test --- dev.py | 28 ++ meson-cross-emscripten.ini | 14 + meson.build | 25 +- src_c/base.c | 528 +++++++++++++++++++++++++++++++++++++ src_c/meson.build | 9 +- 5 files changed, 595 insertions(+), 9 deletions(-) create mode 100644 meson-cross-emscripten.ini diff --git a/dev.py b/dev.py index 7ed720a134..6b380ac56f 100644 --- a/dev.py +++ b/dev.py @@ -36,6 +36,15 @@ PIP_MIN_VERSION = "23.1" +if __import__("sysconfig").get_config_var("HOST_GNU_TYPE").find('wasm') >= 0: + if __import__("sysconfig").get_config_var("HOST_GNU_TYPE").find('wasi')>=0: + WASM='wasi' + else: + WASM='emscripten' +else: + WASM = '' + + class Colors(Enum): RESET = "\033[0m" RED = "\033[31m" @@ -231,6 +240,15 @@ def cmd_build(self): f"-Cbuild-dir=.mesonpy-build{build_suffix}", ] + if WASM: + stripped = True + install_args.extend([ + f'--config-settings=setup-args=--cross-file={os.getcwd()}/meson-cross-{WASM}.ini', + "-Csetup-args=-Dmidi=disabled", + '-Csetup-args=-Dc_link_args=-lfreetype -lharfbuzz -lSDL2_ttf', + ]) + wheel_dir = Path("./whl") + if not wheel_dir: # editable install if not quiet: @@ -261,6 +279,7 @@ def cmd_build(self): info_str = ( f"with {debug=}, {lax=}, {sdl3=}, {stripped=}, {coverage=} and {sanitize=}" ) + if wheel_dir: pprint(f"Building wheel at '{wheel_dir}' ({info_str})") cmd_run( @@ -467,6 +486,9 @@ def prep_env(self): bin = venv_path / "Scripts" if os.name == "nt" else venv_path / "bin" self.py = bin / "python" else: + if WASM: + if os.environ.get('EMSDK', None): + self.py = Path(os.environ.get('SDKROOT') + "/python3-wasm") pprint(f"Using python '{self.py}'") # set PATH to give high priority to executables in the python bin folder @@ -487,6 +509,12 @@ def prep_env(self): deps = self.deps.get(self.args["command"], set()) ignored_deps = self.args["ignore_dep"] + + if WASM: + ignored_deps = list(deps) + print("IGNORING:", ignored_deps) + deps.clear() + deps_filtered = deps.copy() if ignored_deps: for constr in deps: diff --git a/meson-cross-emscripten.ini b/meson-cross-emscripten.ini new file mode 100644 index 0000000000..474de1c0d5 --- /dev/null +++ b/meson-cross-emscripten.ini @@ -0,0 +1,14 @@ +[host_machine] +system = 'emscripten' +cpu_family = 'wasm' +cpu = 'wasm' +endian = 'little' + + +[binaries] +c = 'emcc' +cpp = 'em++' +ar = 'llvm-ar' +strip = 'llvm-strip' +exe_wrapper = 'node' +pkgconfig_path = '/opt/python-wasm-sdk/devices/x86_64/usr/bin/pkgconfig' diff --git a/meson.build b/meson.build index 890336cdcf..df59c45862 100644 --- a/meson.build +++ b/meson.build @@ -35,7 +35,7 @@ elif host_machine.system() == 'android' ) elif host_machine.system() == 'emscripten' plat = 'emscripten' - error( + warning( 'The meson buildconfig of pygame-ce does not support emscripten for now. ', 'However it may be added in the future', ) @@ -229,7 +229,8 @@ if plat == 'win' and host_machine.cpu_family().startswith('x86') install_data(dlls, install_dir: pg_dir, install_tag: 'pg-tag') else - bases = ['/usr/local', '/usr', '/opt/homebrew', '/opt/local'] +# bases = ['/usr/local', '/usr', '/opt/homebrew', '/opt/local'] + bases = ['/opt/python-wasm-sdk/emsdk/upstream/emscripten/cache/sysroot', '/opt/python-wasm-sdk/devices/emsdk/usr'] foreach inc_dir : bases foreach sub_inc : [ '', @@ -244,7 +245,7 @@ else endforeach foreach lib_dir : bases - foreach sub_lib : ['lib', 'lib64'] + foreach sub_lib : ['lib', 'lib64', 'lib/wasm32-emscripten/pic', 'lib/wasm32-emscripten'] full_lib = lib_dir / sub_lib if fs.exists(full_lib) pg_lib_dirs += full_lib @@ -299,7 +300,11 @@ if not sdl_ttf_dep.found() ) endif -freetype_dep = dependency('freetype2', required: false) +if plat == 'emscripten' + freetype_dep = dependency('freetype2', required: false) +else + freetype_dep = dependency('freetype2', required: true) +endif if not freetype_dep.found() freetype_dep = declare_dependency( include_directories: pg_inc_dirs, @@ -311,7 +316,16 @@ if not freetype_dep.found() ) endif + + + portmidi_dep = dependency('portmidi', required: false) + +portmidi_deps = [] + +if get_option('midi').disabled() + # disable pypm build +else if not portmidi_dep.found() portmidi_dep = declare_dependency( include_directories: pg_inc_dirs, @@ -323,8 +337,6 @@ if not portmidi_dep.found() ) endif -portmidi_deps = [portmidi_dep] - if portmidi_dep.found() # porttime can be a separate library, or be inbuilt in portmidi. # So if it is available as a separate library, include it as a dependency @@ -341,6 +353,7 @@ if portmidi_dep.found() portmidi_deps += porttime_dep endif endif +endif pg_base_deps = [sdl_dep, py_dep] diff --git a/src_c/base.c b/src_c/base.c index 4d3ac2a906..c1bf4a2bae 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -19,6 +19,13 @@ Pete Shinners pete@shinners.org */ + + +#if defined(__EMSCRIPTEN__) || defined(__wasi__) +#define BUILD_STATIC 1 +#endif + + #define NO_PYGAME_C_API #define PYGAMEAPI_BASE_INTERNAL @@ -2504,3 +2511,524 @@ MODINIT_DEFINE(base) Py_XDECREF(module); return NULL; } + + + +#if defined(BUILD_STATIC) + +#define NO_PYGAME_C_API + +#define PYGAMEAPI_RECT_INTERNAL +#define PYGAMEAPI_EVENT_INTERNAL +#define PYGAMEAPI_JOYSTICK_INTERNAL +#define PYGAMEAPI_BASE_INTERNAL +#define PYGAMEAPI_SURFACE_INTERNAL +#define PYGAMEAPI_BUFFERPROXY_INTERNAL +#define PYGAMEAPI_WINDOW_INTERNAL +#define PYGAMEAPI_RENDER_INTERNAL + +#include + +#undef WITH_THREAD + +#undef import_pygame_base +#undef import_pygame_rect +#undef import_pygame_surface +#undef import_pygame_geometry +#undef import_pygame_color +#undef import_pygame_bufferproxy +#undef import_pygame_rwobject +#undef import_pygame_event + +void +import_pygame_base(void) +{ +} + +void +import_pygame_rect(void) +{ +} + +void +import_pygame_surface(void) +{ +} + +#ifdef import_pygame_window +#undef import_pygame_window +#endif + +void +import_pygame_window(void) +{ +} + +void +import_pygame_geometry(void) +{ +} + +void +import_pygame_color(void) +{ +} + +void +import_pygame_font(void) +{ +} + +void +import_pygame_freetype(void) +{ +} + +void +import_pygame_bufferproxy(void) +{ +} + +void +import_pygame_rwobject(void) +{ +} + +void +import_pygame_event(void) +{ +} + +#ifdef import_pygame_joystick +#undef import_pygame_joystick +#endif + +void +import_pygame_joystick(void) +{ +} + +#undef import_pygame_imageext +#undef import_pygame_render +#undef import_pygame_pixelarray + +void +import_pygame_imageext(void) +{ +} + +void +import_pygame_render(void) +{ +} + +void +import_pygame_pixelarray(void) +{ +} + +PyMODINIT_FUNC +PyInit_base(void); +PyMODINIT_FUNC +PyInit_color(void); +PyMODINIT_FUNC +PyInit_constants(void); +PyMODINIT_FUNC +PyInit_version(void); +PyMODINIT_FUNC +PyInit_rect(void); +PyMODINIT_FUNC +PyInit_geometry(void); +PyMODINIT_FUNC +PyInit_surflock(void); +PyMODINIT_FUNC +PyInit_rwobject(void); +PyMODINIT_FUNC +PyInit_bufferproxy(void); + +PyMODINIT_FUNC +PyInit_surface(void); +PyMODINIT_FUNC +PyInit_display(void); +PyMODINIT_FUNC +PyInit__freetype(void); +PyMODINIT_FUNC +PyInit_font(void); + +PyMODINIT_FUNC +PyInit_draw(void); +PyMODINIT_FUNC +PyInit_mouse(void); +PyMODINIT_FUNC +PyInit_key(void); +PyMODINIT_FUNC +PyInit_event(void); + +PyMODINIT_FUNC +PyInit_joystick(void); + +PyMODINIT_FUNC +PyInit_imageext(void); + +PyMODINIT_FUNC +PyInit_image(void); + +PyMODINIT_FUNC +PyInit_mask(void); + +PyMODINIT_FUNC +PyInit_mixer_music(void); + +PyMODINIT_FUNC +PyInit_mixer(void); + +PyMODINIT_FUNC +PyInit_pg_math(void); + +PyMODINIT_FUNC +PyInit_pg_time(void); + + +PyMODINIT_FUNC +PyInit_system(void); + + +PyMODINIT_FUNC +PyInit_transform(void); + + +PyMODINIT_FUNC +PyInit__sprite(void); + +PyMODINIT_FUNC +PyInit_pixelcopy(void); + +PyMODINIT_FUNC +PyInit_newbuffer(void); + +PyMODINIT_FUNC +PyInit_gfxdraw(void); + +PyMODINIT_FUNC +PyInit_pixelarray(void); + +PyMODINIT_FUNC +PyInit_window(void); + +PyMODINIT_FUNC +PyInit__render(void); + +// pygame _sdl2 + +PyMODINIT_FUNC +PyInit_sdl2(void); + +PyMODINIT_FUNC +PyInit_controller(void); + +PyMODINIT_FUNC +PyInit_mixer(void); + + +PyMODINIT_FUNC +PyInit_audio(void); + +PyMODINIT_FUNC +PyInit_video(void); + + + +// pygame_static module + +void +load_submodule(const char *parent, PyObject *mod, const char *alias) +{ + char fqn[1024]; + if (!mod) { + snprintf(fqn, sizeof(fqn), "ERROR: PyInit_%s failed for %s.%s", alias, + parent, alias); + puts(fqn); + PyErr_Print(); + PyErr_Clear(); + } + else { + snprintf(fqn, sizeof(fqn), "%s.%s", parent, alias); + PyObject *modules = PyImport_GetModuleDict(); + + PyObject *pmod = PyDict_GetItemString(modules, parent); + if (!pmod) { + snprintf(fqn, sizeof(fqn), "ERROR: Parent %s not found for %s.%s", + parent, parent, alias); + puts(fqn); + } + else { + PyDict_SetItemString(modules, fqn, mod); + PyDict_SetItemString(PyModule_GetDict(mod), "__name__", + PyUnicode_FromString(fqn)); + PyModule_AddObjectRef(pmod, alias, mod); + Py_XDECREF(mod); + } + } +} + +void +load_submodule_mphase(const char *parent, PyObject *mdef, PyObject *spec, + const char *alias) +{ + char fqn[1024]; + snprintf(fqn, sizeof(fqn), "%s.%s", parent, alias); + + PyObject *modules = PyImport_GetModuleDict(); + + Py_DECREF(PyObject_GetAttrString(spec, "name")); + PyObject_SetAttrString(spec, "name", PyUnicode_FromString(alias)); + PyObject *pmod = PyDict_GetItemString(modules, parent); + PyObject *mod = PyModule_FromDefAndSpec((PyModuleDef *)mdef, spec); + PyDict_SetItemString(PyModule_GetDict(mod), "__package__", + PyUnicode_FromString(parent)); + // TODO SET PACKAGE + PyModule_ExecDef(mod, (PyModuleDef *)mdef); + + if (pmod) { + PyDict_SetItemString(modules, fqn, mod); + PyDict_SetItemString(PyModule_GetDict(mod), "__name__", + PyUnicode_FromString(fqn)); + PyModule_AddObjectRef(pmod, alias, mod); + Py_XDECREF(mod); + } + if (!pmod || PyErr_Occurred()) { + snprintf(fqn, sizeof(fqn), "Error after init in : %s.%s\n", parent, + alias); + fputs(fqn, stderr); + PyErr_Print(); + PyErr_Clear(); + } +} + +static PyObject * +mod_pygame_import_cython(PyObject *self, PyObject *spec) +{ + load_submodule_mphase("pygame._sdl2", PyInit_sdl2(), spec, "sdl2"); +/* + load_submodule_mphase("pygame._sdl2", PyInit_mixer(), spec, "mixer"); + load_submodule("pygame._sdl2", PyInit_controller(), "controller"); + load_submodule_mphase("pygame._sdl2", PyInit_audio(), spec, "audio"); + load_submodule_mphase("pygame._sdl2", PyInit_video(), spec, "video"); +*/ + + Py_RETURN_NONE; +} + +static PyMethodDef mod_pygame_static_methods[] = { + {"import_cython", (PyCFunction)mod_pygame_import_cython, METH_O, + "pygame._sdl2.*"}, + {NULL, NULL, 0, NULL}}; + +static struct PyModuleDef mod_pygame_static = {PyModuleDef_HEAD_INIT, + "pygame_static", NULL, -1, + mod_pygame_static_methods}; + +PyMODINIT_FUNC +PyInit_pygame_static() +{ + // cannot fail here, and font_initialized is already set to 1 in font.c . + TTF_Init(); + + // for correct input in wasm worker + SDL_SetHint("SDL_EMSCRIPTEN_KEYBOARD_ELEMENT", "1"); + + // base module is including current file + // all globals are accessible from here. + load_submodule("pygame", PyInit_base(), "base"); + + load_submodule("pygame", PyInit_constants(), "constants"); + // + load_submodule("pygame", PyInit_pg_math(), "math"); + + // base, pygame.colordict + load_submodule("pygame", PyInit_color(), "color"); + + // base + load_submodule("pygame", PyInit_rect(), "rect"); + + // base, rect + load_submodule("pygame", PyInit_geometry(), "geometry"); + + load_submodule("pygame", PyInit_bufferproxy(), "bufferproxy"); + load_submodule("pygame", PyInit_surflock(), "surflock"); + + // base, color, rect, bufferproxy, surflock + load_submodule("pygame", PyInit_surface(), "surface"); + + load_submodule("pygame", PyInit_rwobject(), "rwobject"); + + // base, color, rect, bufferproxy, surflock, surface, rwobject + load_submodule("pygame", PyInit_imageext(), "imageext"); + // base, color, rect, bufferproxy, surflock, surface, rwobject + load_submodule("pygame", PyInit_image(), "image"); + + load_submodule("pygame", PyInit__freetype(), "_freetype"); + load_submodule("pygame", PyInit_font(), "font"); + load_submodule("pygame", PyInit_pixelcopy(), "pixelcopy"); + load_submodule("pygame", PyInit_newbuffer(), "newbuffer"); + + // base + load_submodule("pygame", PyInit_joystick(), "joystick"); + // base, joystick + load_submodule("pygame", PyInit_event(), "event"); + + // base, rect, event + load_submodule("pygame", PyInit_key(), "key"); + // base, event + load_submodule("pygame", PyInit_pg_time(), "time"); + + load_submodule("pygame", PyInit_transform(), "transform"); + load_submodule("pygame", PyInit_draw(), "draw"); + + load_submodule("pygame", PyInit_mask(), "mask"); + load_submodule("pygame", PyInit_mouse(), "mouse"); + + load_submodule("pygame", PyInit_mixer(), "mixer"); + load_submodule("pygame.mixer", PyInit_mixer_music(), "music"); + + // base, color, rect, bufferproxy, surflock, surface + load_submodule("pygame", PyInit_window(), "window"); + + // base, color, rect, surflock, surface, window + load_submodule("pygame", PyInit_display(), "display"); + load_submodule("pygame", PyInit__render(), "_render"); + + load_submodule("pygame", PyInit_pixelarray(), "pixelarray"); + + // base, color, rect, bufferproxy, surflock, surface + load_submodule("pygame", PyInit_gfxdraw(), "gfxdraw"); + + load_submodule("pygame", PyInit_system(), "system"); + + return PyModule_Create(&mod_pygame_static); +} + +// meson static support + +#include "constants.c" + +#include "rect.c" +#include "pgcompat_rect.c" + +#undef pgSurface_Lock +#undef pgSurface_Unlock +#undef pgSurface_LockBy +#undef pgSurface_UnlockBy +#undef pgSurface_Prep +#undef pgSurface_Unprep + +#include "surflock.c" + +#undef pgColor_New +#undef pgColor_NewLength +#undef pg_RGBAFromObjEx +#undef pg_MappedColorFromObj +#undef pgColor_Type + +#include "color.c" + +#undef pgBufferProxy_New + +#include "bufferproxy.c" + +#undef pgSurface_Blit +#undef pgSurface_New +#undef pgSurface_Type +#undef pgSurface_SetSurface + +#include "surface.c" +#include "simd_blitters_avx2.c" +#include "simd_blitters_sse2.c" + +#include "window.c" + +#undef pgVidInfo_Type +#undef pgVidInfo_New + +#include "display.c" + +#include "draw.c" + +#undef pg_EncodeString +#undef pg_EncodeFilePath +#undef pgRWops_IsFileObject +#undef pgRWops_GetFileExtension +#undef pgRWops_FromFileObject +#undef pgRWops_FromObject + +#include "rwobject.c" + +#define pgSurface_New(surface) (pgSurfaceObject *)pgSurface_New2((surface), 1) +#include "render.c" +#include "image.c" + +#include "imageext.c" + +#include "mask.c" + +#undef pg_EnableKeyRepeat +#undef pg_GetKeyRepeat +#undef pgEvent_FillUserEvent +#undef pgEvent_Type +#undef pgEvent_New + +#include "joystick.c" + +#include "event.c" + +#include "mouse.c" + +#include "key.c" + +#include "time.c" + +#include "system.c" +#include "geometry.c" + +#if defined(DEC_CONST) + #undef DEC_CONST +#endif +#include "_freetype.c" +#include "freetype/ft_wrap.c" +#include "freetype/ft_render.c" +#include "freetype/ft_render_cb.c" +#include "freetype/ft_cache.c" +#include "freetype/ft_layout.c" +#include "freetype/ft_unicode.c" + +#include "font.c" + +#include "mixer.c" + +#include "music.c" + +#include "gfxdraw.c" + +#include "alphablit.c" + +#include "surface_fill.c" +#include "pixelarray.c" +#include "pixelcopy.c" +#include "newbuffer.c" + +#include "_sdl2/controller.c" +// #include "_sdl2/controller_old.c" +// #include "_sdl2/mixer.c" +#include "_sdl2/touch.c" +#include "_sdl2/sdl2.c" + +#include "transform.c" +// that remove some warnings +#undef MAX +#undef MIN +#include "scale2x.c" + +#include "math.c" + + +#endif // defined(BUILD_STATIC) diff --git a/src_c/meson.build b/src_c/meson.build index 0c1479a812..d590af37cf 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -1,14 +1,15 @@ # first the "required" modules - base = py.extension_module( - 'base', + 'pygame_static', 'base.c', c_args: warnings_error, dependencies: pg_base_deps, install: true, - subdir: pg, +# subdir: pg, ) +if host_machine.system() != 'emscripten' + color = py.extension_module( 'color', 'color.c', @@ -458,3 +459,5 @@ if portmidi_dep.found() subdir: pg, ) endif + +endif From 9682e0153ed611d5782c984a97d716106ded1c0c Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sun, 20 Jul 2025 03:16:53 +0200 Subject: [PATCH 2/6] revert base --- src_c/base.c | 528 --------------------------------------------------- 1 file changed, 528 deletions(-) diff --git a/src_c/base.c b/src_c/base.c index c1bf4a2bae..4d3ac2a906 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -19,13 +19,6 @@ Pete Shinners pete@shinners.org */ - - -#if defined(__EMSCRIPTEN__) || defined(__wasi__) -#define BUILD_STATIC 1 -#endif - - #define NO_PYGAME_C_API #define PYGAMEAPI_BASE_INTERNAL @@ -2511,524 +2504,3 @@ MODINIT_DEFINE(base) Py_XDECREF(module); return NULL; } - - - -#if defined(BUILD_STATIC) - -#define NO_PYGAME_C_API - -#define PYGAMEAPI_RECT_INTERNAL -#define PYGAMEAPI_EVENT_INTERNAL -#define PYGAMEAPI_JOYSTICK_INTERNAL -#define PYGAMEAPI_BASE_INTERNAL -#define PYGAMEAPI_SURFACE_INTERNAL -#define PYGAMEAPI_BUFFERPROXY_INTERNAL -#define PYGAMEAPI_WINDOW_INTERNAL -#define PYGAMEAPI_RENDER_INTERNAL - -#include - -#undef WITH_THREAD - -#undef import_pygame_base -#undef import_pygame_rect -#undef import_pygame_surface -#undef import_pygame_geometry -#undef import_pygame_color -#undef import_pygame_bufferproxy -#undef import_pygame_rwobject -#undef import_pygame_event - -void -import_pygame_base(void) -{ -} - -void -import_pygame_rect(void) -{ -} - -void -import_pygame_surface(void) -{ -} - -#ifdef import_pygame_window -#undef import_pygame_window -#endif - -void -import_pygame_window(void) -{ -} - -void -import_pygame_geometry(void) -{ -} - -void -import_pygame_color(void) -{ -} - -void -import_pygame_font(void) -{ -} - -void -import_pygame_freetype(void) -{ -} - -void -import_pygame_bufferproxy(void) -{ -} - -void -import_pygame_rwobject(void) -{ -} - -void -import_pygame_event(void) -{ -} - -#ifdef import_pygame_joystick -#undef import_pygame_joystick -#endif - -void -import_pygame_joystick(void) -{ -} - -#undef import_pygame_imageext -#undef import_pygame_render -#undef import_pygame_pixelarray - -void -import_pygame_imageext(void) -{ -} - -void -import_pygame_render(void) -{ -} - -void -import_pygame_pixelarray(void) -{ -} - -PyMODINIT_FUNC -PyInit_base(void); -PyMODINIT_FUNC -PyInit_color(void); -PyMODINIT_FUNC -PyInit_constants(void); -PyMODINIT_FUNC -PyInit_version(void); -PyMODINIT_FUNC -PyInit_rect(void); -PyMODINIT_FUNC -PyInit_geometry(void); -PyMODINIT_FUNC -PyInit_surflock(void); -PyMODINIT_FUNC -PyInit_rwobject(void); -PyMODINIT_FUNC -PyInit_bufferproxy(void); - -PyMODINIT_FUNC -PyInit_surface(void); -PyMODINIT_FUNC -PyInit_display(void); -PyMODINIT_FUNC -PyInit__freetype(void); -PyMODINIT_FUNC -PyInit_font(void); - -PyMODINIT_FUNC -PyInit_draw(void); -PyMODINIT_FUNC -PyInit_mouse(void); -PyMODINIT_FUNC -PyInit_key(void); -PyMODINIT_FUNC -PyInit_event(void); - -PyMODINIT_FUNC -PyInit_joystick(void); - -PyMODINIT_FUNC -PyInit_imageext(void); - -PyMODINIT_FUNC -PyInit_image(void); - -PyMODINIT_FUNC -PyInit_mask(void); - -PyMODINIT_FUNC -PyInit_mixer_music(void); - -PyMODINIT_FUNC -PyInit_mixer(void); - -PyMODINIT_FUNC -PyInit_pg_math(void); - -PyMODINIT_FUNC -PyInit_pg_time(void); - - -PyMODINIT_FUNC -PyInit_system(void); - - -PyMODINIT_FUNC -PyInit_transform(void); - - -PyMODINIT_FUNC -PyInit__sprite(void); - -PyMODINIT_FUNC -PyInit_pixelcopy(void); - -PyMODINIT_FUNC -PyInit_newbuffer(void); - -PyMODINIT_FUNC -PyInit_gfxdraw(void); - -PyMODINIT_FUNC -PyInit_pixelarray(void); - -PyMODINIT_FUNC -PyInit_window(void); - -PyMODINIT_FUNC -PyInit__render(void); - -// pygame _sdl2 - -PyMODINIT_FUNC -PyInit_sdl2(void); - -PyMODINIT_FUNC -PyInit_controller(void); - -PyMODINIT_FUNC -PyInit_mixer(void); - - -PyMODINIT_FUNC -PyInit_audio(void); - -PyMODINIT_FUNC -PyInit_video(void); - - - -// pygame_static module - -void -load_submodule(const char *parent, PyObject *mod, const char *alias) -{ - char fqn[1024]; - if (!mod) { - snprintf(fqn, sizeof(fqn), "ERROR: PyInit_%s failed for %s.%s", alias, - parent, alias); - puts(fqn); - PyErr_Print(); - PyErr_Clear(); - } - else { - snprintf(fqn, sizeof(fqn), "%s.%s", parent, alias); - PyObject *modules = PyImport_GetModuleDict(); - - PyObject *pmod = PyDict_GetItemString(modules, parent); - if (!pmod) { - snprintf(fqn, sizeof(fqn), "ERROR: Parent %s not found for %s.%s", - parent, parent, alias); - puts(fqn); - } - else { - PyDict_SetItemString(modules, fqn, mod); - PyDict_SetItemString(PyModule_GetDict(mod), "__name__", - PyUnicode_FromString(fqn)); - PyModule_AddObjectRef(pmod, alias, mod); - Py_XDECREF(mod); - } - } -} - -void -load_submodule_mphase(const char *parent, PyObject *mdef, PyObject *spec, - const char *alias) -{ - char fqn[1024]; - snprintf(fqn, sizeof(fqn), "%s.%s", parent, alias); - - PyObject *modules = PyImport_GetModuleDict(); - - Py_DECREF(PyObject_GetAttrString(spec, "name")); - PyObject_SetAttrString(spec, "name", PyUnicode_FromString(alias)); - PyObject *pmod = PyDict_GetItemString(modules, parent); - PyObject *mod = PyModule_FromDefAndSpec((PyModuleDef *)mdef, spec); - PyDict_SetItemString(PyModule_GetDict(mod), "__package__", - PyUnicode_FromString(parent)); - // TODO SET PACKAGE - PyModule_ExecDef(mod, (PyModuleDef *)mdef); - - if (pmod) { - PyDict_SetItemString(modules, fqn, mod); - PyDict_SetItemString(PyModule_GetDict(mod), "__name__", - PyUnicode_FromString(fqn)); - PyModule_AddObjectRef(pmod, alias, mod); - Py_XDECREF(mod); - } - if (!pmod || PyErr_Occurred()) { - snprintf(fqn, sizeof(fqn), "Error after init in : %s.%s\n", parent, - alias); - fputs(fqn, stderr); - PyErr_Print(); - PyErr_Clear(); - } -} - -static PyObject * -mod_pygame_import_cython(PyObject *self, PyObject *spec) -{ - load_submodule_mphase("pygame._sdl2", PyInit_sdl2(), spec, "sdl2"); -/* - load_submodule_mphase("pygame._sdl2", PyInit_mixer(), spec, "mixer"); - load_submodule("pygame._sdl2", PyInit_controller(), "controller"); - load_submodule_mphase("pygame._sdl2", PyInit_audio(), spec, "audio"); - load_submodule_mphase("pygame._sdl2", PyInit_video(), spec, "video"); -*/ - - Py_RETURN_NONE; -} - -static PyMethodDef mod_pygame_static_methods[] = { - {"import_cython", (PyCFunction)mod_pygame_import_cython, METH_O, - "pygame._sdl2.*"}, - {NULL, NULL, 0, NULL}}; - -static struct PyModuleDef mod_pygame_static = {PyModuleDef_HEAD_INIT, - "pygame_static", NULL, -1, - mod_pygame_static_methods}; - -PyMODINIT_FUNC -PyInit_pygame_static() -{ - // cannot fail here, and font_initialized is already set to 1 in font.c . - TTF_Init(); - - // for correct input in wasm worker - SDL_SetHint("SDL_EMSCRIPTEN_KEYBOARD_ELEMENT", "1"); - - // base module is including current file - // all globals are accessible from here. - load_submodule("pygame", PyInit_base(), "base"); - - load_submodule("pygame", PyInit_constants(), "constants"); - // - load_submodule("pygame", PyInit_pg_math(), "math"); - - // base, pygame.colordict - load_submodule("pygame", PyInit_color(), "color"); - - // base - load_submodule("pygame", PyInit_rect(), "rect"); - - // base, rect - load_submodule("pygame", PyInit_geometry(), "geometry"); - - load_submodule("pygame", PyInit_bufferproxy(), "bufferproxy"); - load_submodule("pygame", PyInit_surflock(), "surflock"); - - // base, color, rect, bufferproxy, surflock - load_submodule("pygame", PyInit_surface(), "surface"); - - load_submodule("pygame", PyInit_rwobject(), "rwobject"); - - // base, color, rect, bufferproxy, surflock, surface, rwobject - load_submodule("pygame", PyInit_imageext(), "imageext"); - // base, color, rect, bufferproxy, surflock, surface, rwobject - load_submodule("pygame", PyInit_image(), "image"); - - load_submodule("pygame", PyInit__freetype(), "_freetype"); - load_submodule("pygame", PyInit_font(), "font"); - load_submodule("pygame", PyInit_pixelcopy(), "pixelcopy"); - load_submodule("pygame", PyInit_newbuffer(), "newbuffer"); - - // base - load_submodule("pygame", PyInit_joystick(), "joystick"); - // base, joystick - load_submodule("pygame", PyInit_event(), "event"); - - // base, rect, event - load_submodule("pygame", PyInit_key(), "key"); - // base, event - load_submodule("pygame", PyInit_pg_time(), "time"); - - load_submodule("pygame", PyInit_transform(), "transform"); - load_submodule("pygame", PyInit_draw(), "draw"); - - load_submodule("pygame", PyInit_mask(), "mask"); - load_submodule("pygame", PyInit_mouse(), "mouse"); - - load_submodule("pygame", PyInit_mixer(), "mixer"); - load_submodule("pygame.mixer", PyInit_mixer_music(), "music"); - - // base, color, rect, bufferproxy, surflock, surface - load_submodule("pygame", PyInit_window(), "window"); - - // base, color, rect, surflock, surface, window - load_submodule("pygame", PyInit_display(), "display"); - load_submodule("pygame", PyInit__render(), "_render"); - - load_submodule("pygame", PyInit_pixelarray(), "pixelarray"); - - // base, color, rect, bufferproxy, surflock, surface - load_submodule("pygame", PyInit_gfxdraw(), "gfxdraw"); - - load_submodule("pygame", PyInit_system(), "system"); - - return PyModule_Create(&mod_pygame_static); -} - -// meson static support - -#include "constants.c" - -#include "rect.c" -#include "pgcompat_rect.c" - -#undef pgSurface_Lock -#undef pgSurface_Unlock -#undef pgSurface_LockBy -#undef pgSurface_UnlockBy -#undef pgSurface_Prep -#undef pgSurface_Unprep - -#include "surflock.c" - -#undef pgColor_New -#undef pgColor_NewLength -#undef pg_RGBAFromObjEx -#undef pg_MappedColorFromObj -#undef pgColor_Type - -#include "color.c" - -#undef pgBufferProxy_New - -#include "bufferproxy.c" - -#undef pgSurface_Blit -#undef pgSurface_New -#undef pgSurface_Type -#undef pgSurface_SetSurface - -#include "surface.c" -#include "simd_blitters_avx2.c" -#include "simd_blitters_sse2.c" - -#include "window.c" - -#undef pgVidInfo_Type -#undef pgVidInfo_New - -#include "display.c" - -#include "draw.c" - -#undef pg_EncodeString -#undef pg_EncodeFilePath -#undef pgRWops_IsFileObject -#undef pgRWops_GetFileExtension -#undef pgRWops_FromFileObject -#undef pgRWops_FromObject - -#include "rwobject.c" - -#define pgSurface_New(surface) (pgSurfaceObject *)pgSurface_New2((surface), 1) -#include "render.c" -#include "image.c" - -#include "imageext.c" - -#include "mask.c" - -#undef pg_EnableKeyRepeat -#undef pg_GetKeyRepeat -#undef pgEvent_FillUserEvent -#undef pgEvent_Type -#undef pgEvent_New - -#include "joystick.c" - -#include "event.c" - -#include "mouse.c" - -#include "key.c" - -#include "time.c" - -#include "system.c" -#include "geometry.c" - -#if defined(DEC_CONST) - #undef DEC_CONST -#endif -#include "_freetype.c" -#include "freetype/ft_wrap.c" -#include "freetype/ft_render.c" -#include "freetype/ft_render_cb.c" -#include "freetype/ft_cache.c" -#include "freetype/ft_layout.c" -#include "freetype/ft_unicode.c" - -#include "font.c" - -#include "mixer.c" - -#include "music.c" - -#include "gfxdraw.c" - -#include "alphablit.c" - -#include "surface_fill.c" -#include "pixelarray.c" -#include "pixelcopy.c" -#include "newbuffer.c" - -#include "_sdl2/controller.c" -// #include "_sdl2/controller_old.c" -// #include "_sdl2/mixer.c" -#include "_sdl2/touch.c" -#include "_sdl2/sdl2.c" - -#include "transform.c" -// that remove some warnings -#undef MAX -#undef MIN -#include "scale2x.c" - -#include "math.c" - - -#endif // defined(BUILD_STATIC) From 1cf6be9d6c308602f680ab08a2e286007d8ed3cf Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sun, 20 Jul 2025 05:29:45 +0200 Subject: [PATCH 3/6] ok mixer, static base --- dev.py | 2 +- src_c/meson.build | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev.py b/dev.py index 6b380ac56f..8498526270 100644 --- a/dev.py +++ b/dev.py @@ -245,7 +245,7 @@ def cmd_build(self): install_args.extend([ f'--config-settings=setup-args=--cross-file={os.getcwd()}/meson-cross-{WASM}.ini', "-Csetup-args=-Dmidi=disabled", - '-Csetup-args=-Dc_link_args=-lfreetype -lharfbuzz -lSDL2_ttf', + '-Csetup-args=-Dc_link_args=-Os -g0 -lfreetype -lharfbuzz -lSDL2_ttf -logg -lvorbis -lSDL2_mixer_ogg', ]) wheel_dir = Path("./whl") diff --git a/src_c/meson.build b/src_c/meson.build index d590af37cf..512c3c198e 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -1,11 +1,11 @@ # first the "required" modules base = py.extension_module( - 'pygame_static', + 'base', 'base.c', c_args: warnings_error, dependencies: pg_base_deps, install: true, -# subdir: pg, + subdir: pg, ) if host_machine.system() != 'emscripten' From ff8c8136e48f1f430876071e5c7c995303f5d502 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sun, 20 Jul 2025 12:14:38 +0200 Subject: [PATCH 4/6] fence _sdl2, in for not meson --- dev.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dev.py b/dev.py index 8498526270..216ad881e3 100644 --- a/dev.py +++ b/dev.py @@ -37,10 +37,10 @@ if __import__("sysconfig").get_config_var("HOST_GNU_TYPE").find('wasm') >= 0: - if __import__("sysconfig").get_config_var("HOST_GNU_TYPE").find('wasi')>=0: - WASM='wasi' + if __import__("sysconfig").get_config_var("HOST_GNU_TYPE").find('wasi') >= 0: + WASM = 'wasi' else: - WASM='emscripten' + WASM = 'emscripten' else: WASM = '' @@ -242,11 +242,14 @@ def cmd_build(self): if WASM: stripped = True - install_args.extend([ - f'--config-settings=setup-args=--cross-file={os.getcwd()}/meson-cross-{WASM}.ini', - "-Csetup-args=-Dmidi=disabled", - '-Csetup-args=-Dc_link_args=-Os -g0 -lfreetype -lharfbuzz -lSDL2_ttf -logg -lvorbis -lSDL2_mixer_ogg', - ]) + install_args.extend( + [ + f'--config-settings=setup-args=--cross-file={os.getcwd()}/meson-cross-{WASM}.ini', + "-Csetup-args=-Dmidi=disabled", + '-Csetup-args=-Dc_args=-Os -g0 -DBUILD_STATIC -DNO_SDL2', + '-Csetup-args=-Dc_link_args=-Os -g0 -lfreetype -lharfbuzz -lSDL2_ttf -logg -lvorbis -lSDL2_mixer_ogg', + ] + ) wheel_dir = Path("./whl") if not wheel_dir: From e4fca9a91a5a80e7bf0ab4078e2319abb17e9edf Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sun, 20 Jul 2025 12:32:35 +0200 Subject: [PATCH 5/6] use sdk opts --- dev.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev.py b/dev.py index 216ad881e3..92a95fb1f4 100644 --- a/dev.py +++ b/dev.py @@ -246,8 +246,8 @@ def cmd_build(self): [ f'--config-settings=setup-args=--cross-file={os.getcwd()}/meson-cross-{WASM}.ini', "-Csetup-args=-Dmidi=disabled", - '-Csetup-args=-Dc_args=-Os -g0 -DBUILD_STATIC -DNO_SDL2', - '-Csetup-args=-Dc_link_args=-Os -g0 -lfreetype -lharfbuzz -lSDL2_ttf -logg -lvorbis -lSDL2_mixer_ogg', + '-Csetup-args=-Dc_args=-DBUILD_STATIC -DNO_SDL2', + '-Csetup-args=-Dc_link_args=-lfreetype -lharfbuzz -lSDL2_ttf -logg -lvorbis -lSDL2_mixer_ogg', ] ) wheel_dir = Path("./whl") From 3097cf76dbeae8c9f87d849c3aee51f446910413 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Sun, 20 Jul 2025 13:27:45 +0200 Subject: [PATCH 6/6] _sdl2 ok --- dev.py | 2 +- src_c/meson.build | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dev.py b/dev.py index 92a95fb1f4..e330507a4c 100644 --- a/dev.py +++ b/dev.py @@ -247,7 +247,7 @@ def cmd_build(self): f'--config-settings=setup-args=--cross-file={os.getcwd()}/meson-cross-{WASM}.ini', "-Csetup-args=-Dmidi=disabled", '-Csetup-args=-Dc_args=-DBUILD_STATIC -DNO_SDL2', - '-Csetup-args=-Dc_link_args=-lfreetype -lharfbuzz -lSDL2_ttf -logg -lvorbis -lSDL2_mixer_ogg', + '-Csetup-args=-Dc_link_args=-lfreetype -lharfbuzz -lSDL2_ttf -lSDL2_image -logg -lvorbis -lSDL2_mixer_ogg', ] ) wheel_dir = Path("./whl") diff --git a/src_c/meson.build b/src_c/meson.build index 512c3c198e..ac95338b4d 100644 --- a/src_c/meson.build +++ b/src_c/meson.build @@ -460,4 +460,9 @@ if portmidi_dep.found() ) endif +else # !emscripten +# TODO: sizes ! +if sdl_api != 3 +subdir('_sdl2') endif +endif # emscripten