Wednesday, August 11, 2010

Interfacing RPython with C

The documentation on how to interface RPython with C is rather limited, the extending doc mentions that `MixedModules` using rffi is the most advanced method available. The rffi document only provides a quick glance at how to use rffi.llexternal. The best place to get started is looking at the source code in rlib/rsdl/RSDL.py which binds RPython to SDL. What should be clear after reading RSDL.py is that PyPy provides a very direct and easy to understand interface for C; that should also provide the highest possible performance.

The source code in rffi_platform.py (found in pypy/rpython/tool) is very interesting, it generates dynamic C code that it compiles to get information about the C code we are trying to wrap, for example if you had used the wrong name in a struct it will generate an error. Using rffi_platform different C types can be defined, such as structs, constant integers; these are then put into a special container class called CConfig, which is then parsed by rffi_platform.configure(CConfig) and wrappers returned. After we have the wrappers we must tell any pointers we had previously used in the CConfig that they `become` those objects, MyPointer.TO.become(MyStruct).

RSDL that is included in PyPy is incomplete and lacks wrappers for Joystick, the code below wraps SDL Joystick. Full source code is available here.



eci = get_rsdl_compilation_info()
## wrapper for rffi.llexternal just to shorten the call
def external(name, args, result): return rffi.llexternal(name, args, result, compilation_info=eci)

JoystickPtr = lltype.Ptr(lltype.ForwardReference())
JoyAxisEventPtr = lltype.Ptr(lltype.ForwardReference())
JoyBallEventPtr = lltype.Ptr(lltype.ForwardReference())
JoyButtonEventPtr = lltype.Ptr(lltype.ForwardReference())
JoyHatEventPtr = lltype.Ptr(lltype.ForwardReference())


class CConfig:
_compilation_info_ = eci
Joystick = platform.Struct('SDL_JoyAxisEvent', []) # just and ID, struct contains nothing
# rsdl/constants.py already defines SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, etc..
JoyAxisEvent = platform.Struct('SDL_JoyAxisEvent',
[('type', rffi.INT),
('which', rffi.INT),
('axis', rffi.INT),
('value', rffi.INT)])


CONSTS = 'INIT_JOYSTICK QUERY ENABLE IGNORE PRESSED RELEASED'
for name in CONSTS.split():
name = name.strip()
if name:
ci = platform.ConstantInteger('SDL_%s' %name)
setattr( CConfig, name, ci )
globals().update(platform.configure(CConfig))

JoystickPtr.TO.become(Joystick)
JoyAxisEventPtr.TO.become(JoyAxisEvent)

JoystickUpdate = external('SDL_JoystickUpdate', [], lltype.Void)
NumJoysticks = external('SDL_NumJoysticks', [], rffi.INT)
## CCHARP seems to stand for C char pointer ##
JoystickName = external('SDL_JoystickName', [rffi.INT], rffi.CCHARP)
JoystickOpen = external('SDL_JoystickOpen', [rffi.INT], JoystickPtr)
JoystickOpened = external('SDL_JoystickOpened', [rffi.INT], rffi.INT)

JoystickEventState = external('SDL_JoystickEventState', [rffi.INT], rffi.INT)

def handle_event( etype, event ):
if etype == RSDL.JOYAXISMOTION:
p = rffi.cast( JoyAxisEventPtr, event )
axis = rffi.getintfield(p, 'c_axis')
value = rffi.getintfield(p, 'c_value')
print 'axis: %s value: %s' %(axis, value)



def poll(loops=1000):
event = lltype.malloc(RSDL.Event, flavor='raw')
try:
i = 1
while i < ok =" RSDL.PollEvent(event);" ok =" rffi.cast(lltype.Signed,">= 0
if ok > 0: c_type = rffi.getintfield(event, 'c_type'); handle_event( c_type, event )
time.sleep(0.01)
i += 1
finally: lltype.free(event, flavor='raw')

def test():
assert RSDL.Init(INIT_JOYSTICK | RSDL.INIT_VIDEO ) >= 0
num = NumJoysticks(); print 'number of joysticks/gamepads: %s' %num
JoystickEventState( RSDL.ENABLE )
if num:
joy = JoystickOpen( 0 )
numaxes = JoystickNumAxes( joy ); print 'number of axes: %s' %numaxes
numbut = JoystickNumButtons( joy ); print 'number of buttons: %s' %numbut
poll()

if __name__ == '__main__':
from pypy.translator.interactive import Translation
t = Translation( test )
t.annotate(); t.rtype()
entrypoint = t.compile_c()
entrypoint()

No comments:

Post a Comment