diff --git a/.ci/getsdl2.py b/.ci/getsdl2.py index 15a3cb1..4ee84c1 100644 --- a/.ci/getsdl2.py +++ b/.ci/getsdl2.py @@ -34,8 +34,8 @@ } libversions = { - '2.0.22.post1': { # NOTE: Temporary until SDL2 2.24.0 is released - 'SDL2': '2.0.22', + '2.24.0': { + 'SDL2': '2.24.0', 'SDL2_mixer': '2.6.0', 'SDL2_ttf': '2.20.0', 'SDL2_image': '2.6.0', diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 0882dd7..72d8f25 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -23,17 +23,18 @@ jobs: fail-fast: false matrix: python-version: ['2.7', '3.6', '3.7', '3.8', '3.9', '3.10'] - sdl2: ['2.0.22'] + sdl2: ['2.24.0'] name-prefix: ['Linux (Python '] include: - python-version: 'pypy-2.7' - sdl2: '2.0.22' + sdl2: '2.24.0' name-prefix: 'Experimental / Linux (' - python-version: 'pypy-3.7' - sdl2: '2.0.22' + sdl2: '2.24.0' name-prefix: 'Experimental / Linux (' env: + PYSDL2_DLL_VERSION: ${{ matrix.sdl2 }} SDL_VIDEODRIVER: dummy SDL_AUDIODRIVER: dummy SDL_RENDER_DRIVER: software @@ -51,7 +52,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install numpy pytest pillow - python -m pip install pysdl2-dll + python -m pip install pysdl2-dll==$PYSDL2_DLL_VERSION - name: Install and test PySDL2 run: | @@ -71,11 +72,11 @@ jobs: fail-fast: false matrix: python-version: [3.9] - sdl2: ['2.0.22.post1', '2.0.20', '2.0.16'] + sdl2: ['2.24.0', '2.0.22', '2.0.20'] name-prefix: ['macOS (Python '] include: - python-version: '2.7' - sdl2: '2.0.22.post1' + sdl2: '2.24.0' name-prefix: 'macOS (Python ' - python-version: '3.9' sdl2: 'from Homebrew' @@ -127,21 +128,27 @@ jobs: matrix: python-version: [3.9] architecture: ['x64'] - sdl2: ['2.0.22.post1', '2.0.20', '2.0.18', '2.0.16', '2.0.14', '2.0.12', - '2.0.10', '2.0.9', '2.0.8', '2.0.7', '2.0.6', '2.0.5'] + sdl2: [ + '2.24.0', '2.0.22', '2.0.20', '2.0.18', '2.0.16', '2.0.14', '2.0.12', + '2.0.10', '2.0.9', '2.0.8', '2.0.7', '2.0.6', '2.0.5' + ] name-prefix: ['Windows (Python '] include: - python-version: '2.7' architecture: 'x64' - sdl2: '2.0.22.post1' + sdl2: '2.24.0' name-prefix: 'Windows (Python ' - python-version: '2.7' architecture: 'x86' - sdl2: '2.0.22.post1' + sdl2: '2.24.0' name-prefix: 'Windows 32-bit (Python ' - python-version: '3.9' architecture: 'x86' - sdl2: '2.0.22.post1' + sdl2: '2.24.0' + name-prefix: 'Windows 32-bit (Python ' + - python-version: '2.7' + architecture: 'x86' + sdl2: '2.0.22' name-prefix: 'Windows 32-bit (Python ' - python-version: '2.7' architecture: 'x86' diff --git a/doc/news.rst b/doc/news.rst index 8cb983f..e3ed6f7 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -15,6 +15,12 @@ New Features: individually using keyword arguments (e.g. ``sdl2.ext.init(audio=True)`` to initialize the audio subsystem as well as the default video subsystem). Previously, this function only initailized the video subsystem. +* Updated to wrap new functions and constants in SDL2 2.24.0 (PR #246). + +Fixed Bugs: + +* Fixed broken behaviour (and potential segfaults) with usage of + :func:`sdl2.SDL_GUIDToString` on Python 3.6 and older (PR #246). 0.9.13 diff --git a/sdl2/audio.py b/sdl2/audio.py index a75c734..14bde64 100644 --- a/sdl2/audio.py +++ b/sdl2/audio.py @@ -186,6 +186,10 @@ class SDL_AudioStream(c_void_p): SDLFunc("SDL_GetNumAudioDevices", [c_int], c_int), SDLFunc("SDL_GetAudioDeviceName", [c_int, c_int], c_char_p), SDLFunc("SDL_GetAudioDeviceSpec", [c_int, c_int, _P(SDL_AudioSpec)], c_int, added='2.0.16'), + SDLFunc("SDL_GetDefaultAudioInfo", + [_P(c_char_p), _P(SDL_AudioSpec), c_int], + returns = c_int, added = '2.24.0' + ), SDLFunc("SDL_OpenAudioDevice", [c_char_p, c_int, _P(SDL_AudioSpec), _P(SDL_AudioSpec), c_int], returns = SDL_AudioDeviceID @@ -243,6 +247,7 @@ class SDL_AudioStream(c_void_p): SDL_GetNumAudioDevices = _ctypes["SDL_GetNumAudioDevices"] SDL_GetAudioDeviceName = _ctypes["SDL_GetAudioDeviceName"] SDL_GetAudioDeviceSpec = _ctypes["SDL_GetAudioDeviceSpec"] +SDL_GetDefaultAudioInfo = _ctypes["SDL_GetDefaultAudioInfo"] SDL_OpenAudioDevice = _ctypes["SDL_OpenAudioDevice"] SDL_GetAudioStatus = _ctypes["SDL_GetAudioStatus"] SDL_GetAudioDeviceStatus = _ctypes["SDL_GetAudioDeviceStatus"] diff --git a/sdl2/gamecontroller.py b/sdl2/gamecontroller.py index 38133fa..e7b0122 100644 --- a/sdl2/gamecontroller.py +++ b/sdl2/gamecontroller.py @@ -72,6 +72,10 @@ SDL_CONTROLLER_TYPE_PS5 = 7 SDL_CONTROLLER_TYPE_AMAZON_LUNA = 8 SDL_CONTROLLER_TYPE_GOOGLE_STADIA = 9 +SDL_CONTROLLER_TYPE_NVIDIA_SHIELD = 10 +SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT = 11 +SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT = 12 +SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR = 13 SDL_GameControllerAxis = c_int SDL_CONTROLLER_AXIS_INVALID = -1 diff --git a/sdl2/guid.py b/sdl2/guid.py index a5ebd19..8890d8a 100644 --- a/sdl2/guid.py +++ b/sdl2/guid.py @@ -1,7 +1,8 @@ -from ctypes import Structure, c_int, c_char, c_char_p -from ctypes import POINTER as _P +import sys +from ctypes import c_int, c_char_p from .dll import _bind, SDLFunc, AttributeDict from .stdinc import Uint8 +from .joystick import SDL_JoystickGUID __all__ = [ # Defines @@ -11,14 +12,13 @@ # Constants & typedefs -class SDL_GUID(Structure): - _fields_ = [("data", (Uint8 * 16))] +SDL_GUID = SDL_JoystickGUID # Raw ctypes function definitions _funcdefs = [ - SDLFunc("SDL_GUIDToString", [SDL_GUID, _P(c_char), c_int], None, added='2.23.1'), + SDLFunc("SDL_GUIDToString", [SDL_GUID, c_char_p, c_int], None, added='2.23.1'), SDLFunc("SDL_GUIDFromString", [c_char_p], SDL_GUID, added='2.23.1'), ] _ctypes = AttributeDict() @@ -26,6 +26,12 @@ class SDL_GUID(Structure): _ctypes[f.name] = _bind(f.name, f.args, f.returns, f.added) __all__.append(f.name) # Add all bound functions to module namespace +# Workaround for bizarre ctypes bug with older Python versions +# (The joystick function here is a thin wrapper around SDL_GUIDToString) +if sys.version_info < (3, 7, 0): + from .joystick import SDL_JoystickGetGUIDString + _ctypes["SDL_GUIDToString"] = SDL_JoystickGetGUIDString + # Aliases for ctypes bindings diff --git a/sdl2/hints.py b/sdl2/hints.py index ccac1bc..7e03764 100644 --- a/sdl2/hints.py +++ b/sdl2/hints.py @@ -48,7 +48,10 @@ "SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE", "SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE", "SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS", + "SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS", "SDL_HINT_JOYSTICK_HIDAPI_LUNA", + "SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC", + "SDL_HINT_JOYSTICK_HIDAPI_SHIELD", "SDL_HINT_JOYSTICK_HIDAPI_PS4", "SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE", "SDL_HINT_JOYSTICK_HIDAPI_PS5", @@ -58,6 +61,8 @@ "SDL_HINT_JOYSTICK_HIDAPI_STEAM", "SDL_HINT_JOYSTICK_HIDAPI_SWITCH", "SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED", + "SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED", + "SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED", "SDL_HINT_JOYSTICK_HIDAPI_XBOX", "SDL_HINT_JOYSTICK_RAWINPUT", "SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT", @@ -72,6 +77,7 @@ "SDL_HINT_LINUX_JOYSTICK_DEADZONES", "SDL_HINT_MAC_BACKGROUND_APP", "SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK", + "SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH", "SDL_HINT_MOUSE_DOUBLE_CLICK_RADIUS", "SDL_HINT_MOUSE_DOUBLE_CLICK_TIME", "SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH", @@ -80,6 +86,7 @@ "SDL_HINT_MOUSE_RELATIVE_MODE_WARP", "SDL_HINT_MOUSE_RELATIVE_SCALING", "SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE", + "SDL_HINT_MOUSE_RELATIVE_WARP_MOTION", "SDL_HINT_MOUSE_TOUCH_EVENTS", "SDL_HINT_MOUSE_AUTO_CAPTURE", "SDL_HINT_NO_SIGNAL_HANDLERS", @@ -257,6 +264,7 @@ SDL_HINT_MOUSE_RELATIVE_MODE_WARP = b"SDL_MOUSE_RELATIVE_MODE_WARP" SDL_HINT_MOUSE_RELATIVE_SCALING = b"SDL_MOUSE_RELATIVE_SCALING" SDL_HINT_MOUSE_RELATIVE_SPEED_SCALE = b"SDL_MOUSE_RELATIVE_SPEED_SCALE" +SDL_HINT_MOUSE_RELATIVE_WARP_MOTION = b"SDL_MOUSE_RELATIVE_WARP_MOTION" SDL_HINT_MOUSE_TOUCH_EVENTS = b"SDL_MOUSE_TOUCH_EVENTS" SDL_HINT_TOUCH_MOUSE_EVENTS = b"SDL_TOUCH_MOUSE_EVENTS" @@ -278,7 +286,10 @@ SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE = b"SDL_JOYSTICK_HIDAPI_GAMECUBE" SDL_HINT_JOYSTICK_GAMECUBE_RUMBLE_BRAKE = b"SDL_JOYSTICK_GAMECUBE_RUMBLE_BRAKE" SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS = b"SDL_JOYSTICK_HIDAPI_JOY_CONS" +SDL_HINT_JOYSTICK_HIDAPI_COMBINE_JOY_CONS = b"SDL_JOYSTICK_HIDAPI_COMBINE_JOY_CONS" SDL_HINT_JOYSTICK_HIDAPI_LUNA = b"SDL_JOYSTICK_HIDAPI_LUNA" +SDL_HINT_JOYSTICK_HIDAPI_NINTENDO_CLASSIC = b"SDL_JOYSTICK_HIDAPI_NINTENDO_CLASSIC" +SDL_HINT_JOYSTICK_HIDAPI_SHIELD = b"SDL_JOYSTICK_HIDAPI_SHIELD" SDL_HINT_JOYSTICK_HIDAPI_PS4 = b"SDL_JOYSTICK_HIDAPI_PS4" SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE = b"SDL_JOYSTICK_HIDAPI_PS4_RUMBLE" SDL_HINT_JOYSTICK_HIDAPI_PS5 = b"SDL_JOYSTICK_HIDAPI_PS5" @@ -288,6 +299,8 @@ SDL_HINT_JOYSTICK_HIDAPI_STEAM = b"SDL_JOYSTICK_HIDAPI_STEAM" SDL_HINT_JOYSTICK_HIDAPI_SWITCH = b"SDL_JOYSTICK_HIDAPI_SWITCH" SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED = b"SDL_JOYSTICK_HIDAPI_SWITCH_HOME_LED" +SDL_HINT_JOYSTICK_HIDAPI_JOYCON_HOME_LED = b"SDL_JOYSTICK_HIDAPI_JOYCON_HOME_LED" +SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED = b"SDL_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED" SDL_HINT_JOYSTICK_HIDAPI_XBOX = b"SDL_JOYSTICK_HIDAPI_XBOX" SDL_HINT_JOYSTICK_RAWINPUT = b"SDL_JOYSTICK_RAWINPUT" SDL_HINT_JOYSTICK_RAWINPUT_CORRELATE_XINPUT = b"SDL_JOYSTICK_RAWINPUT_CORRELATE_XINPUT" @@ -307,6 +320,7 @@ SDL_HINT_LINUX_JOYSTICK_DEADZONES = b"SDL_LINUX_JOYSTICK_DEADZONES" SDL_HINT_MAC_BACKGROUND_APP = b"SDL_MAC_BACKGROUND_APP" SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK = b"SDL_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK" +SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH = b"SDL_MAC_OPENGL_ASYNC_DISPATCH" SDL_HINT_RPI_VIDEO_LAYER = b"SDL_RPI_VIDEO_LAYER" SDL_HINT_RENDER_DIRECT3D11_DEBUG = b"SDL_RENDER_DIRECT3D11_DEBUG" SDL_HINT_RENDER_DIRECT3D_THREADSAFE = b"SDL_RENDER_DIRECT3D_THREADSAFE" diff --git a/sdl2/joystick.py b/sdl2/joystick.py index 5064349..726d152 100644 --- a/sdl2/joystick.py +++ b/sdl2/joystick.py @@ -3,7 +3,6 @@ from ctypes import POINTER as _P from .dll import _bind, SDLFunc, AttributeDict from .stdinc import Sint16, Sint32, Uint32, Uint16, Uint8, SDL_bool -from .guid import SDL_GUID __all__ = [ # Structs & Opaque Types @@ -72,7 +71,9 @@ # Structs & typedefs SDL_JoystickID = Sint32 -SDL_JoystickGUID = SDL_GUID + +class SDL_JoystickGUID(Structure): + _fields_ = [("data", (Uint8 * 16))] class SDL_Joystick(c_void_p): pass diff --git a/sdl2/keyboard.py b/sdl2/keyboard.py index 4da98ff..b133ec9 100644 --- a/sdl2/keyboard.py +++ b/sdl2/keyboard.py @@ -29,6 +29,7 @@ class SDL_Keysym(Structure): _funcdefs = [ SDLFunc("SDL_GetKeyboardFocus", None, _P(SDL_Window)), SDLFunc("SDL_GetKeyboardState", [_P(c_int)], _P(Uint8)), + SDLFunc("SDL_ResetKeyboard", None, None, added='2.24.0'), SDLFunc("SDL_GetModState", None, SDL_Keymod), SDLFunc("SDL_SetModState", [SDL_Keymod]), SDLFunc("SDL_GetKeyFromScancode", [SDL_Scancode], SDL_Keycode), @@ -56,6 +57,7 @@ class SDL_Keysym(Structure): SDL_GetKeyboardFocus = _ctypes["SDL_GetKeyboardFocus"] SDL_GetKeyboardState = _ctypes["SDL_GetKeyboardState"] +SDL_ResetKeyboard = _ctypes["SDL_ResetKeyboard"] SDL_GetModState = _ctypes["SDL_GetModState"] SDL_SetModState = _ctypes["SDL_SetModState"] SDL_GetKeyFromScancode = _ctypes["SDL_GetKeyFromScancode"] diff --git a/sdl2/test/audio_test.py b/sdl2/test/audio_test.py index abfbe4d..569d249 100644 --- a/sdl2/test/audio_test.py +++ b/sdl2/test/audio_test.py @@ -20,6 +20,7 @@ def with_sdl_audio(): if original_driver: os.environ["SDL_AUDIODRIVER"] = original_driver # Initialize SDL2 with video and audio subsystems + sdl2.SDL_Quit() sdl2.SDL_ClearError() ret = sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_AUDIO) assert sdl2.SDL_GetError() == b"" @@ -31,6 +32,23 @@ def with_sdl_audio(): if original_driver: os.environ["SDL_AUDIODRIVER"] = original_driver +@pytest.fixture +def with_default_driver(with_sdl_audio): + driver = sdl2.SDL_GetCurrentAudioDriver() + if driver == None or sdl2.SDL_GetNumAudioDevices(False) == 0: + sdl2.SDL_QuitSubSystem(SDL_INIT_AUDIO) + os.environ["SDL_AUDIODRIVER"] = b'dummy' + sdl2.SDL_InitSubSystem(SDL_INIT_AUDIO) + driver = sdl2.SDL_GetCurrentAudioDriver() + yield driver + +def _get_audio_drivers(): + drivers = [] + for index in range(sdl2.SDL_GetNumAudioDrivers()): + name = sdl2.SDL_GetAudioDriver(index) + drivers.append(name.decode('utf-8')) + return drivers + # Test macro functions @@ -227,11 +245,10 @@ def test_SDL_GetAudioDeviceName(with_sdl_audio): # Reset audio subsystem SDL_Quit() SDL_Init(0) - for index in range(sdl2.SDL_GetNumAudioDrivers()): + for drivername in _get_audio_drivers(): # Get input/output device names for each audio driver - drivername = sdl2.SDL_GetAudioDriver(index) - backends.append(drivername.decode("utf-8")) - os.environ["SDL_AUDIODRIVER"] = drivername.decode("utf-8") + backends.append(drivername) + os.environ["SDL_AUDIODRIVER"] = drivername # Need to reinitialize subsystem for each driver SDL_InitSubSystem(SDL_INIT_AUDIO) driver = sdl2.SDL_GetCurrentAudioDriver() @@ -258,24 +275,13 @@ def test_SDL_GetAudioDeviceName(with_sdl_audio): print(" - output: {0}".format(str(devices[driver]['output']))) @pytest.mark.skipif(sdl2.dll.version < 2016, reason="not available") -def test_SDL_GetAudioDeviceSpec(with_sdl_audio): - # Reset audio subsystem - SDL_Quit() - SDL_Init(0) - # Find an audio driver with at least one output - SDL_InitSubSystem(SDL_INIT_AUDIO) - driver = sdl2.SDL_GetCurrentAudioDriver() - if driver == None or sdl2.SDL_GetNumAudioDevices(False) == 0: - SDL_QuitSubSystem(SDL_INIT_AUDIO) - os.environ["SDL_AUDIODRIVER"] = b'dummy' - SDL_InitSubSystem(SDL_INIT_AUDIO) - driver = sdl2.SDL_GetCurrentAudioDriver() +def test_SDL_GetAudioDeviceSpec(with_default_driver): + driver = with_default_driver drivername = driver.decode('utf-8') # Get name and spec of first output device outspec = sdl2.SDL_AudioSpec(0, 0, 0, 0) outname = sdl2.SDL_GetAudioDeviceName(0, False).decode('utf-8') ret = sdl2.SDL_GetAudioDeviceSpec(0, False, ctypes.byref(outspec)) - SDL_QuitSubSystem(SDL_INIT_AUDIO) assert ret == 0 # Validate frequency and channel count were set hz = outspec.freq @@ -291,6 +297,32 @@ def test_SDL_GetAudioDeviceSpec(with_sdl_audio): print(msg.format(outname, drivername)) print(msg2.format(hz, chans, fmt, bufsize)) +@pytest.mark.skipif(sdl2.dll.version < 2240, reason="not available") +def test_SDL_GetDefaultAudioInfo(with_default_driver): + driver = with_default_driver + drivername = driver.decode('utf-8') + # Get name and spec of first output device + outspec = sdl2.SDL_AudioSpec(0, 0, 0, 0) + outname = ctypes.c_char_p() + ret = sdl2.SDL_GetDefaultAudioInfo(ctypes.byref(outname), ctypes.byref(outspec), 0) + # If method isn't implemented for the current back end, just skip + if ret < 0 and b"not supported" in sdl2.SDL_GetError(): + pytest.skip("not supported by driver") + assert ret == 0 + # Validate frequency and channel count were set + hz = outspec.freq + fmt = FORMAT_NAME_MAP[outspec.format] if outspec.format > 0 else 'unknown' + chans = outspec.channels + bufsize = outspec.samples if outspec.samples > 0 else 'unknown' + assert hz > 0 + assert chans > 0 + # Print out device spec info + outname = outname.value.decode('utf-8') + msg = "Default audio spec for {0} with '{1}' driver:" + msg2 = "{0} Hz, {1} channels, {2} format, {3} sample buffer size" + print(msg.format(outname, drivername)) + print(msg2.format(hz, chans, fmt, bufsize)) + def test_SDL_OpenCloseAudioDevice(with_sdl_audio): #TODO: Add tests for callback fmt = sdl2.AUDIO_F32 if sys.platform == "darwin" else sdl2.AUDIO_U16 diff --git a/sdl2/test/clipboard_test.py b/sdl2/test/clipboard_test.py index fbb6b4d..eef05f6 100644 --- a/sdl2/test/clipboard_test.py +++ b/sdl2/test/clipboard_test.py @@ -8,8 +8,10 @@ def window(with_sdl): flag = sdl2.SDL_WINDOW_BORDERLESS w = sdl2.SDL_CreateWindow(b"Test", 10, 40, 12, 13, flag) - assert sdl2.SDL_GetError() == b"" - assert isinstance(w.contents, sdl2.SDL_Window) + if not isinstance(w.contents, sdl2.SDL_Window): + assert sdl2.SDL_GetError() == b"" + assert isinstance(w.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() yield w sdl2.SDL_DestroyWindow(w) diff --git a/sdl2/test/keyboard_test.py b/sdl2/test/keyboard_test.py index 2e12c6c..71c72cd 100644 --- a/sdl2/test/keyboard_test.py +++ b/sdl2/test/keyboard_test.py @@ -11,8 +11,10 @@ def window(with_sdl): flag = video.SDL_WINDOW_INPUT_FOCUS w = video.SDL_CreateWindow(b"Test", 10, 40, 32, 24, flag) - assert SDL_GetError() == b"" - assert isinstance(w.contents, video.SDL_Window) + if not isinstance(w.contents, sdl2.SDL_Window): + assert sdl2.SDL_GetError() == b"" + assert isinstance(w.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() yield w video.SDL_DestroyWindow(w) @@ -56,6 +58,11 @@ def test_SDL_GetKeyboardState(with_sdl): for key in keystates[:numkeys.value]: assert key in [0, 1] +@pytest.mark.skipif(sdl2.dll.version < 2240, reason="not available") +def test_SDL_ResetKeyboard(with_sdl): + # Not entirely sure how to test this without user interaction + sdl2.SDL_ResetKeyboard() + def test_SDL_GetSetModState(with_sdl): test_states = [ keycode.KMOD_NUM | keycode.KMOD_CAPS | keycode.KMOD_MODE, diff --git a/sdl2/test/render_test.py b/sdl2/test/render_test.py index febebdd..2c3312b 100644 --- a/sdl2/test/render_test.py +++ b/sdl2/test/render_test.py @@ -21,8 +21,10 @@ def _create_window(pos, size, flags=video.SDL_WINDOW_HIDDEN): window = video.SDL_CreateWindow( b"Test", pos[0], pos[1], size[0], size[1], video.SDL_WINDOW_HIDDEN ) - assert SDL_GetError() == b"" - assert isinstance(window.contents, video.SDL_Window) + if not isinstance(window.contents, video.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(window.contents, video.SDL_Window) + sdl2.SDL_ClearError() return window def _get_renderflags(): @@ -31,6 +33,25 @@ def _get_renderflags(): flags = sdl2.SDL_RENDERER_SOFTWARE return flags +@pytest.fixture(scope="module") +def supported_renderers(with_sdl): + supported = [] + flags = _get_renderflags() + for i in range(sdl2.SDL_GetNumRenderDrivers()): + window = _create_window((30, 30), (100, 100)) + try: + renderer = sdl2.SDL_CreateRenderer(window, i, flags) + except OSError: + renderer = None + if (renderer and renderer.contents): + supported.append(i) + sdl2.SDL_DestroyRenderer(renderer) + video.SDL_DestroyWindow(window) + if not len(supported): + raise RuntimeError("Unable to create a renderer with any backend") + sdl2.SDL_ClearError() + yield supported + @pytest.fixture def testsurf(with_sdl): # Create a solid black surface for tests @@ -60,7 +81,10 @@ def with_renderer(with_sdl): window = video.SDL_CreateWindow( b"Test", 30, 30, 100, 100, video.SDL_WINDOW_HIDDEN ) - assert SDL_GetError() == b"" + if not isinstance(window.contents, sdl2.SDL_Window): + assert sdl2.SDL_GetError() == b"" + assert isinstance(window.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() renderer = sdl2.SDL_CreateRenderer(window, -1, flags) assert SDL_GetError() == b"" yield (renderer, window) @@ -186,16 +210,20 @@ def test_SDL_CreateWindowAndRenderer(with_sdl): ) sdl2.SDL_DestroyRenderer(renderer) video.SDL_DestroyWindow(window) - assert SDL_GetError() == b"" - assert ret == 0 + if ret != 0: + assert SDL_GetError() == b"" + assert ret == 0 -def test_SDL_CreateDestroyRenderer(with_sdl): +def test_SDL_CreateDestroyRenderer(supported_renderers): flags = _get_renderflags() errs = {} rcount = sdl2.SDL_GetNumRenderDrivers() for i in range(rcount): window = _create_window((30, 30), (100, 100)) - renderer = sdl2.SDL_CreateRenderer(window, i, flags) + try: + renderer = sdl2.SDL_CreateRenderer(window, i, flags) + except OSError: + renderer = None if (renderer and renderer.contents): assert isinstance(renderer.contents, sdl2.SDL_Renderer) sdl2.SDL_DestroyRenderer(renderer) @@ -215,11 +243,10 @@ def test_SDL_CreateSoftwareRenderer(with_sdl): sdl2.SDL_DestroyRenderer(renderer) surface.SDL_FreeSurface(sf) -def test_SDL_GetRenderer(with_sdl): +def test_SDL_GetRenderer(supported_renderers): flags = _get_renderflags() usable = 0 - rcount = sdl2.SDL_GetNumRenderDrivers() - for i in range(rcount): + for i in supported_renderers: window = _create_window((30, 30), (100, 100)) renderer = sdl2.SDL_CreateRenderer(window, i, flags) if (renderer and renderer.contents): @@ -233,10 +260,9 @@ def test_SDL_GetRenderer(with_sdl): assert usable > 0 @pytest.mark.skipif(sdl2.dll.version < 2022, reason="not available") -def test_SDL_RenderGetWindow(with_sdl): +def test_SDL_RenderGetWindow(supported_renderers): flags = _get_renderflags() - rcount = sdl2.SDL_GetNumRenderDrivers() - for i in range(rcount): + for i in supported_renderers: window = _create_window((30, 30), (100, 100)) renderer = sdl2.SDL_CreateRenderer(window, i, flags) if (renderer and renderer.contents): @@ -258,7 +284,10 @@ def test_SDL_GetRendererInfo(with_sdl): for i in range(rcount): sdl2.SDL_ClearError() window = _create_window((30, 30), (100, 100)) - renderer = sdl2.SDL_CreateRenderer(window, i, flags) + try: + renderer = sdl2.SDL_CreateRenderer(window, i, flags) + except OSError: + renderer = None if not (renderer and renderer.contents): err = stringify(sdl2.SDL_GetError()) errs.append("Unable to create renderer {0}: {1}".format(i, err)) @@ -463,11 +492,10 @@ def test_SDL_LockUnlockTexture(texture): def test_SDL_LockTextureToSurface(texture): pass -def test_SDL_RenderTargetSupported(with_sdl): +def test_SDL_RenderTargetSupported(supported_renderers): flags = _get_renderflags() usable = 0 - rcount = sdl2.SDL_GetNumRenderDrivers() - for i in range(rcount): + for i in supported_renderers: window = _create_window((30, 30), (100, 100)) renderer = sdl2.SDL_CreateRenderer(window, i, flags) if (renderer and renderer.contents): @@ -480,13 +508,12 @@ def test_SDL_RenderTargetSupported(with_sdl): video.SDL_DestroyWindow(window) assert usable > 0 -def test_SDL_GetSetRenderTarget(with_sdl): +def test_SDL_GetSetRenderTarget(supported_renderers): # First, determine which renderers support render targets flags = _get_renderflags() usable = 0 supports_targets = [] - rcount = sdl2.SDL_GetNumRenderDrivers() - for i in range(rcount): + for i in supported_renderers: window = _create_window((30, 30), (100, 100)) renderer = sdl2.SDL_CreateRenderer(window, i, flags) if (renderer and renderer.contents): diff --git a/sdl2/test/syswm_test.py b/sdl2/test/syswm_test.py index 0dcad39..f41427f 100644 --- a/sdl2/test/syswm_test.py +++ b/sdl2/test/syswm_test.py @@ -26,12 +26,17 @@ def test_SDL_GetWindowWMInfo(with_sdl): window = video.SDL_CreateWindow( b"Test", 10, 10, 10, 10, video.SDL_WINDOW_HIDDEN ) + if not isinstance(window.contents, video.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(window.contents, video.SDL_Window) + sdl2.SDL_ClearError() wminfo = sdl2.SDL_SysWMinfo() version.SDL_VERSION(wminfo.version) ret = sdl2.SDL_GetWindowWMInfo(window, ctypes.byref(wminfo)) video.SDL_DestroyWindow(window) - assert SDL_GetError() == b"" - assert ret == SDL_TRUE + if not ret == SDL_TRUE: + assert SDL_GetError() == b"" + assert ret == SDL_TRUE # Test window manager types for different platforms platform = sys.platform if platform in ("win32", "cygwin", "msys"): diff --git a/sdl2/test/video_test.py b/sdl2/test/video_test.py index 2b6b6e8..a60c668 100644 --- a/sdl2/test/video_test.py +++ b/sdl2/test/video_test.py @@ -53,8 +53,10 @@ def with_sdl_gl(with_sdl): def window(with_sdl): flag = sdl2.SDL_WINDOW_BORDERLESS w = sdl2.SDL_CreateWindow(b"Test", 10, 40, 12, 13, flag) - assert SDL_GetError() == b"" - assert isinstance(w.contents, sdl2.SDL_Window) + if not isinstance(w.contents, sdl2.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(w.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() yield w sdl2.SDL_DestroyWindow(w) @@ -62,8 +64,10 @@ def window(with_sdl): def decorated_window(with_sdl): flag = sdl2.SDL_WINDOW_RESIZABLE w = sdl2.SDL_CreateWindow(b"Test", 10, 40, 12, 13, flag) - assert SDL_GetError() == b"" - assert isinstance(w.contents, sdl2.SDL_Window) + if not isinstance(w.contents, sdl2.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(w.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() yield w sdl2.SDL_DestroyWindow(w) @@ -71,13 +75,23 @@ def decorated_window(with_sdl): def gl_window(with_sdl_gl): flag = sdl2.SDL_WINDOW_OPENGL w = sdl2.SDL_CreateWindow(b"OpenGL", 10, 40, 12, 13, flag) - assert SDL_GetError() == b"" + if not isinstance(w.contents, sdl2.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(w.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() ctx = sdl2.SDL_GL_CreateContext(w) assert SDL_GetError() == b"" yield (w, ctx) sdl2.SDL_GL_DeleteContext(ctx) sdl2.SDL_DestroyWindow(w) +def _create_window(name, h, w, x, y, flags): + window = sdl2.SDL_CreateWindow(name, h, w, x, y, flags) + if not isinstance(window.contents, sdl2.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(window.contents, sdl2.SDL_Window) + sdl2.SDL_ClearError() + return window # Test custom macros @@ -334,8 +348,9 @@ def test_GetDisplayInfo(with_sdl): def test_SDL_CreateDestroyWindow(with_sdl): flag = sdl2.SDL_WINDOW_BORDERLESS window = sdl2.SDL_CreateWindow(b"Test", 10, 40, 12, 13, flag) - assert SDL_GetError() == b"" - assert isinstance(window.contents, sdl2.SDL_Window) + if not isinstance(window.contents, sdl2.SDL_Window): + assert SDL_GetError() == b"" + assert isinstance(window.contents, sdl2.SDL_Window) sdl2.SDL_DestroyWindow(window) @pytest.mark.skip("not implemented") @@ -343,6 +358,24 @@ def test_SDL_CreateWindowFrom(with_sdl): # No obvious cross-platform way to test this pass +@pytest.mark.skipif(sdl2.dll.version < 2240, reason="not available") +def test_SDL_GetPointDisplayIndex(with_sdl): + for index in range(sdl2.SDL_GetNumVideoDisplays()): + bounds = rect.SDL_Rect() + ret = sdl2.SDL_GetDisplayUsableBounds(index, byref(bounds)) + assert ret == 0 + p = sdl2.SDL_Point(bounds.x + 50, bounds.y + 50) + assert sdl2.SDL_GetPointDisplayIndex(p) == index + +@pytest.mark.skipif(sdl2.dll.version < 2240, reason="not available") +def test_SDL_GetRectDisplayIndex(with_sdl): + for index in range(sdl2.SDL_GetNumVideoDisplays()): + bounds = rect.SDL_Rect() + ret = sdl2.SDL_GetDisplayUsableBounds(index, byref(bounds)) + assert ret == 0 + r = sdl2.SDL_Rect(bounds.x + 50, bounds.y + 50, 10, 10) + assert sdl2.SDL_GetRectDisplayIndex(r) == index + def test_SDL_GetWindowDisplayIndex(window): numdisplays = sdl2.SDL_GetNumVideoDisplays() dindex = sdl2.SDL_GetWindowDisplayIndex(window) @@ -353,8 +386,9 @@ def test_SDL_GetWindowDisplayMode(window): # NOTE: Gets fullscreen mode of parent display, not size of window dmode = sdl2.SDL_DisplayMode() ret = sdl2.SDL_GetWindowDisplayMode(window, byref(dmode)) - assert SDL_GetError() == b"" - assert ret == 0 + if ret != 0: + assert SDL_GetError() == b"" + assert ret == 0 assert dmode.w > 0 assert dmode.h > 0 @@ -368,8 +402,9 @@ def test_SDL_SetWindowDisplayMode(window): sdl2.SDL_SetWindowDisplayMode(window, dmode) wmode = sdl2.SDL_DisplayMode() ret = sdl2.SDL_GetWindowDisplayMode(window, byref(wmode)) - assert SDL_GetError() == b"" - assert ret == 0 + if ret != 0: + assert SDL_GetError() == b"" + assert ret == 0 assert dmode == wmode @pytest.mark.skipif(sdl2.dll.version < 2018, reason="not available") @@ -442,7 +477,7 @@ def test_SDL_GetSetWindowData(window): @pytest.mark.xfail(DRIVER_X11, reason="Wonky with some window managers") def test_SDL_GetSetWindowPosition(with_sdl): - window = sdl2.SDL_CreateWindow(b"Test", 10, 200, 10, 10, 0) + window = _create_window(b"Test", 10, 200, 10, 10, 0) px, py = c_int(0), c_int(0) sdl2.SDL_GetWindowPosition(window, byref(px), byref(py)) assert (px.value, py.value) == (10, 200) @@ -682,7 +717,7 @@ def test_SDL_GetGrabbedWindow(window): def test_SDL_GetSetWindowMouseRect(with_sdl): flags = sdl2.SDL_WINDOW_BORDERLESS bounds_in = rect.SDL_Rect(0, 0, 100, 50) - window = sdl2.SDL_CreateWindow(b"Test", 200, 200, 200, 200, flags) + window = _create_window(b"Test", 200, 200, 200, 200, flags) # Try setting a mouse boundary ret = sdl2.SDL_SetWindowMouseRect(window, byref(bounds_in)) assert SDL_GetError() == b"" @@ -812,7 +847,7 @@ def test_SDL_GL_LoadUnloadLibrary(with_sdl): @pytest.mark.skipif(DRIVER_DUMMY, reason="Doesn't work with dummy driver") def test_SDL_GL_CreateDeleteContext(with_sdl_gl): - window = sdl2.SDL_CreateWindow( + window = _create_window( b"OpenGL", 10, 40, 32, 24, sdl2.SDL_WINDOW_OPENGL ) ctx = sdl2.SDL_GL_CreateContext(window) @@ -837,7 +872,7 @@ def test_SDL_GL_ExtensionSupported(gl_window): @pytest.mark.skipif(DRIVER_DUMMY, reason="Doesn't work with dummy driver") def test_SDL_GL_GetSetResetAttribute(with_sdl_gl): # Create a context and get its bit depth - window = sdl2.SDL_CreateWindow( + window = _create_window( b"OpenGL", 10, 40, 12, 13, sdl2.SDL_WINDOW_OPENGL ) ctx = sdl2.SDL_GL_CreateContext(window) @@ -845,15 +880,19 @@ def test_SDL_GL_GetSetResetAttribute(with_sdl_gl): ret = sdl2.SDL_GL_GetAttribute(sdl2.SDL_GL_DOUBLEBUFFER, byref(bufstate)) sdl2.SDL_GL_DeleteContext(ctx) sdl2.SDL_DestroyWindow(window) - assert SDL_GetError() == b"" - assert ret == 0 + if ret != 0: + assert SDL_GetError() == b"" + assert ret == 0 + sdl2.SDL_ClearError() # Try setting a different GL bit depth new_bufstate = 0 if bufstate.value == 1 else 1 sdl2.SDL_GL_SetAttribute(sdl2.SDL_GL_DOUBLEBUFFER, new_bufstate) - assert SDL_GetError() == b"" - assert ret == 0 + if ret != 0: + assert SDL_GetError() == b"" + assert ret == 0 + sdl2.SDL_ClearError() # Create a new context to see if it's using the new bit depth - window = sdl2.SDL_CreateWindow( + window = _create_window( b"OpenGL", 10, 40, 12, 13, sdl2.SDL_WINDOW_OPENGL ) ctx = sdl2.SDL_GL_CreateContext(window) @@ -861,13 +900,15 @@ def test_SDL_GL_GetSetResetAttribute(with_sdl_gl): ret = sdl2.SDL_GL_GetAttribute(sdl2.SDL_GL_DOUBLEBUFFER, byref(val)) sdl2.SDL_GL_DeleteContext(ctx) sdl2.SDL_DestroyWindow(window) - assert SDL_GetError() == b"" - assert ret == 0 + if ret != 0: + assert SDL_GetError() == b"" + assert ret == 0 + sdl2.SDL_ClearError() assert bufstate.value != val.value assert val.value == new_bufstate # Try resetting the context and see if it goes back to the original depth sdl2.SDL_GL_ResetAttributes() - window = sdl2.SDL_CreateWindow( + window = _create_window( b"OpenGL", 10, 40, 12, 13, sdl2.SDL_WINDOW_OPENGL ) ctx = sdl2.SDL_GL_CreateContext(window) diff --git a/sdl2/version.py b/sdl2/version.py index 18d7672..8875eb4 100644 --- a/sdl2/version.py +++ b/sdl2/version.py @@ -19,8 +19,8 @@ # Constants, enums, & macros SDL_MAJOR_VERSION = 2 -SDL_MINOR_VERSION = 23 -SDL_PATCHLEVEL = 1 +SDL_MINOR_VERSION = 24 +SDL_PATCHLEVEL = 0 def SDL_VERSION(x): x.major = SDL_MAJOR_VERSION diff --git a/sdl2/video.py b/sdl2/video.py index a84ed09..48e1ed6 100644 --- a/sdl2/video.py +++ b/sdl2/video.py @@ -65,6 +65,7 @@ "SDL_GL_CONTEXT_PROFILE_MASK", "SDL_GL_SHARE_WITH_CURRENT_CONTEXT", "SDL_GL_FRAMEBUFFER_SRGB_CAPABLE", "SDL_GL_CONTEXT_RELEASE_BEHAVIOR", "SDL_GL_CONTEXT_RESET_NOTIFICATION", "SDL_GL_CONTEXT_NO_ERROR", + "SDL_GL_FLOATBUFFERS", "SDL_GLprofile", "SDL_GL_CONTEXT_PROFILE_CORE", @@ -198,6 +199,7 @@ SDL_GL_CONTEXT_RELEASE_BEHAVIOR = 24 SDL_GL_CONTEXT_RESET_NOTIFICATION = 25 SDL_GL_CONTEXT_NO_ERROR = 26 +SDL_GL_FLOATBUFFERS = 27 SDL_GLprofile = c_int SDL_GL_CONTEXT_PROFILE_CORE = 0x0001 @@ -306,6 +308,8 @@ def __ne__(self, mode): [c_int, _P(SDL_DisplayMode), _P(SDL_DisplayMode)], returns = _P(SDL_DisplayMode) ), + SDLFunc("SDL_GetPointDisplayIndex", [_P(SDL_Point)], c_int, added='2.24.0'), + SDLFunc("SDL_GetRectDisplayIndex", [_P(SDL_Rect)], c_int, added='2.24.0'), SDLFunc("SDL_GetWindowDisplayIndex", [_P(SDL_Window)], c_int), SDLFunc("SDL_SetWindowDisplayMode", [_P(SDL_Window), _P(SDL_DisplayMode)], c_int), SDLFunc("SDL_GetWindowDisplayMode", [_P(SDL_Window), _P(SDL_DisplayMode)], c_int), @@ -408,6 +412,8 @@ def __ne__(self, mode): SDL_GetDesktopDisplayMode = _ctypes["SDL_GetDesktopDisplayMode"] SDL_GetCurrentDisplayMode = _ctypes["SDL_GetCurrentDisplayMode"] SDL_GetClosestDisplayMode = _ctypes["SDL_GetClosestDisplayMode"] +SDL_GetPointDisplayIndex = _ctypes["SDL_GetPointDisplayIndex"] +SDL_GetRectDisplayIndex = _ctypes["SDL_GetRectDisplayIndex"] SDL_GetWindowDisplayIndex = _ctypes["SDL_GetWindowDisplayIndex"] SDL_SetWindowDisplayMode = _ctypes["SDL_SetWindowDisplayMode"] SDL_GetWindowDisplayMode = _ctypes["SDL_GetWindowDisplayMode"]