122 lines
4 KiB
Python
122 lines
4 KiB
Python
"""DLL wrapper"""
|
|
import os
|
|
import sys
|
|
import warnings
|
|
from ctypes import CDLL
|
|
from ctypes.util import find_library
|
|
|
|
__all__ = ["DLL", "nullfunc"]
|
|
|
|
|
|
def _findlib(libnames, path=None):
|
|
"""."""
|
|
platform = sys.platform
|
|
if platform in ("win32",):
|
|
pattern = "%s.dll"
|
|
elif platform == "darwin":
|
|
pattern = "lib%s.dylib"
|
|
else:
|
|
pattern = "lib%s.so"
|
|
searchfor = libnames
|
|
if type(libnames) is dict:
|
|
# different library names for the platforms
|
|
if platform not in libnames:
|
|
platform = "DEFAULT"
|
|
searchfor = libnames[platform]
|
|
results = []
|
|
if path:
|
|
for libname in searchfor:
|
|
for subpath in str.split(path, os.pathsep):
|
|
dllfile = os.path.join(subpath, pattern % libname)
|
|
if os.path.exists(dllfile):
|
|
results.append(dllfile)
|
|
for libname in searchfor:
|
|
dllfile = find_library(libname)
|
|
if dllfile:
|
|
results.append(dllfile)
|
|
return results
|
|
|
|
|
|
class DLLWarning(Warning):
|
|
pass
|
|
|
|
|
|
class DLL(object):
|
|
"""Function wrapper around the different DLL functions. Do not use or
|
|
instantiate this one directly from your user code.
|
|
"""
|
|
def __init__(self, libinfo, libnames, path=None):
|
|
self._dll = None
|
|
foundlibs = _findlib(libnames, path)
|
|
dllmsg = "PYSDL2_DLL_PATH: %s" % (os.getenv("PYSDL2_DLL_PATH") or "unset")
|
|
if len(foundlibs) == 0:
|
|
raise RuntimeError("could not find any library for %s (%s)" %
|
|
(libinfo, dllmsg))
|
|
for libfile in foundlibs:
|
|
try:
|
|
self._dll = CDLL(libfile)
|
|
self._libfile = libfile
|
|
break
|
|
except Exception as exc:
|
|
# Could not load the DLL, move to the next, but inform the user
|
|
# about something weird going on - this may become noisy, but
|
|
# is better than confusing the users with the RuntimeError below
|
|
warnings.warn(repr(exc), DLLWarning)
|
|
if self._dll is None:
|
|
raise RuntimeError("found %s, but it's not usable for the library %s" %
|
|
(foundlibs, libinfo))
|
|
if path is not None and sys.platform in ("win32",) and \
|
|
path in self._libfile:
|
|
os.environ["PATH"] = "%s;%s" % (path, os.environ["PATH"])
|
|
|
|
def bind_function(self, funcname, args=None, returns=None, optfunc=None):
|
|
"""Binds the passed argument and return value types to the specified
|
|
function."""
|
|
func = getattr(self._dll, funcname, None)
|
|
warnings.warn\
|
|
("function '%s' not found in %r, using replacement" %
|
|
(funcname, self._dll), ImportWarning)
|
|
if not func:
|
|
if optfunc:
|
|
warnings.warn\
|
|
("function '%s' not found in %r, using replacement" %
|
|
(funcname, self._dll), ImportWarning)
|
|
func = _nonexistent(funcname, optfunc)
|
|
else:
|
|
raise ValueError("could not find function '%s' in %r" %
|
|
(funcname, self._dll))
|
|
func.argtypes = args
|
|
func.restype = returns
|
|
return func
|
|
|
|
@property
|
|
def libfile(self):
|
|
"""Gets the filename of the loaded library."""
|
|
return self._libfile
|
|
|
|
|
|
def _nonexistent(funcname, func):
|
|
"""A simple wrapper to mark functions and methods as nonexistent."""
|
|
def wrapper(*fargs, **kw):
|
|
warnings.warn("%s does not exist" % funcname,
|
|
category=RuntimeWarning, stacklevel=2)
|
|
return func(*fargs, **kw)
|
|
wrapper.__name__ = func.__name__
|
|
return wrapper
|
|
|
|
|
|
def nullfunc(*args):
|
|
"""A simple no-op function to be used as dll replacement."""
|
|
return
|
|
|
|
try:
|
|
dll = DLL("SDL2", ["SDL2", "SDL2-2.0"], os.getenv("PYSDL2_DLL_PATH"))
|
|
except RuntimeError as exc:
|
|
raise ImportError(exc)
|
|
|
|
def get_dll_file():
|
|
"""Gets the file name of the loaded SDL2 library."""
|
|
return dll.libfile
|
|
|
|
_bind = dll.bind_function
|