Input_Engine

PDJE_Input is the current public entry point for low-latency keyboard, mouse, and MIDI ingestion.

Public Types

class PDJE_Input

Input device manager.

Searches for connected devices and pairs them with the engine.

struct DeviceData
enum class PDJE_Dev_Type

Values:

enumerator MOUSE
enumerator KEYBOARD
enumerator UNKNOWN
enum PDJE_INPUT_STATE

Values:

enumerator DEVICE_CONFIG_STATE
enumerator INPUT_LOOP_READY
enumerator INPUT_LOOP_RUNNING
enumerator DEAD

Platform Behavior

  • Windows uses the Raw Input subprocess and IPC path and currently reports the backend string rawinput-ipc.

  • Linux can use the evdev path directly or a Wayland-backed path when device selection requires it.

  • macOS is not implemented in the current source tree.

Transport Notes

The older docs spent more time on transport details, and that context is still useful:

  • On Windows, standard input is intentionally isolated behind a child-process Raw Input path so the engine can collect low-latency keyboard and mouse data and ship it back through IPC.

  • On Linux, the default low-level path is evdev. When evdev is not the path the current environment can use, the engine can also operate through the Wayland-backed route described by Init(…).

  • MIDI is handled as a separate stream and surfaces through midi_datas even though the standard input backend and MIDI engine are configured together.

Binding Status

The older docs also covered non-C++ integration paths. The current split is:

  • the in-tree SWIG C# and Python bindings do not expose PDJE_Input

  • the Godot-facing wrapper path is still the documented non-C++ route for this module

  • that Godot wrapper uses PDJE_Input_Module together with an InputLine node that emits keyboard and MIDI signals instead of handing out raw transport pointers

Initialization Signature

bool PDJE_Input::Init(void *platform_ctx0 = nullptr, void *platform_ctx1 = nullptr, bool use_internal_window = false)

initialize pdje input.

Platform contexts (optional):

  • Linux: platform_ctx0 = wl_display*, platform_ctx1 = wl_surface*

  • Windows: currently ignored (reserved)

use_internal_window:

  • Linux: if true, allows PDJE to create an internal Wayland window when evdev -> wayland fallback is needed and host handles are unavailable.

  • Windows: currently ignored (reserved)

Init() accepts optional platform handles:

  • platform_ctx0 on Linux is expected to be wl_display* when the host already owns the Wayland connection

  • platform_ctx1 on Linux is expected to be wl_surface* when the host already owns the Wayland surface

  • use_internal_window allows PDJE to create its own internal Wayland window when a Wayland fallback is needed and host handles are not available

Windows keeps the same signature for parity but ignores these parameters in the current implementation.

Current Lifecycle

The tested integration path in the current tree is:

  1. Call Init(…).

  2. Discover devices with GetDevs() and GetMIDIDevs().

  3. Select at least one keyboard or mouse device for the standard backend, then add any desired MIDI ports.

  4. Call Config(…).

  5. Inspect GetCurrentInputBackend() if you need to know which backend was selected.

  6. Acquire a PDJE_INPUT_DATA_LINE with PullOutDataLine().

  7. Call Run().

  8. Consume input_arena and midi_datas.

  9. Call Kill() on shutdown.

Selected methods:

std::vector<DeviceData> PDJE_Input::GetDevs()

Get All Connected devices.

std::vector<libremidi::input_port> PDJE_Input::GetMIDIDevs()

Get All Connected MIDI devices.

bool PDJE_Input::Config(std::vector<DeviceData> &devs, const std::vector<libremidi::input_port> &midi_dev)

configure device data.

PDJE_INPUT_STATE PDJE_Input::GetState()

get pdje input module’s configuration & running state

std::string PDJE_Input::GetCurrentInputBackend() const

get current active input backend name.

bool PDJE_Input::Run()

run input Loop

bool PDJE_Input::Kill()

kill input Loop

PDJE_INPUT_DATA_LINE PDJE_Input::PullOutDataLine()

pull out input data line. The input Loop will pass datas in here.

Data Line Semantics

struct PDJE_INPUT_DATA_LINE

PDJE_INPUT_DATA_LINE exposes two optional pointers:

  • input_arena points at the standard keyboard and mouse event transport. Call input_arena->Receive() before reading input_arena->datas.

  • midi_datas points at Atomic_Double_Buffer<PDJE_MIDI::MIDI_EV>. Call Get() to swap buffers and read the current vector snapshot.

Operationally:

  • input_arena is the keyboard and mouse lane the judge currently expects when Judge_Init::SetInputLine() validates the input path

  • midi_datas is a separate MIDI lane and can be consumed independently of the standard input events

Example

PDJE_Input input;
if (!input.Init(nullptr, nullptr, false)) {
    return;
}

auto devs = input.GetDevs();
auto midi_ports = input.GetMIDIDevs();

DEV_LIST selected_standard;
std::vector<libremidi::input_port> selected_midi;

for (const auto &dev : devs) {
    if (dev.Type == PDJE_Dev_Type::KEYBOARD) {
        selected_standard.push_back(dev);
    }
}

for (const auto &port : midi_ports) {
    selected_midi.push_back(port);
}

if (!input.Config(selected_standard, selected_midi)) {
    return;
}

auto backend = input.GetCurrentInputBackend();
auto line = input.PullOutDataLine();

if (!input.Run()) {
    return;
}

while (input.GetState() == PDJE_INPUT_STATE::INPUT_LOOP_RUNNING) {
    if (line.input_arena) {
        line.input_arena->Receive();
        for (const auto &event : line.input_arena->datas) {
            std::string device_name(event.name, event.name_len);
            (void)device_name;
            (void)event.microSecond;
        }
    }

    if (line.midi_datas) {
        auto *midi_events = line.midi_datas->Get();
        for (const auto &event : *midi_events) {
            std::string port_name(event.port_name, event.port_name_len);
            (void)port_name;
            (void)event.highres_time;
        }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

(void)backend;
input.Kill();

Notes

  • GetCurrentInputBackend() returns “none” until default_devs has been initialized.

  • Reacquire the data line after tearing down and rebuilding the input module.

  • The judge module currently expects SetInputLine() to receive a non-null input_arena, so the tested path includes a configured standard backend even when MIDI rails are also used.

  • Keep device selection explicit. GetDevs() is for standard devices, GetMIDIDevs() is for MIDI ports, and Config(…) is where the two streams are joined into one input runtime.

Godot Wrapper Example

extends Node

var input_module:PDJE_Input_Module

func _ready():
    input_module = PDJE_Input_Module.new()
    input_module.Init()

    var selected_devices:Array = []
    for device in input_module.GetDevs():
        if device["type"] == "KEYBOARD":
            selected_devices.push_back(device)

    var selected_midi_devices = input_module.GetMIDIDevs()
    input_module.Config(selected_devices, selected_midi_devices)

    input_module.InitializeInputLine($InputLine)
    input_module.Run()

func _process(_delta):
    $InputLine.emit_input_signal()

func _on_input_line_pdje_input_keyboard_signal(device_id, device_name,
                                               microsecond_string,
                                               keyboard_key, is_pressed):
    print(device_id, device_name, microsecond_string, keyboard_key,
          is_pressed)

func _on_input_line_pdje_midi_input_signal(port_name, input_type, channel,
                                           position, value,
                                           microsecond_string):
    print(port_name, input_type, channel, position, value,
          microsecond_string)