SDL

SDL enables fflive to use input devices such as keyboard, mouse, and joysticks.

A global constructor SDL() is built-in to the quickjs engine. This is a simple wrapper around SDL functionality.

This page is mostly based on SDL documentation itself.

NOTE: Currently, you need to explicitly poll for the events. I plan on moving this to using async functions in the future.

NOTE2: SDL support only works with fflive (and not ffgac or ffedit).


SDL Constructor

The new SDL() constructor is used to create a new SDL object.

Syntax

new SDL()

Return value

The new SDL object.

Examples

const sdl = new SDL();
if ( sdl )
  print("SDL initialized");

SDL.prototype.numJoysticks()

This is a wrapper around SDL_NumJoysticks().

Syntax

numJoysticks()

Return value

Returns the number of attached joysticks on success or a negative error code on failure.

Examples

const sdl = new SDL();
const numJoysticks = sdl.numJoysticks();
print(`numJoysticks: ${numJoysticks}`);

SDL.prototype.joystickOpen()

This is a wrapper around SDL_JoystickOpen().

NOTE: The resulting object is kept internally in the SDL object.

Syntax

joystickOpen(device_index)

Parameters

device_index is the index of the joystick to query.

Return value

Nothing useful.

Examples

const sdl = new SDL();
sdl.joystickOpen(0);

SDL.prototype.joystickClose()

This is a wrapper around SDL_JoystickClose().

NOTE: The input parameter is obtained internally from the SDL object (joystickOpen() must have already been called).

Syntax

joystickClose()

Return value

Nothing useful.

Examples

const sdl = new SDL();
sdl.joystickOpen(0);
sdl.joystickClose();

SDL.prototype.gameControllerOpen()

This is a wrapper around SDL_GameControllerOpen().

NOTE: The resulting object is kept internally in the SDL object.

Syntax

gameControllerOpen(joystick_index)

Parameters

joystick_index is the device_index of a device, up to SDL_NumJoysticks().

Return value

Nothing useful.

Examples

const sdl = new SDL();
sdl.gameControllerOpen(0);

SDL.prototype.gameControllerClose()

This is a wrapper around SDL_GameControllerClose().

NOTE: The input parameter is obtained internally fromt the SDL object (gameControllerOpen() must have already been called).

Syntax

gameControllerClose()

Return value

Nothing useful.

Examples

const sdl = new SDL();
sdl.gameControllerOpen(0);
sdl.gameControllerClose();

SDL.prototype.getEvent()

This is a poll-based abstraction around SDL_PeepEvents().

You should call this function repeatedly and process the resulting SDL_Event until the function returns null.

Syntax

getEvent()

Return value

An SDL_Event (defined below) if available, null otherwise.

Examples

const sdl = new SDL();
sdl.joystickOpen(0);
while ( true )
{
  const e = sdl.getEvent();
  if ( e === null )
    break;
  print(JSON.stringify(e));
}
sdl.joystickClose();

SDL_Event

The SDL_Events returned by getEvent() are a representation of the SDL_Event structure from SDL.

The structure is different depending on the type of event.

SDL_KeyboardEvent

TODO: number: SDL.SDL_KEYDOWN or SDL.SDL_KEYUP.

TODO: number: Event timestamp, in milliseconds.

TODO: number: SDL.SDL_PRESSED or SDL.SDL_RELEASED.

TODO: number: Non-zero if this is a key repeat.

TODO: number: SDL physical key code - see SDL_Scancode for details.

  • The key codes are available in the global SDL object, i.e.: SDL.SDL_SCANCODE_UNKNOWN.

TODO: number: SDL virtual key code - see SDL_Keycode for details.

  • The key codes are available in the global SDL object, i.e.: SDL.SDLK_UNKNOWN.

TODO: number: Current key modifiers.

SDL_JoyAxisEvent

TODO: number: SDL.SDL_JOYAXISMOTION.

TODO: number: Event timestamp, in milliseconds.

TODO: number: The joystick instance id.

TODO: number: The joystick axis index.

TODO: number: The axis value (range: -32768 to 32767).

SDL_JoyBallEvent

TODO: number: SDL.SDL_JOYBALLMOTION.

TODO: number: Event timestamp, in milliseconds.

TODO: number: The joystick instance id.

TODO: number: The joystick trackball index.

TODO: number: The relative motion in the X direction.

TODO: number: The relative motion in the Y direction.

SDL_JoyHatEvent

TODO: number: SDL.SDL_JOYHATMOTION.

TODO: number: Event timestamp, in milliseconds.

TODO: number: The joystick instance id.

TODO: number: The joystick trackball index.

TODO: number: The hat position value.

SDL_JoyButtonEvent

TODO: number: SDL.SDL_JOYBUTTONDOWN or SDL.SDL_JOYBUTTONUP.

TODO: number: Event timestamp, in milliseconds.

TODO: number: The joystick instance id.

TODO: number: The joystick button index.

TODO: number: SDL.SDL_PRESSED or SDL.SDL_RELEASED.

SDL_ControllerAxisEvent

TODO: number: SDL.SDL_CONTROLLERAXISMOTION.

TODO: number: Event timestamp, in milliseconds.

TODO: number: The joystick instance id.

TODO: number: The controller axis (SDL_GameControllerAxis).

TODO: number: The axis value (range: -32768 to 32767).

SDL_ControllerButtonEvent

TODO: number: SDL.SDL_CONTROLLERBUTTONDOWN or SDL.SDL_CONTROLLERBUTTONUP.

TODO: number: Event timestamp, in milliseconds.

TODO: number: The joystick instance id.

TODO: number: The controller button (SDL_GameControllerButton).

TODO: number: SDL.SDL_PRESSED or SDL.SDL_RELEASED.

Full Example

This example will open the first joystick found (I use a cheap SNES USB controller), and use the arrow keys to add motion to the entire frame.

let sdl;
let cur_pan_mv = new MV(0,0);
let step = 0;

export function setup(args)
{
  // select motion vectors
  args.features = [ "mv" ];

  // parse params from command line (a number is expected)
  if ( !("params" in args) )
    throw new Error("A parameter is expected for the step in the command line (use -sp <step>).");
  step = args.params;

  // initialize SDL with first joystick
  sdl = new SDL();
  const numJoysticks = sdl.numJoysticks();
  console.log(`numJoysticks: ${numJoysticks}`);
  if ( numJoysticks > 0 )
    sdl.joystickOpen(0);
}

export function glitch_frame(frame)
{
  const fwd_mvs = frame.mv?.forward;
  if ( !fwd_mvs )
    return;

  // set motion vector overflow behaviour in ffglitch to "truncate"
  frame.mv.overflow = "truncate";

  // parse all SDL events
  while ( true )
  {
    const event = sdl.getEvent();
    if ( event === null )
      break;
    // Uncomment the following line to debug the event structure.
    // console.log(JSON.stringify(event));
    if ( event.type === SDL.SDL_JOYAXISMOTION )
    {
      // Update current MV on arrow keys
      switch ( event.value )
      {
        case -32768: cur_pan_mv[event.axis] += step; break;
        case  32767: cur_pan_mv[event.axis] -= step; break;
      }
    }
  }

  // pan entire frame with current MV
  fwd_mvs.add(cur_pan_mv);
}

Run it with:

$ fflive -i input.avi -s script.js -sp 3