diff --git a/.github/workflows/run-ubuntu-checks.yml b/.github/workflows/run-ubuntu-checks.yml index 06eb1ccbd6..de99d94e8b 100644 --- a/.github/workflows/run-ubuntu-checks.yml +++ b/.github/workflows/run-ubuntu-checks.yml @@ -101,7 +101,7 @@ jobs: id: build-pygame-ce run: | pyenv global ${{ matrix.python }}-debug - python dev.py build --lax --coverage + python dev.py build --lax --coverage --ctest - name: Run tests env: @@ -135,13 +135,19 @@ jobs: steps: - uses: actions/checkout@v4.2.2 + with: + fetch-depth: 0 # fetch full history - name: Check if any src_c files changed id: check-changes + continue-on-error: true run: | - git fetch origin ${{ github.base_ref }} --depth=1 || true - git checkout ${{ github.base_ref }} - CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) + if [[ "${{ github.event_name }}" == "pull_request" ]]; then + CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD) + else + CHANGED_FILES=$(git diff --name-only HEAD^1...HEAD) + fi + echo "Changed files: $CHANGED_FILES" echo "$CHANGED_FILES" | grep '^src_c/' || echo "skip=true" >> "$GITHUB_OUTPUT" - name: Install cppcheck diff --git a/.gitignore b/.gitignore index ee57d22510..f29b774e44 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,10 @@ # Ruff .ruff_cache +# Meson subprojects +subprojects/* +!subprojects/*.wrap + # Other envdev* .virtualenv* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a9476d3460..cd840221a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,6 +15,7 @@ repos: | ^.*\.svg$ | ^.*\.sfd$ | docs/LGPL.txt + | subprojects/.* )$ - id: trailing-whitespace exclude: | @@ -23,6 +24,7 @@ repos: | ^.*\.svg$ | ^.*\.sfd$ | docs/LGPL.txt + | subprojects/.* )$ - repo: https://github.com/astral-sh/ruff-pre-commit @@ -47,4 +49,5 @@ repos: | src_c/include/sse2neon.h | src_c/include/pythoncapi_compat.h | src_c/pypm.c + | subprojects/.* )$ diff --git a/ctest/base_ctest.c b/ctest/base_ctest.c new file mode 100644 index 0000000000..c001552ad6 --- /dev/null +++ b/ctest/base_ctest.c @@ -0,0 +1,121 @@ +#include + +#include "base.h" +#include "test_common.h" + +static PyObject *base_module; + +/* setUp and tearDown must be nonstatic void(void) */ +void setUp(void) {} + +void tearDown(void) {} + +/** + * @brief Tests _pg_is_int_tuple when passed a tuple of ints + */ +PG_CTEST(test__pg_is_int_tuple_nominal)(PyObject *self, PyObject *_null) { + PyObject *arg1 = Py_BuildValue("(iii)", 1, 2, 3); + PyObject *arg2 = Py_BuildValue("(iii)", -1, -2, -3); + PyObject *arg3 = Py_BuildValue("(iii)", 1, -2, -3); + + TEST_ASSERT_EQUAL(1, _pg_is_int_tuple(arg1)); + TEST_ASSERT_EQUAL(1, _pg_is_int_tuple(arg2)); + TEST_ASSERT_EQUAL(1, _pg_is_int_tuple(arg3)); + + Py_RETURN_NONE; +} + +/** + * @brief Tests _pg_is_int_tuple when passed a tuple of non-numeric values + */ +PG_CTEST(test__pg_is_int_tuple_failureModes)(PyObject *self, PyObject *_null) { + PyObject *arg1 = + Py_BuildValue("(sss)", (char *)"Larry", (char *)"Moe", (char *)"Curly"); + PyObject *arg2 = Py_BuildValue("(sss)", (char *)NULL, (char *)NULL, + (char *)NULL); // tuple of None's + PyObject *arg3 = Py_BuildValue("(OOO)", arg1, arg2, arg1); + + TEST_ASSERT_EQUAL(0, _pg_is_int_tuple(arg1)); + TEST_ASSERT_EQUAL(0, _pg_is_int_tuple(arg2)); + TEST_ASSERT_EQUAL(0, _pg_is_int_tuple(arg3)); + + Py_RETURN_NONE; +} + +/** + * @brief Tests _pg_is_int_tuple when passed a tuple of floats + */ +PG_CTEST(test__pg_is_int_tuple_floats)(PyObject *self, PyObject *_null) { + PyObject *arg1 = Py_BuildValue("(ddd)", 1.0, 2.0, 3.0); + PyObject *arg2 = Py_BuildValue("(ddd)", -1.1, -2.2, -3.3); + PyObject *arg3 = Py_BuildValue("(ddd)", 1.0, -2.0, -3.1); + + TEST_ASSERT_EQUAL(0, _pg_is_int_tuple(arg1)); + TEST_ASSERT_EQUAL(0, _pg_is_int_tuple(arg2)); + TEST_ASSERT_EQUAL(0, _pg_is_int_tuple(arg3)); + + Py_RETURN_NONE; +} + +/*=======Test Reset Option=====*/ +/* This must be void(void) */ +void resetTest(void) { + tearDown(); + setUp(); +} + +/*=======Exposed Test Reset Option=====*/ +static PyObject *reset_test(PyObject *self, PyObject *_null) { + resetTest(); + + Py_RETURN_NONE; +} + +/*=======Run The Tests=======*/ +static PyObject *run_tests(PyObject *self, PyObject *_null) { + UnityBegin("base_ctest.c"); + RUN_TEST_PG_INTERNAL(test__pg_is_int_tuple_nominal); + RUN_TEST_PG_INTERNAL(test__pg_is_int_tuple_failureModes); + RUN_TEST_PG_INTERNAL(test__pg_is_int_tuple_floats); + + return PyLong_FromLong(UnityEnd()); +} + +static PyMethodDef base_test_methods[] = { + {"test__pg_is_int_tuple_nominal", + (PyCFunction)test__pg_is_int_tuple_nominal, METH_NOARGS, + "Tests _pg_is_int_tuple when passed a tuple of ints"}, + {"test__pg_is_int_tuple_failureModes", + (PyCFunction)test__pg_is_int_tuple_failureModes, METH_NOARGS, + "Tests _pg_is_int_tuple when passed a tuple of non-numeric values"}, + {"test__pg_is_int_tuple_floats", (PyCFunction)test__pg_is_int_tuple_floats, + METH_NOARGS, "Tests _pg_is_int_tuple when passed a tuple of floats"}, + {"reset_test", (PyCFunction)reset_test, METH_NOARGS, + "Resets the test suite between tests, run_tests automatically calls this " + "after each test case it calls"}, + {"run_tests", (PyCFunction)run_tests, METH_NOARGS, + "Runs all the tests in this test wuite"}, + {NULL, NULL, 0, NULL}}; + +MODINIT_DEFINE(base_ctest) { + PyObject *module; + + static struct PyModuleDef _module = { + PyModuleDef_HEAD_INIT, + "base_ctest", + "C unit tests for the pygame.base internal implementation", + -1, + base_test_methods, + NULL, + NULL, + NULL, + NULL}; + + /* create the module */ + module = PyModule_Create(&_module); + if (!module) { + return NULL; + } + + return module; +} diff --git a/ctest/meson.build b/ctest/meson.build new file mode 100644 index 0000000000..aa961e79c6 --- /dev/null +++ b/ctest/meson.build @@ -0,0 +1,13 @@ +unity_subproject = subproject('unity') +unity_dependency = unity_subproject.get_variable('unity_dep') + +base_ctest = py.extension_module( + 'base_ctest', + 'base_ctest.c', + c_args: warnings_error, + dependencies: [pg_base_deps, unity_dependency], + sources: ['../src_c/base.c'], + install: true, + subdir: pg, + include_directories: ['../src_c'] +) diff --git a/ctest/test_common.h b/ctest/test_common.h new file mode 100644 index 0000000000..4442f6dce2 --- /dev/null +++ b/ctest/test_common.h @@ -0,0 +1,48 @@ +#include + +#include "unity.h" + +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +struct TestCase { + char *test_name; + int line_num; +}; + +/* + This will take some explanation... the PG_CTEST macro defines two things + for an individual test case. The test case itself, and a struct instance + called meta_TEST_CASE_NAME. The struct has two pieces of important + information that unity needs: the name in string format and the line + number of the test. This would be an absolute nighmare to maintain by + hand, so I defined a macro to do it automagically for us. + + The RUN_TEST_PG_INTERNAL macro then references that struct for each test + case that we tell it about and automatically populates the unity fields + with the requisite data. + + Note that the arguments to the test function must be *exactly* + (PyObject * self, PyObject * _null), but due to gcc throwing a fit, I + cannot just use token pasting to have the macro generate that part for me +*/ +#define PG_CTEST(TestFunc) \ + static struct TestCase meta_##TestFunc = {#TestFunc, __LINE__}; \ + static PyObject *TestFunc + +#define RUN_TEST_PG_INTERNAL(TestFunc) \ + { \ + Unity.CurrentTestName = meta_##TestFunc.test_name; \ + Unity.CurrentTestLineNumber = meta_##TestFunc.line_num; \ + Unity.NumberOfTests++; \ + if (TEST_PROTECT()) { \ + setUp(); \ + TestFunc(self, _null); \ + } \ + if (TEST_PROTECT()) { \ + tearDown(); \ + } \ + UnityConcludeTest(); \ + } + +#endif // #ifndef TEST_COMMON_H diff --git a/dev.py b/dev.py index d334178c04..936adceb7f 100644 --- a/dev.py +++ b/dev.py @@ -30,6 +30,8 @@ ] COVERAGE_ARGS = ["-Csetup-args=-Dcoverage=true"] +CTEST_ARGS = ["-Csetup-args=-Dctest=true"] + # We assume this script works with any pip version above this. PIP_MIN_VERSION = "23.1" @@ -208,6 +210,7 @@ def cmd_build(self): lax = self.args.get("lax", False) sdl3 = self.args.get("sdl3", False) coverage = self.args.get("coverage", False) + ctest = self.args.get("ctest", False) if wheel_dir and coverage: pprint("Cannot pass --wheel and --coverage together", Colors.RED) sys.exit(1) @@ -221,6 +224,8 @@ def cmd_build(self): build_suffix += "-sdl3" if coverage: build_suffix += "-cov" + if ctest: + build_suffix += "-ctest" install_args = [ "--no-build-isolation", f"-Cbuild-dir=.mesonpy-build{build_suffix}", @@ -245,6 +250,9 @@ def cmd_build(self): if coverage: install_args.extend(COVERAGE_ARGS) + if ctest: + install_args.extend(CTEST_ARGS) + info_str = f"with {debug=}, {lax=}, {sdl3=}, and {coverage=}" if wheel_dir: pprint(f"Building wheel at '{wheel_dir}' ({info_str})") @@ -376,6 +384,9 @@ def parse_args(self): "supported if the underlying compiler supports the --coverage argument" ), ) + build_parser.add_argument( + "--ctest", action="store_true", help="Build the C-direct unit tests" + ) # Docs command docs_parser = subparsers.add_parser("docs", help="Generate docs") diff --git a/meson.build b/meson.build index f58ab5b4ff..38d2d63e62 100644 --- a/meson.build +++ b/meson.build @@ -436,4 +436,9 @@ if not get_option('stripped') subdir('buildconfig/stubs') install_subdir('examples', install_dir: pg_dir, install_tag: 'pg-tag') # TODO: install headers? not really important though + + if get_option('ctest') + subproject('unity') + subdir('ctest') + endif endif diff --git a/meson_options.txt b/meson_options.txt index e433b07f52..aec5f9ce4d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -24,19 +24,23 @@ option('midi', type: 'feature', value: 'enabled') # Controls whether to make a "stripped" pygame install. Enabling this disables # the bundling of docs/examples/tests/stubs in the wheels. # The default behaviour is to bundle all of these. -option('stripped', type: 'boolean', value: 'false') +option('stripped', type: 'boolean', value: false) # Controls whether to compile with -Werror (or its msvc equivalent). The default # behaviour is to not do this by default -option('error_on_warns', type: 'boolean', value: 'false') +option('error_on_warns', type: 'boolean', value: false) # Controls whether to error on build if generated docs are missing. Defaults to # false. -option('error_docs_missing', type: 'boolean', value: 'false') +option('error_docs_missing', type: 'boolean', value: false) # Controls whether to do a coverage build. # This argument must be used together with the editable install. option('coverage', type: 'boolean', value: false) +# Controls whether to do to a C unit test build. Defaults to false. +# If "stripped" is true, this is ignored. +option('ctest', type: 'boolean', value: false) + # Controls whether to use SDL3 instead of SDL2. The default is to use SDL2 option('sdl_api', type: 'integer', min: 2, max: 3, value: 2) diff --git a/src_c/base.c b/src_c/base.c index 654e73404a..f4cf4136c7 100644 --- a/src_c/base.c +++ b/src_c/base.c @@ -1,3 +1,4 @@ + /* pygame-ce - Python Game Library Copyright (C) 2000-2001 Pete Shinners @@ -22,58 +23,12 @@ #define NO_PYGAME_C_API #define PYGAMEAPI_BASE_INTERNAL -#include "pygame.h" - -#include -#include "doc/pygame_doc.h" -#include "pgarrinter.h" -#include "pgcompat.h" - -/* This file controls all the initialization of - * the module and the various SDL subsystems - */ - -/*platform specific init stuff*/ - -#ifdef MS_WIN32 /*python gives us MS_WIN32*/ -#define WIN32_LEAN_AND_MEAN -#define VC_EXTRALEAN -#include -extern int -SDL_RegisterApp(const char *, Uint32, void *); -#endif - -#if defined(macintosh) -#if (!defined(__MWERKS__) && !TARGET_API_MAC_CARBON) -QDGlobals pg_qd; -#endif -#endif - -#if SDL_BYTEORDER == SDL_LIL_ENDIAN -#define PAI_MY_ENDIAN '<' -#define PAI_OTHER_ENDIAN '>' -#define BUF_OTHER_ENDIAN '>' -#else -#define PAI_MY_ENDIAN '>' -#define PAI_OTHER_ENDIAN '<' -#define BUF_OTHER_ENDIAN '<' -#endif -#define BUF_MY_ENDIAN '=' - -/* Extended array struct */ -typedef struct pg_capsule_interface_s { - PyArrayInterface inter; - Py_intptr_t imem[1]; -} pgCapsuleInterface; +#include "base.h" -/* Py_buffer internal data for an array interface/struct */ -typedef struct pg_view_internals_s { - char format[4]; /* make 4 byte word sized */ - Py_ssize_t imem[1]; -} pgViewInternals; +PG_PixelFormatEnum pg_default_convert_format = 0; /* Custom exceptions */ -static PyObject *pgExc_BufferError = NULL; +PyObject *pgExc_BufferError = NULL; /* Only one instance of the state per process. */ static PyObject *pg_quit_functions = NULL; @@ -83,101 +38,8 @@ SDL_Window *pg_default_window = NULL; pgSurfaceObject *pg_default_screen = NULL; static int pg_env_blend_alpha_SDL2 = 0; -static void -pg_install_parachute(void); -static void -pg_uninstall_parachute(void); -static void -pg_atexit_quit(void); -static int -pgGetArrayStruct(PyObject *, PyObject **, PyArrayInterface **); -static PyObject * -pgArrayStruct_AsDict(PyArrayInterface *); -static PyObject * -pgBuffer_AsArrayInterface(Py_buffer *); -static PyObject * -pgBuffer_AsArrayStruct(Py_buffer *); -static int -_pg_buffer_is_byteswapped(Py_buffer *); -static void -pgBuffer_Release(pg_buffer *); -static int -pgObject_GetBuffer(PyObject *, pg_buffer *, int); -static inline PyObject * -pgObject_getRectHelper(PyObject *, PyObject *const *, Py_ssize_t, PyObject *, - char *); -static int -pgGetArrayInterface(PyObject **, PyObject *); -static int -pgArrayStruct_AsBuffer(pg_buffer *, PyObject *, PyArrayInterface *, int); -static int -_pg_arraystruct_as_buffer(Py_buffer *, PyObject *, PyArrayInterface *, int); -static int -_pg_arraystruct_to_format(char *, PyArrayInterface *, int); -static int -pgDict_AsBuffer(pg_buffer *, PyObject *, int); -static int -_pg_shape_check(PyObject *); -static int -_pg_typestr_check(PyObject *); -static int -_pg_strides_check(PyObject *); -static int -_pg_data_check(PyObject *); -static int -_pg_is_int_tuple(PyObject *); -static int -_pg_values_as_buffer(Py_buffer *, int, PyObject *, PyObject *, PyObject *, - PyObject *); -static int -_pg_int_tuple_as_ssize_arr(PyObject *, Py_ssize_t *); -static int -_pg_typestr_as_format(PyObject *, char *, Py_ssize_t *); -static PyObject * -pg_view_get_typestr_obj(Py_buffer *); -static PyObject * -pg_view_get_shape_obj(Py_buffer *); -static PyObject * -pg_view_get_strides_obj(Py_buffer *); -static PyObject * -pg_view_get_data_obj(Py_buffer *); -static char -_pg_as_arrayinter_typekind(Py_buffer *); -static char -_pg_as_arrayinter_byteorder(Py_buffer *); -static int -_pg_as_arrayinter_flags(Py_buffer *); -static pgCapsuleInterface * -_pg_new_capsuleinterface(Py_buffer *); -static void -_pg_capsule_PyMem_Free(PyObject *); -static PyObject * -_pg_shape_as_tuple(PyArrayInterface *); -static PyObject * -_pg_typekind_as_str(PyArrayInterface *); -static PyObject * -_pg_strides_as_tuple(PyArrayInterface *); -static PyObject * -_pg_data_as_tuple(PyArrayInterface *); -static PyObject * -pg_get_array_interface(PyObject *, PyObject *); -static void -_pg_release_buffer_array(Py_buffer *); -static void -_pg_release_buffer_generic(Py_buffer *); -static SDL_Window * -pg_GetDefaultWindow(void); -static void -pg_SetDefaultWindow(SDL_Window *); -static pgSurfaceObject * -pg_GetDefaultWindowSurface(void); -static void -pg_SetDefaultWindowSurface(pgSurfaceObject *); -static int -pg_EnvShouldBlendAlphaSDL2(void); - /* compare compiled to linked, raise python error on incompatibility */ -static int +int pg_CheckSDLVersions(void) { #if SDL_VERSION_ATLEAST(3, 0, 0) @@ -246,7 +108,7 @@ pg_RegisterQuit(void (*func)(void)) } } -static PyObject * +PyObject * pg_register_quit(PyObject *self, PyObject *value) { if (!pg_quit_functions) { @@ -263,7 +125,7 @@ pg_register_quit(PyObject *self, PyObject *value) } /* init pygame modules, returns 1 if successful, 0 if fail, with PyErr set*/ -static int +int pg_mod_autoinit(const char *modname) { PyObject *module, *funcobj, *temp; @@ -296,7 +158,7 @@ pg_mod_autoinit(const char *modname) } /* try to quit pygame modules, errors silenced */ -static void +void pg_mod_autoquit(const char *modname) { PyObject *module, *funcobj, *temp; @@ -333,7 +195,7 @@ pg_mod_autoquit(const char *modname) Py_XDECREF(funcobj); } -static PyObject * +PyObject * pg_init(PyObject *self, PyObject *_null) { int i = 0, success = 0, fail = 0; @@ -374,7 +236,7 @@ pg_init(PyObject *self, PyObject *_null) return pg_tuple_couple_from_values_int(success, fail); } -static void +void pg_atexit_quit(void) { /* Maybe it is safe to call SDL_quit more than once after an SDL_Init, @@ -387,7 +249,7 @@ pg_atexit_quit(void) } } -static PyObject * +PyObject * pg_get_sdl_version(PyObject *self, PyObject *args, PyObject *kwargs) { int linked = 1; /* Default is linked version. */ @@ -417,13 +279,13 @@ pg_get_sdl_version(PyObject *self, PyObject *args, PyObject *kwargs) PG_FIND_VNUM_MICRO(version)); } -static PyObject * +PyObject * pg_get_sdl_byteorder(PyObject *self, PyObject *_null) { return PyLong_FromLong(SDL_BYTEORDER); } -static void +void _pg_quit(void) { Py_ssize_t num, i; @@ -491,21 +353,21 @@ _pg_quit(void) Py_END_ALLOW_THREADS; } -static PyObject * +PyObject * pg_quit(PyObject *self, PyObject *_null) { _pg_quit(); Py_RETURN_NONE; } -static PyObject * +PyObject * pg_base_get_init(PyObject *self, PyObject *_null) { return PyBool_FromLong(pg_is_init); } /* internal C API utility functions */ -static int +int pg_IntFromObj(PyObject *obj, int *val) { if (PyFloat_Check(obj)) { @@ -524,7 +386,7 @@ pg_IntFromObj(PyObject *obj, int *val) return 1; } -static int +int pg_IntFromObjIndex(PyObject *obj, int _index, int *val) { int result = 0; @@ -539,7 +401,7 @@ pg_IntFromObjIndex(PyObject *obj, int _index, int *val) return result; } -static int +int pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2) { // First, lets check the size. This returns -1 if invalid and may set an @@ -602,7 +464,7 @@ pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2) return 1; } -static int +int pg_FloatFromObj(PyObject *obj, float *val) { if (PyFloat_Check(obj)) { @@ -618,7 +480,7 @@ pg_FloatFromObj(PyObject *obj, float *val) return 1; } -static int +int pg_FloatFromObjIndex(PyObject *obj, int _index, float *val) { int result = 0; @@ -633,7 +495,7 @@ pg_FloatFromObjIndex(PyObject *obj, int _index, float *val) return result; } -static int +int pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2) { // First, lets check the size. This returns -1 if invalid and may set an @@ -696,106 +558,7 @@ pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2) return 1; } -static inline int -pg_DoubleFromObj(PyObject *obj, double *val) -{ - if (PyFloat_Check(obj)) { - *val = PyFloat_AS_DOUBLE(obj); - return 1; - } - - *val = (double)PyLong_AsLong(obj); - if (PyErr_Occurred()) { - PyErr_Clear(); - return 0; - } - - return 1; -} - -/*Assumes obj is a Sequence, internal or conscious use only*/ -static inline int -_pg_DoubleFromObjIndex(PyObject *obj, int index, double *val) -{ - int result = 0; - - PyObject *item = PySequence_ITEM(obj, index); - if (!item) { - PyErr_Clear(); - return 0; - } - result = pg_DoubleFromObj(item, val); - Py_DECREF(item); - - return result; -} - -static inline int -pg_TwoDoublesFromObj(PyObject *obj, double *val1, double *val2) -{ - Py_ssize_t length; - /*Faster path for tuples and lists*/ - if (pgSequenceFast_Check(obj)) { - length = PySequence_Fast_GET_SIZE(obj); - PyObject **f_arr = PySequence_Fast_ITEMS(obj); - if (length == 2) { - if (!pg_DoubleFromObj(f_arr[0], val1) || - !pg_DoubleFromObj(f_arr[1], val2)) { - return 0; - } - } - else if (length == 1) { - /* Handle case of ((x, y), ) 'nested sequence' */ - return pg_TwoDoublesFromObj(f_arr[0], val1, val2); - } - else { - return 0; - } - } - else if (PySequence_Check(obj)) { - length = PySequence_Length(obj); - if (length == 2) { - if (!_pg_DoubleFromObjIndex(obj, 0, val1)) { - return 0; - } - if (!_pg_DoubleFromObjIndex(obj, 1, val2)) { - return 0; - } - } - else if (length == 1 && !PyUnicode_Check(obj)) { - /* Handle case of ((x, y), ) 'nested sequence' */ - PyObject *tmp = PySequence_ITEM(obj, 0); - int ret = pg_TwoDoublesFromObj(tmp, val1, val2); - Py_DECREF(tmp); - return ret; - } - else { - PyErr_Clear(); - return 0; - } - } - else { - return 0; - } - - return 1; -} - -static inline int -pg_TwoDoublesFromFastcallArgs(PyObject *const *args, Py_ssize_t nargs, - double *val1, double *val2) -{ - if (nargs == 1 && pg_TwoDoublesFromObj(args[0], val1, val2)) { - return 1; - } - else if (nargs == 2 && pg_DoubleFromObj(args[0], val1) && - pg_DoubleFromObj(args[1], val2)) { - return 1; - } - return 0; -} - -static int +int pg_UintFromObj(PyObject *obj, Uint32 *val) { if (PyNumber_Check(obj)) { @@ -816,7 +579,7 @@ pg_UintFromObj(PyObject *obj, Uint32 *val) return 0; } -static int +int pg_UintFromObjIndex(PyObject *obj, int _index, Uint32 *val) { int result = 0; @@ -832,7 +595,7 @@ pg_UintFromObjIndex(PyObject *obj, int _index, Uint32 *val) } /* You probably want to use the pg_RGBAFromObjEx function instead of this. */ -static int +int pg_RGBAFromObj(PyObject *obj, Uint8 *RGBA) { Py_ssize_t length; @@ -872,13 +635,13 @@ pg_RGBAFromObj(PyObject *obj, Uint8 *RGBA) return 1; } -static PyObject * +PyObject * pg_get_error(PyObject *self, PyObject *_null) { return PyUnicode_FromString(SDL_GetError()); } -static PyObject * +PyObject * pg_set_error(PyObject *s, PyObject *args) { char *errstring = NULL; @@ -900,7 +663,7 @@ pg_set_error(PyObject *s, PyObject *args) /*array interface*/ -static int +int pgGetArrayStruct(PyObject *obj, PyObject **cobj_p, PyArrayInterface **inter_p) { PyObject *cobj = PyObject_GetAttrString(obj, "__array_struct__"); @@ -929,7 +692,7 @@ pgGetArrayStruct(PyObject *obj, PyObject **cobj_p, PyArrayInterface **inter_p) return 0; } -static PyObject * +PyObject * pgArrayStruct_AsDict(PyArrayInterface *inter_p) { PyObject *dictobj = Py_BuildValue("{sisNsNsNsN}", "version", (int)3, @@ -957,7 +720,7 @@ pgArrayStruct_AsDict(PyArrayInterface *inter_p) return dictobj; } -static PyObject * +PyObject * pgBuffer_AsArrayInterface(Py_buffer *view_p) { return Py_BuildValue("{sisNsNsNsN}", "version", (int)3, "typestr", @@ -967,7 +730,7 @@ pgBuffer_AsArrayInterface(Py_buffer *view_p) pg_view_get_data_obj(view_p)); } -static PyObject * +PyObject * pgBuffer_AsArrayStruct(Py_buffer *view_p) { void *cinter_p = _pg_new_capsuleinterface(view_p); @@ -984,7 +747,7 @@ pgBuffer_AsArrayStruct(Py_buffer *view_p) return capsule; } -static pgCapsuleInterface * +pgCapsuleInterface * _pg_new_capsuleinterface(Py_buffer *view_p) { int ndim = view_p->ndim; @@ -1021,13 +784,13 @@ _pg_new_capsuleinterface(Py_buffer *view_p) return cinter_p; } -static void +void _pg_capsule_PyMem_Free(PyObject *capsule) { PyMem_Free(PyCapsule_GetPointer(capsule, 0)); } -static int +int _pg_as_arrayinter_flags(Py_buffer *view_p) { int inter_flags = PAI_ALIGNED; /* atomic int types always aligned */ @@ -1045,7 +808,7 @@ _pg_as_arrayinter_flags(Py_buffer *view_p) return inter_flags; } -static PyObject * +PyObject * pg_view_get_typestr_obj(Py_buffer *view) { return PyUnicode_FromFormat("%c%c%i", _pg_as_arrayinter_byteorder(view), @@ -1053,7 +816,7 @@ pg_view_get_typestr_obj(Py_buffer *view) (int)view->itemsize); } -static PyObject * +PyObject * pg_view_get_shape_obj(Py_buffer *view) { PyObject *shapeobj = PyTuple_New(view->ndim); @@ -1074,7 +837,7 @@ pg_view_get_shape_obj(Py_buffer *view) return shapeobj; } -static PyObject * +PyObject * pg_view_get_strides_obj(Py_buffer *view) { PyObject *shapeobj = PyTuple_New(view->ndim); @@ -1095,14 +858,14 @@ pg_view_get_strides_obj(Py_buffer *view) return shapeobj; } -static PyObject * +PyObject * pg_view_get_data_obj(Py_buffer *view) { return Py_BuildValue("NN", PyLong_FromVoidPtr(view->buf), PyBool_FromLong((long)view->readonly)); } -static char +char _pg_as_arrayinter_typekind(Py_buffer *view) { char type = view->format ? view->format[0] : 'B'; @@ -1142,7 +905,7 @@ _pg_as_arrayinter_typekind(Py_buffer *view) return typekind; } -static char +char _pg_as_arrayinter_byteorder(Py_buffer *view) { char format_0 = view->format ? view->format[0] : 'B'; @@ -1174,7 +937,7 @@ _pg_as_arrayinter_byteorder(Py_buffer *view) return byteorder; } -static PyObject * +PyObject * _pg_shape_as_tuple(PyArrayInterface *inter_p) { PyObject *shapeobj = PyTuple_New((Py_ssize_t)inter_p->nd); @@ -1195,7 +958,7 @@ _pg_shape_as_tuple(PyArrayInterface *inter_p) return shapeobj; } -static PyObject * +PyObject * _pg_typekind_as_str(PyArrayInterface *inter_p) { return PyUnicode_FromFormat( @@ -1207,7 +970,7 @@ _pg_typekind_as_str(PyArrayInterface *inter_p) inter_p->typekind, inter_p->itemsize); } -static PyObject * +PyObject * _pg_strides_as_tuple(PyArrayInterface *inter_p) { PyObject *stridesobj = PyTuple_New((Py_ssize_t)inter_p->nd); @@ -1228,7 +991,7 @@ _pg_strides_as_tuple(PyArrayInterface *inter_p) return stridesobj; } -static PyObject * +PyObject * _pg_data_as_tuple(PyArrayInterface *inter_p) { long readonly = (inter_p->flags & PAI_WRITEABLE) == 0; @@ -1237,7 +1000,7 @@ _pg_data_as_tuple(PyArrayInterface *inter_p) PyBool_FromLong(readonly)); } -static PyObject * +PyObject * pg_get_array_interface(PyObject *self, PyObject *arg) { PyObject *cobj; @@ -1252,7 +1015,7 @@ pg_get_array_interface(PyObject *self, PyObject *arg) return dictobj; } -static int +int pgObject_GetBuffer(PyObject *obj, pg_buffer *pg_view_p, int flags) { Py_buffer *view_p = (Py_buffer *)pg_view_p; @@ -1385,33 +1148,7 @@ pgObject_GetBuffer(PyObject *obj, pg_buffer *pg_view_p, int flags) return 0; } -static inline PyObject * -pgObject_getRectHelper(PyObject *rect, PyObject *const *args, Py_ssize_t nargs, - PyObject *kwnames, char *type) -{ - if (nargs > 0) { - Py_DECREF(rect); - return PyErr_Format(PyExc_TypeError, - "get_%s only accepts keyword arguments", type); - } - - if (rect && kwnames) { - Py_ssize_t i, sequence_len; - PyObject **sequence_items; - sequence_items = PySequence_Fast_ITEMS(kwnames); - sequence_len = PyTuple_GET_SIZE(kwnames); - - for (i = 0; i < sequence_len; ++i) { - if ((PyObject_SetAttr(rect, sequence_items[i], args[i]) == -1)) { - Py_DECREF(rect); - return NULL; - } - } - } - return rect; -} - -static void +void pgBuffer_Release(pg_buffer *pg_view_p) { assert(pg_view_p && pg_view_p->release_buffer); @@ -1423,7 +1160,7 @@ pgBuffer_Release(pg_buffer *pg_view_p) PyErr_Restore(type, value, traceback); } -static void +void _pg_release_buffer_generic(Py_buffer *view_p) { if (view_p->obj) { @@ -1432,7 +1169,7 @@ _pg_release_buffer_generic(Py_buffer *view_p) } } -static void +void _pg_release_buffer_array(Py_buffer *view_p) { /* This is deliberately made safe for use on an uninitialized *view_p */ @@ -1443,7 +1180,7 @@ _pg_release_buffer_array(Py_buffer *view_p) _pg_release_buffer_generic(view_p); } -static int +int _pg_buffer_is_byteswapped(Py_buffer *view) { if (view->format) { @@ -1468,7 +1205,7 @@ _pg_buffer_is_byteswapped(Py_buffer *view) return 0; } -static int +int pgGetArrayInterface(PyObject **dict, PyObject *obj) { PyObject *inter = PyObject_GetAttrString(obj, "__array_interface__"); @@ -1491,7 +1228,7 @@ pgGetArrayInterface(PyObject **dict, PyObject *obj) return 0; } -static int +int pgArrayStruct_AsBuffer(pg_buffer *pg_view_p, PyObject *cobj, PyArrayInterface *inter_p, int flags) { @@ -1504,7 +1241,7 @@ pgArrayStruct_AsBuffer(pg_buffer *pg_view_p, PyObject *cobj, return 0; } -static int +int _pg_arraystruct_as_buffer(Py_buffer *view_p, PyObject *cobj, PyArrayInterface *inter_p, int flags) { @@ -1599,7 +1336,7 @@ _pg_arraystruct_as_buffer(Py_buffer *view_p, PyObject *cobj, return 0; } -static int +int _pg_arraystruct_to_format(char *format, PyArrayInterface *inter_p, int max_format_len) { @@ -1724,7 +1461,7 @@ _pg_arraystruct_to_format(char *format, PyArrayInterface *inter_p, return 0; } -static int +int pgDict_AsBuffer(pg_buffer *pg_view_p, PyObject *dict, int flags) { PyObject *shape = PyDict_GetItemString(dict, "shape"); @@ -1753,7 +1490,7 @@ pgDict_AsBuffer(pg_buffer *pg_view_p, PyObject *dict, int flags) return 0; } -static int +int _pg_shape_check(PyObject *op) { if (!op) { @@ -1773,7 +1510,7 @@ _pg_shape_check(PyObject *op) return 0; } -static int +int _pg_typestr_check(PyObject *op) { if (!op) { @@ -1803,7 +1540,7 @@ _pg_typestr_check(PyObject *op) return 0; } -static int +int _pg_data_check(PyObject *op) { PyObject *item; @@ -1830,7 +1567,7 @@ _pg_data_check(PyObject *op) return 0; } -static int +int _pg_strides_check(PyObject *op) { if (op && !_pg_is_int_tuple(op) /* Conditional && */) { @@ -1841,7 +1578,7 @@ _pg_strides_check(PyObject *op) return 0; } -static int +int _pg_is_int_tuple(PyObject *op) { Py_ssize_t i; @@ -1861,7 +1598,7 @@ _pg_is_int_tuple(PyObject *op) return 1; } -static int +int _pg_values_as_buffer(Py_buffer *view_p, int flags, PyObject *typestr, PyObject *shape, PyObject *data, PyObject *strides) { @@ -1976,7 +1713,7 @@ _pg_values_as_buffer(Py_buffer *view_p, int flags, PyObject *typestr, return 0; } -static int +int _pg_int_tuple_as_ssize_arr(PyObject *tp, Py_ssize_t *arr) { Py_ssize_t i; @@ -1991,7 +1728,7 @@ _pg_int_tuple_as_ssize_arr(PyObject *tp, Py_ssize_t *arr) return 0; } -static int +int _pg_typestr_as_format(PyObject *sp, char *format, Py_ssize_t *itemsize_p) { const char *typestr; @@ -2167,13 +1904,13 @@ _pg_typestr_as_format(PyObject *sp, char *format, Py_ssize_t *itemsize_p) } /*Default window(display)*/ -static SDL_Window * +SDL_Window * pg_GetDefaultWindow(void) { return pg_default_window; } -static void +void pg_SetDefaultWindow(SDL_Window *win) { /*Allows a window to be replaced by itself*/ @@ -2186,14 +1923,14 @@ pg_SetDefaultWindow(SDL_Window *win) pg_default_window = win; } -static pgSurfaceObject * +pgSurfaceObject * pg_GetDefaultWindowSurface(void) { /* return a borrowed reference*/ return pg_default_screen; } -static void +void pg_SetDefaultWindowSurface(pgSurfaceObject *screen) { /*a screen surface can be replaced with itself*/ @@ -2205,9 +1942,7 @@ pg_SetDefaultWindowSurface(pgSurfaceObject *screen) pg_default_screen = screen; } -PG_PixelFormatEnum pg_default_convert_format = 0; - -static PG_PixelFormatEnum +PG_PixelFormatEnum pg_GetDefaultConvertFormat(void) { if (pg_default_screen) { @@ -2216,20 +1951,20 @@ pg_GetDefaultConvertFormat(void) return pg_default_convert_format; } -static void +void pg_SetDefaultConvertFormat(PG_PixelFormatEnum format) { pg_default_convert_format = format; } -static int +int pg_EnvShouldBlendAlphaSDL2(void) { return pg_env_blend_alpha_SDL2; } /*error signal handlers(replacing SDL parachute)*/ -static void +void pygame_parachute(int sig) { #ifdef HAVE_SIGNAL_H @@ -2282,7 +2017,7 @@ static int fatal_signals[] = { }; static int parachute_installed = 0; -static void +void pg_install_parachute(void) { #ifdef HAVE_SIGNAL_H @@ -2306,7 +2041,7 @@ pg_install_parachute(void) return; } -static void +void pg_uninstall_parachute(void) { #ifdef HAVE_SIGNAL_H @@ -2346,13 +2081,6 @@ static PyMethodDef _base_methods[] = { "return an array struct interface as an interface dictionary"}, {NULL, NULL, 0, NULL}}; -#if defined(BUILD_STATIC) && defined(NO_PYGAME_C_API) -// in case of wasm+dynamic loading it could be a trampoline in the globals -// generated at runtime. -// when building static make global accessible symbol directly. -static PyObject *pgExc_SDLError; -#endif - MODINIT_DEFINE(base) { PyObject *module, *apiobj, *atexit; diff --git a/src_c/base.h b/src_c/base.h new file mode 100644 index 0000000000..8fb0970eac --- /dev/null +++ b/src_c/base.h @@ -0,0 +1,356 @@ +#ifndef BASE_H +#define BASE_H + +#define NO_PYGAME_C_API +#define PYGAMEAPI_BASE_INTERNAL + +#include "pygame.h" + +#include +#include "doc/pygame_doc.h" +#include "pgarrinter.h" +#include "pgcompat.h" + +/* This file controls all the initialization of + * the module and the various SDL subsystems + */ + +/*platform specific init stuff*/ + +#ifdef MS_WIN32 /*python gives us MS_WIN32*/ +#define WIN32_LEAN_AND_MEAN +#define VC_EXTRALEAN +#include +extern int +SDL_RegisterApp(const char *, Uint32, void *); +#endif + +#if defined(macintosh) +#if (!defined(__MWERKS__) && !TARGET_API_MAC_CARBON) +QDGlobals pg_qd; +#endif +#endif + +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define PAI_MY_ENDIAN '<' +#define PAI_OTHER_ENDIAN '>' +#define BUF_OTHER_ENDIAN '>' +#else +#define PAI_MY_ENDIAN '>' +#define PAI_OTHER_ENDIAN '<' +#define BUF_OTHER_ENDIAN '<' +#endif +#define BUF_MY_ENDIAN '=' + +/* Extended array struct */ +typedef struct pg_capsule_interface_s { + PyArrayInterface inter; + Py_intptr_t imem[1]; +} pgCapsuleInterface; + +/* Py_buffer internal data for an array interface/struct */ +typedef struct pg_view_internals_s { + char format[4]; /* make 4 byte word sized */ + Py_ssize_t imem[1]; +} pgViewInternals; + +extern PG_PixelFormatEnum pg_default_convert_format; +extern PyObject *pgExc_BufferError; +extern SDL_Window *pg_default_window; +extern pgSurfaceObject *pg_default_screen; + +void +pg_install_parachute(void); +void +pg_uninstall_parachute(void); +void +pg_atexit_quit(void); +int +pgGetArrayStruct(PyObject *, PyObject **, PyArrayInterface **); +PyObject * +pgArrayStruct_AsDict(PyArrayInterface *); +PyObject * +pgBuffer_AsArrayInterface(Py_buffer *); +PyObject * +pgBuffer_AsArrayStruct(Py_buffer *); +int +_pg_buffer_is_byteswapped(Py_buffer *); +void +pgBuffer_Release(pg_buffer *); +int +pgObject_GetBuffer(PyObject *, pg_buffer *, int); +static inline PyObject * +pgObject_getRectHelper(PyObject *, PyObject *const *, Py_ssize_t, PyObject *, + char *); +int +pgGetArrayInterface(PyObject **, PyObject *); +int +pgArrayStruct_AsBuffer(pg_buffer *, PyObject *, PyArrayInterface *, int); +int +_pg_arraystruct_as_buffer(Py_buffer *, PyObject *, PyArrayInterface *, int); +int +_pg_arraystruct_to_format(char *, PyArrayInterface *, int); +int +pgDict_AsBuffer(pg_buffer *, PyObject *, int); +int +_pg_shape_check(PyObject *); +int +_pg_typestr_check(PyObject *); +int +_pg_strides_check(PyObject *); +int +_pg_data_check(PyObject *); +int +_pg_is_int_tuple(PyObject *); +int +_pg_values_as_buffer(Py_buffer *, int, PyObject *, PyObject *, PyObject *, + PyObject *); +int +_pg_int_tuple_as_ssize_arr(PyObject *, Py_ssize_t *); +int +_pg_typestr_as_format(PyObject *, char *, Py_ssize_t *); +PyObject * +pg_view_get_typestr_obj(Py_buffer *); +PyObject * +pg_view_get_shape_obj(Py_buffer *); +PyObject * +pg_view_get_strides_obj(Py_buffer *); +PyObject * +pg_view_get_data_obj(Py_buffer *); +char +_pg_as_arrayinter_typekind(Py_buffer *); +char +_pg_as_arrayinter_byteorder(Py_buffer *); +int +_pg_as_arrayinter_flags(Py_buffer *); +pgCapsuleInterface * +_pg_new_capsuleinterface(Py_buffer *); +void +_pg_capsule_PyMem_Free(PyObject *); +PyObject * +_pg_shape_as_tuple(PyArrayInterface *); +PyObject * +_pg_typekind_as_str(PyArrayInterface *); +PyObject * +_pg_strides_as_tuple(PyArrayInterface *); +PyObject * +_pg_data_as_tuple(PyArrayInterface *); +PyObject * +pg_get_array_interface(PyObject *, PyObject *); +void +_pg_release_buffer_array(Py_buffer *); +void +_pg_release_buffer_generic(Py_buffer *); +SDL_Window * +pg_GetDefaultWindow(void); +void +pg_SetDefaultWindow(SDL_Window *); +pgSurfaceObject * +pg_GetDefaultWindowSurface(void); +void +pg_SetDefaultWindowSurface(pgSurfaceObject *); +int +pg_EnvShouldBlendAlphaSDL2(void); +int +pg_CheckSDLVersions(void); +void +pg_RegisterQuit(void (*func)(void)); +PyObject * +pg_register_quit(PyObject *self, PyObject *value); +int +pg_mod_autoinit(const char *modname); +void +pg_mod_autoquit(const char *modname); +PyObject * +pg_init(PyObject *self, PyObject *_null); +PyObject * +pg_get_sdl_version(PyObject *self, PyObject *args, PyObject *kwargs); +PyObject * +pg_get_sdl_byteorder(PyObject *self, PyObject *_null); +void +_pg_quit(void); +PyObject * +pg_quit(PyObject *self, PyObject *_null); +PyObject * +pg_base_get_init(PyObject *self, PyObject *_null); +/* internal C API utility functions */ +int +pg_IntFromObj(PyObject *obj, int *val); +int +pg_IntFromObjIndex(PyObject *obj, int _index, int *val); +int +pg_TwoIntsFromObj(PyObject *obj, int *val1, int *val2); +int +pg_FloatFromObj(PyObject *obj, float *val); +int +pg_FloatFromObjIndex(PyObject *obj, int _index, float *val); +int +pg_TwoFloatsFromObj(PyObject *obj, float *val1, float *val2); +static inline int +pg_DoubleFromObj(PyObject *obj, double *val); +/*Assumes obj is a Sequence, internal or conscious use only*/ +static inline int +_pg_DoubleFromObjIndex(PyObject *obj, int index, double *val); +static inline int +pg_TwoDoublesFromObj(PyObject *obj, double *val1, double *val2); +static inline int +pg_TwoDoublesFromFastcallArgs(PyObject *const *args, Py_ssize_t nargs, + double *val1, double *val2); +int +pg_UintFromObj(PyObject *obj, Uint32 *val); +int +pg_UintFromObjIndex(PyObject *obj, int _index, Uint32 *val); +/* You probably want to use the pg_RGBAFromObjEx function instead of this. */ +int +pg_RGBAFromObj(PyObject *obj, Uint8 *RGBA); +PyObject * +pg_get_error(PyObject *self, PyObject *_null); +PyObject * +pg_set_error(PyObject *s, PyObject *args); + +/*error signal handlers(replacing SDL parachute)*/ +void +pygame_parachute(int sig); + +void +pg_SetDefaultConvertFormat(PG_PixelFormatEnum format); + +PG_PixelFormatEnum +pg_GetDefaultConvertFormat(void); + +#if defined(BUILD_STATIC) && defined(NO_PYGAME_C_API) +// in case of wasm+dynamic loading it could be a trampoline in the globals +// generated at runtime. +// when building static make global accessible symbol directly. +extern PyObject *pgExc_SDLError; +#endif + +MODINIT_DEFINE(base); + +/*=======static inline function definitions=======*/ +static inline PyObject * +pgObject_getRectHelper(PyObject *rect, PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames, char *type) +{ + if (nargs > 0) { + Py_DECREF(rect); + return PyErr_Format(PyExc_TypeError, + "get_%s only accepts keyword arguments", type); + } + + if (rect && kwnames) { + Py_ssize_t i, sequence_len; + PyObject **sequence_items; + sequence_items = PySequence_Fast_ITEMS(kwnames); + sequence_len = PyTuple_GET_SIZE(kwnames); + + for (i = 0; i < sequence_len; ++i) { + if ((PyObject_SetAttr(rect, sequence_items[i], args[i]) == -1)) { + Py_DECREF(rect); + return NULL; + } + } + } + return rect; +} + +static inline int +pg_DoubleFromObj(PyObject *obj, double *val) +{ + if (PyFloat_Check(obj)) { + *val = PyFloat_AS_DOUBLE(obj); + return 1; + } + + *val = (double)PyLong_AsLong(obj); + if (PyErr_Occurred()) { + PyErr_Clear(); + return 0; + } + + return 1; +} + +/*Assumes obj is a Sequence, internal or conscious use only*/ +static inline int +_pg_DoubleFromObjIndex(PyObject *obj, int index, double *val) +{ + int result = 0; + + PyObject *item = PySequence_ITEM(obj, index); + if (!item) { + PyErr_Clear(); + return 0; + } + result = pg_DoubleFromObj(item, val); + Py_DECREF(item); + + return result; +} + +static inline int +pg_TwoDoublesFromObj(PyObject *obj, double *val1, double *val2) +{ + Py_ssize_t length; + /*Faster path for tuples and lists*/ + if (pgSequenceFast_Check(obj)) { + length = PySequence_Fast_GET_SIZE(obj); + PyObject **f_arr = PySequence_Fast_ITEMS(obj); + if (length == 2) { + if (!pg_DoubleFromObj(f_arr[0], val1) || + !pg_DoubleFromObj(f_arr[1], val2)) { + return 0; + } + } + else if (length == 1) { + /* Handle case of ((x, y), ) 'nested sequence' */ + return pg_TwoDoublesFromObj(f_arr[0], val1, val2); + } + else { + return 0; + } + } + else if (PySequence_Check(obj)) { + length = PySequence_Length(obj); + if (length == 2) { + if (!_pg_DoubleFromObjIndex(obj, 0, val1)) { + return 0; + } + if (!_pg_DoubleFromObjIndex(obj, 1, val2)) { + return 0; + } + } + else if (length == 1 && !PyUnicode_Check(obj)) { + /* Handle case of ((x, y), ) 'nested sequence' */ + PyObject *tmp = PySequence_ITEM(obj, 0); + int ret = pg_TwoDoublesFromObj(tmp, val1, val2); + Py_DECREF(tmp); + return ret; + } + else { + PyErr_Clear(); + return 0; + } + } + else { + return 0; + } + + return 1; +} + +static inline int +pg_TwoDoublesFromFastcallArgs(PyObject *const *args, Py_ssize_t nargs, + double *val1, double *val2) +{ + if (nargs == 1 && pg_TwoDoublesFromObj(args[0], val1, val2)) { + return 1; + } + else if (nargs == 2 && pg_DoubleFromObj(args[0], val1) && + pg_DoubleFromObj(args[1], val2)) { + return 1; + } + return 0; +} + +#endif // #ifndef BASE_H diff --git a/src_c/static.c b/src_c/static.c index 87c0264d14..6201f1bc18 100644 --- a/src_c/static.c +++ b/src_c/static.c @@ -334,7 +334,7 @@ PyInit_pygame_static() #endif // defined(BUILD_STATIC) -#include "base.c" +#include "base.h" #include "rect.c" #include "pgcompat_rect.c" diff --git a/subprojects/unity.wrap b/subprojects/unity.wrap new file mode 100644 index 0000000000..440c66c6bc --- /dev/null +++ b/subprojects/unity.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://github.com/ThrowTheSwitch/Unity.git +revision = v2.6.1 \ No newline at end of file diff --git a/test/ctest_test.py b/test/ctest_test.py new file mode 100644 index 0000000000..73f30fdbfb --- /dev/null +++ b/test/ctest_test.py @@ -0,0 +1,17 @@ +import unittest + +base_ctest = None +try: + import pygame.base_ctest as base_ctest +except ModuleNotFoundError: + pass + + +class Ctest(unittest.TestCase): + @unittest.skipIf(base_ctest is None, "base_ctest not built") + def test_run_base_ctests(self): + self.assertEqual(base_ctest.run_tests(), 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/meson.build b/test/meson.build index 1e2cadfa7d..5ee558cc3e 100644 --- a/test/meson.build +++ b/test/meson.build @@ -9,6 +9,7 @@ test_files = files( 'color_test.py', 'constants_test.py', 'controller_test.py', + 'ctest_test.py', 'cursors_test.py', 'debug_test.py', 'display_test.py',