Skip to content

Commit

Permalink
Merge branch 'pysdl'
Browse files Browse the repository at this point in the history
  • Loading branch information
AstraLuma committed Mar 21, 2020
2 parents 71b8acc + 2a9ab20 commit 78df9c6
Show file tree
Hide file tree
Showing 17 changed files with 752 additions and 315 deletions.
18 changes: 15 additions & 3 deletions .cirrus.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# !!NOTE!!
# If any of the image names or python versions change, update bors.toml

pep517_task:
skip: $CIRRUS_BRANCH =~ '.*\.tmp'
container:
Expand Down Expand Up @@ -48,7 +51,7 @@ task:
matrix:
- IMAGE: python:3.6-slim
- IMAGE: python:3.7-slim
# - IMAGE: python:3.8-slim
- IMAGE: python:3.8-slim
- IMAGE: pypy:3.6-slim

container:
Expand All @@ -70,6 +73,9 @@ task:
else
python .ci/install-wheels.py
fi
- >-
apt-get update || true;
apt-get install -qq -y libsdl2-2.0-0 libsdl2-mixer-2.0-0 libsdl2-image-2.0-0 libsdl2-gfx-1.0-0
script:
- command -v pypy3 >/dev/null && export PY=pypy3
Expand All @@ -85,8 +91,13 @@ macOS_task:
env:
PATH: ${HOME}/.pyenv/shims:${PATH}
matrix:
- PYTHON: 3.6.8
- PYTHON: 3.7.2
# To update:
# Get the current version of pyenv from https://formulae.brew.sh/formula/pyenv
# Look that up on https://github.com/pyenv/pyenv/tree/master/plugins/python-build/share/python-build
# Also update bors.toml
- PYTHON: 3.6.9
- PYTHON: 3.7.5
- PYTHON: 3.8.1
depends_on:
- build
install_script:
Expand Down Expand Up @@ -118,6 +129,7 @@ task:
matrix:
- IMAGE: python:3.6-windowsservercore
- IMAGE: python:3.7-windowsservercore
- IMAGE: python:3.8-windowsservercore

install_script:
- C:\Python\python.exe -m pip install --upgrade-strategy eager -U -r requirements-tests.txt
Expand Down
7 changes: 5 additions & 2 deletions bors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ status = [
"docs",
"Linux python:3.6-slim",
"Linux python:3.7-slim",
"Linux python:3.8-slim",
"Windows python:3.6-windowsservercore",
"Windows python:3.7-windowsservercore",
"macOS PYTHON:3.6.8",
"macOS PYTHON:3.7.2",
"Windows python:3.8-windowsservercore",
"macOS PYTHON:3.6.9",
"macOS PYTHON:3.7.5",
"macOS PYTHON:3.8.1",
"pep517",
]

Expand Down
13 changes: 13 additions & 0 deletions ppb/assetlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class Asset(AbstractAsset):
Meant to be subclassed, but in specific ways.
"""
_data = None

def __new__(cls, name):
clsname = f"{cls.__module__}:{cls.__qualname__}"
try:
Expand Down Expand Up @@ -101,6 +103,17 @@ def background_parse(self, data: bytes):
"""
return data

def free(self, object):
"""
Called by __del__, if necessary. Meant to free the loaded data.
"""

def __del__(self):
# This should only be called after the background threads and other
# processing has finished.
if self._data is not None:
self.free(self._data)

def is_loaded(self):
"""
Returns if the data has been loaded and parsed.
Expand Down
95 changes: 70 additions & 25 deletions ppb/assets.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,79 @@
from typing import Tuple
import sdl2.ext
from sdl2 import (
SDL_Point, # https://wiki.libsdl.org/SDL_Point
SDL_CreateRGBSurface, # https://wiki.libsdl.org/SDL_CreateRGBSurface
SDL_FreeSurface, # https://wiki.libsdl.org/SDL_FreeSurface
SDL_SetColorKey, # https://wiki.libsdl.org/SDL_SetColorKey
SDL_CreateSoftwareRenderer, # https://wiki.libsdl.org/SDL_CreateSoftwareRenderer
SDL_DestroyRenderer, # https://wiki.libsdl.org/SDL_DestroyRenderer
SDL_SetRenderDrawColor, # https://wiki.libsdl.org/SDL_SetRenderDrawColor
SDL_RenderFillRect, # https://wiki.libsdl.org/SDL_RenderFillRect
)

from pygame import Surface
from pygame import draw
from sdl2.sdlgfx import (
filledTrigonRGBA, # https://www.ferzkopp.net/Software/SDL2_gfx/Docs/html/_s_d_l2__gfx_primitives_8h.html#a273cf4a88abf6c6a5e019b2c58ee2423
filledCircleRGBA, # https://www.ferzkopp.net/Software/SDL2_gfx/Docs/html/_s_d_l2__gfx_primitives_8h.html#a666bd764e2fe962656e5829d0aad5ba6
)

from ppb.assetlib import AbstractAsset
from ppb.systems._sdl_utils import sdl_call

__all__ = (
"Square",
"Triangle"
"Triangle",
"Circle",
)

BLACK = 0, 0, 0
MAGENTA = 255, 71, 182
DEFAULT_SPRITE_SIZE = 64
DEFAULT_SPRITE_RESOLUTION = DEFAULT_SPRITE_SIZE, DEFAULT_SPRITE_SIZE


def _create_surface(color):
"""
Creates a surface for assets and sets the color key.
"""
surface = Surface(DEFAULT_SPRITE_RESOLUTION)
surface = sdl_call(
SDL_CreateRGBSurface, 0, DEFAULT_SPRITE_SIZE, DEFAULT_SPRITE_SIZE, 32, 0, 0, 0, 0,
_check_error=lambda rv: not rv
)
color_key = BLACK if color != BLACK else MAGENTA
surface.set_colorkey(color_key)
surface.fill(color_key)
color = sdl2.ext.Color(*color_key)
sdl_call(
SDL_SetColorKey, surface, True, sdl2.ext.prepare_color(color, surface.contents),
_check_error=lambda rv: rv < 0
)
sdl2.ext.fill(surface.contents, color)
return surface


class Shape(AbstractAsset):
"""Shapes are drawing primitives that are good for rapid prototyping."""

_surface = None
def __init__(self, red: int, green: int, blue: int):
color = red, green, blue
self._surface = _create_surface(color)
self.modify_surface(color)

def load(self) -> Surface:
renderer = sdl_call(
SDL_CreateSoftwareRenderer, self._surface,
_check_error=lambda rv: not rv
)
try:
self._draw_shape(renderer, rgb=color)
finally:
sdl_call(SDL_DestroyRenderer, renderer)

def load(self):
"""Return the underlying asset."""
return self._surface

def modify_surface(self, color: Tuple[int, int, int]) -> None:
def __del__(self, _SDL_FreeSurface=SDL_FreeSurface):
if self._surface:
SDL_FreeSurface(self._surface)

def _draw_shape(self, renderer, **_) -> None:
"""
Modify the raw asset to match the intended shape.
Must modify in place.
"""


Expand All @@ -52,29 +82,44 @@ class Square(Shape):
A square image of a single color.
"""

def modify_surface(self, color):
self._surface.fill(color)
def _draw_shape(self, renderer, rgb, **_):
sdl_call(
SDL_SetRenderDrawColor, renderer, *rgb, 255,
_check_error=lambda rv: rv < 0
)
sdl_call(
SDL_RenderFillRect, renderer, None,
_check_error=lambda rv: rv < 0
)


class Triangle(Shape):
"""
A triangle image of a single color.
"""

def modify_surface(self, color):
draw.polygon(self._surface, color,
[
(0, DEFAULT_SPRITE_SIZE),
(DEFAULT_SPRITE_SIZE / 2, 0),
(DEFAULT_SPRITE_SIZE, DEFAULT_SPRITE_SIZE)
])
def _draw_shape(self, renderer, rgb, **_):
sdl_call(
filledTrigonRGBA, renderer,
0, DEFAULT_SPRITE_SIZE,
int(DEFAULT_SPRITE_SIZE / 2), 0,
DEFAULT_SPRITE_SIZE, DEFAULT_SPRITE_SIZE,
*rgb, 255,
_check_error=lambda rv: rv < 0
)


class Circle(Shape):
"""
A circle image of a single color.
"""

def modify_surface(self, color):
def _draw_shape(self, renderer, rgb, **_):
half = int(DEFAULT_SPRITE_SIZE / 2)
draw.circle(self._surface, color, (half, half), half)
sdl_call(
filledCircleRGBA, renderer,
half, half, # Center
half, # Radius
*rgb, 255,
_check_error=lambda rv: rv < 0
)
2 changes: 1 addition & 1 deletion ppb/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def activate(self, next_scene: dict):
return
args = next_scene.get("args", [])
kwargs = next_scene.get("kwargs", {})
self.scenes.append(scene(*args, **kwargs))
self.start_scene(scene(*args, **kwargs), None)

def signal(self, event):
"""
Expand Down
56 changes: 56 additions & 0 deletions ppb/systems/_sdl_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import atexit

from sdl2 import (
SDL_GetError, # https://wiki.libsdl.org/SDL_GetError
SDL_ClearError, # https://wiki.libsdl.org/SDL_ClearError
SDL_InitSubSystem, # https://wiki.libsdl.org/SDL_InitSubSystem
SDL_QuitSubSystem, # https://wiki.libsdl.org/SDL_QuitSubSystem
SDL_Quit, # https://wiki.libsdl.org/SDL_Quit
)

from ppb.systemslib import System


atexit.register(SDL_Quit)
# The PPB model makes it hard to register this in connection with the actual
# engine cleanup, so we'll do it on interpreter exit.


class SdlError(Exception):
"""
SDL raised an error
"""


def sdl_call(func, *pargs, _check_error=None, **kwargs):
"""
Wrapper for calling SDL functions for handling errors.
If _check_error is given, called with the return value to check for errors.
If _check_error returns truthy, an error occurred.
If _check_error is not given, it is assumed that a non-empty error from
Mix_GetError indicates error.
"""
SDL_ClearError()
rv = func(*pargs, **kwargs)
err = SDL_GetError()
if (_check_error(rv) if _check_error else err):
raise SdlError(f"Error calling {func.__name__}: {err.decode('utf-8')}")
else:
return rv


class SdlSubSystem(System):
"""
Handles SDL_InitSubSystem/SDL_QuitSubSystem
"""
_sdl_subsystems = 0

def __enter__(self):
super().__enter__()
sdl_call(SDL_InitSubSystem, self._sdl_subsystems, _check_error=lambda rv: rv < 0)

def __exit__(self, exc_type, exc_val, exc_tb):
sdl_call(SDL_QuitSubSystem, self._sdl_subsystems)
super().__exit__(exc_type, exc_val, exc_tb)
Loading

0 comments on commit 78df9c6

Please sign in to comment.