all repos — mgba @ dce49ea99022187e3798e484580d9a4624ed02bd

mGBA Game Boy Advance Emulator

Python: Add runner, thread interfaces
Vicki Pfau vi@endrift.com
Thu, 22 Jun 2017 22:07:08 -0700
commit

dce49ea99022187e3798e484580d9a4624ed02bd

parent

e1325b03735fb0fcec8539485cc42a6322c05403

M src/platform/python/_builder.hsrc/platform/python/_builder.h

@@ -1,5 +1,6 @@

#define COMMON_H #define PNG_H +#define OPAQUE_THREADING #define _SYS_TIME_H #define _SYS_TIME_H_ #define _TIME_H

@@ -30,6 +31,7 @@

#include <mgba/core/core.h> #include <mgba/core/mem-search.h> #include <mgba/core/tile-cache.h> +#include <mgba/core/thread.h> #include <mgba/core/version.h> #define PYEXPORT extern "Python+C"
M src/platform/python/_builder.pysrc/platform/python/_builder.py

@@ -18,10 +18,12 @@ cppflags.extend(["-I" + incdir, "-I" + srcdir, "-I" + bindir])

ffi.set_source("mgba._pylib", """ #include "flags.h" +#define OPAQUE_THREADING #include <mgba-util/common.h> #include <mgba/core/core.h> #include <mgba/core/log.h> #include <mgba/core/mem-search.h> +#include <mgba/core/thread.h> #include <mgba/core/tile-cache.h> #include <mgba/core/version.h> #include <mgba/internal/arm/arm.h>
M src/platform/python/mgba/core.pysrc/platform/python/mgba/core.py

@@ -38,10 +38,18 @@ raise RuntimeError("Core must be reset first")

return f(self, *args, **kwargs) return wrapper +def protected(f): + def wrapper(self, *args, **kwargs): + if self._protected: + raise RuntimeError("Core is protected") + return f(self, *args, **kwargs) + return wrapper + class Core(object): def __init__(self, native): self._core = native self._wasReset = False + self._protected = False @cached_property def tiles(self):

@@ -51,6 +59,7 @@ @classmethod

def _init(cls, native): core = ffi.gc(native, native.deinit) success = bool(core.init(core)) + lib.mCoreInitConfig(core, ffi.NULL) if not success: raise RuntimeError("Failed to initialize core") if hasattr(cls, 'PLATFORM_GBA') and core.platform(core) == cls.PLATFORM_GBA:

@@ -62,6 +71,7 @@

def _deinit(self): self._core.deinit(self._core) + @protected def loadFile(self, path): return bool(lib.mCoreLoadFile(self._core, path.encode('UTF-8')))

@@ -77,6 +87,7 @@

def loadTemporarySave(self, vf): return bool(self._core.loadTemporarySave(self._core, vf.handle)) + @protected def loadPatch(self, vf): return bool(self._core.loadPatch(self._core, vf.handle))

@@ -98,19 +109,23 @@

def setVideoBuffer(self, image): self._core.setVideoBuffer(self._core, image.buffer, image.stride) + @protected def reset(self): self._core.reset(self._core) self._wasReset = True @needsReset + @protected def runFrame(self): self._core.runFrame(self._core) @needsReset + @protected def runLoop(self): self._core.runLoop(self._core) @needsReset + @protected def step(self): self._core.step(self._core)

@@ -151,6 +166,38 @@ def getGameCode(self):

code = ffi.new("char[12]") self._core.getGameCode(self._core, code) return ffi.string(code, 12).decode("ascii") + +class ICoreOwner(object): + def claim(self): + raise NotImplementedError + + def release(self): + raise NotImplementedError + + def __enter__(self): + self.core = self.claim() + self.core._protected = True + return self.core + + def __exit__(self, type, value, traceback): + self.core._protected = False + self.release() + +class IRunner(object): + def pause(self): + raise NotImplementedError + + def unpause(self): + raise NotImplementedError + + def useCore(self): + raise NotImplementedError + + def isRunning(self): + raise NotImplementedError + + def isPaused(self): + raise NotImplementedError if hasattr(lib, 'PLATFORM_GBA'): from .gba import GBA
A src/platform/python/mgba/thread.py

@@ -0,0 +1,58 @@

+# Copyright (c) 2013-2017 Jeffrey Pfau +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +from ._pylib import ffi, lib +from .core import IRunner, ICoreOwner, Core + +class ThreadCoreOwner(ICoreOwner): + def __init__(self, thread): + self.thread = thread + + def claim(self): + if not self.thread.isRunning(): + raise ValueError + lib.mCoreThreadInterrupt(self.thread._native) + return self.thread._core + + def release(self): + lib.mCoreThreadContinue(self.thread._native) + +class Thread(IRunner): + def __init__(self, native=None): + if native: + self._native = native + self._core = Core(native.core) + self._core._wasReset = lib.mCoreThreadHasStarted(self._native) + else: + self._native = ffi.new("struct mCoreThread*") + + def start(self, core): + if lib.mCoreThreadHasStarted(self._native): + raise ValueError + self._core = core + self._native.core = core._core + lib.mCoreThreadStart(self._native) + self._core._wasReset = lib.mCoreThreadHasStarted(self._native) + + def end(self): + if not lib.mCoreThreadHasStarted(self._native): + raise ValueError + lib.mCoreThreadEnd(self._native) + lib.mCoreThreadJoin(self._native) + + def pause(self): + lib.mCoreThreadPause(self._native) + + def unpause(self): + lib.mCoreThreadUnpause(self._native) + + def isRunning(self): + return bool(lib.mCoreThreadIsActive(self._native)) + + def isPaused(self): + return bool(lib.mCoreThreadIsPaused(self._native)) + + def useCore(self): + return ThreadCoreOwner(self)