> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/opentrack/opentrack/llms.txt
> Use this file to discover all available pages before exploring further.

# IProtocol Interface

> Complete API reference for implementing protocol plugins

## Overview

The `IProtocol` interface is the base class for all protocol plugins. Protocols are responsible for transmitting tracking data from OpenTrack to games and applications using various communication methods.

## Interface Definition

```cpp theme={null}
struct IProtocol : module_status_mixin
{
    IProtocol();
    ~IProtocol() override;
    
    // Core methods (must implement)
    virtual void pose(const double* pose, const double* raw) = 0;
    virtual QString game_name() = 0;
    
    // From module_status_mixin
    virtual module_status initialize() = 0;
    
    // Deleted methods (non-copyable)
    IProtocol(const IProtocol&) = delete;
    IProtocol& operator=(const IProtocol&) = delete;
};
```

## Methods

### initialize()

```cpp theme={null}
virtual module_status initialize() = 0;
```

Initializes the protocol and prepares for data transmission.

<ResponseField name="return" type="module_status">
  Returns `status_ok()` on success, or `error(message)` on failure.
</ResponseField>

**Description:**

* Called once when the protocol is started
* Open network connections, shared memory, or other resources
* Validate configuration settings
* Register with game interfaces
* Return error if initialization fails

**Example implementations:**

<CodeGroup>
  ```cpp proto-ft/ftnoir_protocol_ft.cpp theme={null}
  module_status freetrack::initialize()
  {
      // Verify shared memory is available
      if (!shm.success())
          return error(tr("Can't load freetrack memory mapping"));

      // Set up protocol registry entries
      if (auto ret = set_protocols(); !ret.is_ok())
          return ret;

      // Initialize shared memory structure
      pMemData->data.DataID = 1;
      pMemData->data.CamWidth = 100;
      pMemData->data.CamHeight = 250;
      store(pMemData->GameID2, 0);
      
      for (unsigned k = 0; k < 2; k++)
          store(pMemData->table_ints[k], 0);

      // Start helper process if needed
      if (s.used_interface != settings::enable_freetrack)
          start_dummy();

      return status_ok();
  }
  ```

  ```cpp Example: UDP Protocol theme={null}
  module_status UDPProtocol::initialize()
  {
      // Create UDP socket
      socket = std::make_unique<QUdpSocket>();
      
      // Validate port number
      if (settings.port < 1024 || settings.port > 65535)
          return error("Invalid port number");
      
      // Test connection
      if (!socket->bind())
          return error("Failed to bind UDP socket");
      
      qDebug() << "UDP protocol initialized on port" << settings.port;
      return status_ok();
  }
  ```
</CodeGroup>

### pose()

```cpp theme={null}
virtual void pose(const double* pose, const double* raw) = 0;
```

Transmits filtered and raw tracking data to the target application.

<ParamField path="pose" type="const double*">
  Filtered pose data as array: `[TX, TY, TZ, Yaw, Pitch, Roll]` in degrees/cm
</ParamField>

<ParamField path="raw" type="const double*">
  Raw pose data (before filtering) in same format as `pose`
</ParamField>

<Warning>
  This method is called 250 times per second. Keep it fast and avoid blocking operations.
</Warning>

**Description:**

* Called at 250Hz from the tracking pipeline thread
* Transform data to target format
* Send data via network, shared memory, or other IPC
* Use background threads for expensive operations
* Don't block the calling thread

**Data format:**

* `pose[TX]` / `raw[TX]`: X translation in centimeters
* `pose[TY]` / `raw[TY]`: Y translation in centimeters
* `pose[TZ]` / `raw[TZ]`: Z translation in centimeters
* `pose[Yaw]` / `raw[Yaw]`: Yaw rotation in degrees
* `pose[Pitch]` / `raw[Pitch]`: Pitch rotation in degrees
* `pose[Roll]` / `raw[Roll]`: Roll rotation in degrees

**Example implementation:**

<CodeGroup>
  ```cpp proto-ft/ftnoir_protocol_ft.cpp theme={null}
  void freetrack::pose(const double* headpose, const double* raw)
  {
      constexpr double d2r = M_PI/180;  // Degrees to radians

      // Convert to radians and scale translations
      const float yaw = float(-headpose[Yaw] * d2r);
      const float roll = float(headpose[Roll] * d2r);
      const float tx = float(headpose[TX] * 10);  // cm to mm
      const float ty = float(headpose[TY] * 10);
      const float tz = float(headpose[TZ] * 10);

      // Handle pitch discontinuity at 90 degrees
      const bool is_crossing_90 = std::fabs(headpose[Pitch] - 90) < .15;
      const float pitch = float(-d2r * 
          (is_crossing_90 ? 89.86 : headpose[Pitch]));

      // Write to shared memory atomically
      FTHeap* const ft = pMemData;
      FTData* const data = &ft->data;

      store(data->X, tx);
      store(data->Y, ty);
      store(data->Z, tz);
      store(data->Yaw, yaw);
      store(data->Pitch, pitch);
      store(data->Roll, roll);

      // Also store raw data
      store(data->RawYaw, float(-raw[Yaw] * d2r));
      store(data->RawPitch, float(raw[Pitch] * d2r));
      store(data->RawRoll, float(raw[Roll] * d2r));
      store(data->RawX, float(raw[TX] * 10));
      store(data->RawY, float(raw[TY] * 10));
      store(data->RawZ, float(raw[TZ] * 10));

      // Update game detection
      const std::int32_t id = load(ft->GameID);
      if (intGameID != id)
      {
          // New game connected, update game name
          QString gamename;
          getGameData(id, gamename);
          
          QMutexLocker foo(&game_name_mutex);
          connected_game = gamename.isEmpty() ? 
              tr("Unknown game") : gamename;
          intGameID = id;
      }
      
      // Increment frame counter
      InterlockedAdd((LONG volatile*)&data->DataID, 1);
  }
  ```

  ```cpp Example: UDP Protocol theme={null}
  void UDPProtocol::pose(const double* pose, const double* raw)
  {
      // Pack data into binary format
      struct PosePacket {
          float tx, ty, tz;
          float yaw, pitch, roll;
      } packet;
      
      packet.tx = float(pose[TX]);
      packet.ty = float(pose[TY]);
      packet.tz = float(pose[TZ]);
      packet.yaw = float(pose[Yaw]);
      packet.pitch = float(pose[Pitch]);
      packet.roll = float(pose[Roll]);
      
      // Send via UDP (non-blocking)
      QByteArray data(reinterpret_cast<const char*>(&packet), 
                      sizeof(packet));
      socket->writeDatagram(data, target_address, target_port);
  }
  ```
</CodeGroup>

### game\_name()

```cpp theme={null}
virtual QString game_name() = 0;
```

Returns the name of the currently connected game or application.

<ResponseField name="return" type="QString">
  Name of connected game, or placeholder text if none detected
</ResponseField>

**Description:**

* Called periodically from UI thread
* Return actual game name if detected
* Return generic text like "Game" if unknown
* Use mutex protection if name is updated from `pose()`

**Example implementations:**

<CodeGroup>
  ```cpp proto-ft/ftnoir_protocol_ft.cpp theme={null}
  QString freetrack::game_name()
  {
      QMutexLocker foo(&game_name_mutex);
      return connected_game;
  }
  ```

  ```cpp Example: Static Name theme={null}
  QString MyProtocol::game_name()
  {
      return "FlightGear";
  }
  ```

  ```cpp Example: Dynamic Detection theme={null}
  QString UDPProtocol::game_name()
  {
      QMutexLocker lock(&mutex);
      
      if (last_packet_time.elapsed() > 5000)
          return tr("No connection");
      
      return detected_game_name.isEmpty() ? 
          tr("Unknown") : detected_game_name;
  }
  ```
</CodeGroup>

## Dialog Interface

```cpp theme={null}
struct IProtocolDialog : public BaseDialog
{
    virtual void register_protocol(IProtocol *protocol) = 0;
    virtual void unregister_protocol() = 0;
};
```

### register\_protocol()

```cpp theme={null}
virtual void register_protocol(IProtocol *protocol) = 0;
```

Receives a pointer to the active protocol instance.

<ParamField path="protocol" type="IProtocol*">
  Pointer to the running protocol instance
</ParamField>

**Description:**

* Called from UI thread when protocol starts
* Store pointer for runtime interaction (optional)
* Can query protocol state or display status
* Often implemented as empty function

**Example:**

```cpp theme={null}
class MyProtocolDialog : public IProtocolDialog
{
    IProtocol* protocol = nullptr;
    
public:
    void register_protocol(IProtocol* p) override
    {
        protocol = p;
        // Could start status update timer here
    }
    
    void unregister_protocol() override
    {
        protocol = nullptr;
    }
};
```

### unregister\_protocol()

```cpp theme={null}
virtual void unregister_protocol() = 0;
```

Called when protocol is about to be destroyed.

**Description:**

* Clear any stored protocol pointers
* Stop accessing protocol data
* Clean up any UI state

## Complete Example

<CodeGroup>
  ```cpp my-protocol.h theme={null}
  #pragma once
  #include "api/plugin-api.hpp"
  #include <QUdpSocket>
  #include <QMutex>

  class MyProtocol : public IProtocol
  {
  public:
      MyProtocol();
      ~MyProtocol() override;
      
      module_status initialize() override;
      void pose(const double* pose, const double* raw) override;
      QString game_name() override;
      
  private:
      std::unique_ptr<QUdpSocket> socket;
      QHostAddress target_address;
      quint16 target_port;
      
      QMutex name_mutex;
      QString current_game;
  };

  class MyProtocolDialog : public IProtocolDialog
  {
      Q_OBJECT
  public:
      MyProtocolDialog();
      void register_protocol(IProtocol* p) override {}
      void unregister_protocol() override {}
  };

  class MyProtocolMetadata : public Metadata
  {
      Q_OBJECT
      QString name() override { return tr("My Protocol"); }
      QIcon icon() override { return QIcon(":/images/icon.png"); }
  };
  ```

  ```cpp my-protocol.cpp theme={null}
  #include "my-protocol.h"

  MyProtocol::MyProtocol()
      : target_address("127.0.0.1")
      , target_port(5555)
  {
  }

  MyProtocol::~MyProtocol()
  {
      if (socket)
          socket->close();
  }

  module_status MyProtocol::initialize()
  {
      // Create UDP socket
      socket = std::make_unique<QUdpSocket>();
      
      // Validate configuration
      if (target_port == 0)
          return error("Invalid port number");
      
      // Test socket creation
      if (!socket->bind())
          return error("Failed to create UDP socket");
      
      current_game = "Waiting for connection...";
      
      return status_ok();
  }

  void MyProtocol::pose(const double* pose, const double* raw)
  {
      // Create binary packet
      struct {
          float data[6];
      } packet;
      
      // Copy pose data
      for (int i = 0; i < 6; i++)
          packet.data[i] = float(pose[i]);
      
      // Send UDP packet (non-blocking)
      QByteArray bytes(reinterpret_cast<const char*>(&packet), 
                       sizeof(packet));
      socket->writeDatagram(bytes, target_address, target_port);
  }

  QString MyProtocol::game_name()
  {
      QMutexLocker lock(&name_mutex);
      return current_game;
  }

  // Register plugin
  OPENTRACK_DECLARE_PROTOCOL(MyProtocol, MyProtocolDialog,
                             MyProtocolMetadata)
  ```
</CodeGroup>

## Communication Patterns

<AccordionGroup>
  <Accordion title="Shared Memory">
    Used by FreeTrack/TrackIR protocols for low-latency local communication.

    ```cpp theme={null}
    class SharedMemoryProtocol : public IProtocol
    {
        shm_wrapper shm{"SharedMemName", "MutexName", sizeof(Data)};
        Data* pData = (Data*)shm.ptr();
        
        module_status initialize() override
        {
            if (!shm.success())
                return error("Failed to create shared memory");
            return status_ok();
        }
        
        void pose(const double* pose, const double* raw) override
        {
            // Atomic writes to shared memory
            InterlockedExchange(&pData->x, pose[TX]);
            InterlockedExchange(&pData->y, pose[TY]);
            // ...
        }
    };
    ```
  </Accordion>

  <Accordion title="UDP Network">
    Simple datagram-based protocol for network transmission.

    ```cpp theme={null}
    void UDPProtocol::pose(const double* pose, const double* raw)
    {
        // Format as string
        QString packet = QString("%1,%2,%3,%4,%5,%6")
            .arg(pose[TX]).arg(pose[TY]).arg(pose[TZ])
            .arg(pose[Yaw]).arg(pose[Pitch]).arg(pose[Roll]);
        
        // Send UDP datagram
        socket->writeDatagram(packet.toUtf8(), 
                             target_address, target_port);
    }
    ```
  </Accordion>

  <Accordion title="Virtual Device">
    Emulate joystick or other input device.

    ```cpp theme={null}
    void VirtualDeviceProtocol::pose(const double* pose, 
                                      const double* raw)
    {
        // Map to joystick axes
        joystick_state.x = map_to_axis(pose[TX], -50, 50);
        joystick_state.y = map_to_axis(pose[TY], -50, 50);
        joystick_state.z = map_to_axis(pose[TZ], -50, 50);
        joystick_state.rx = map_to_axis(pose[Yaw], -180, 180);
        joystick_state.ry = map_to_axis(pose[Pitch], -90, 90);
        joystick_state.rz = map_to_axis(pose[Roll], -180, 180);
        
        // Update virtual device
        device->update_state(joystick_state);
    }
    ```
  </Accordion>
</AccordionGroup>

## Performance Considerations

<Warning>
  The `pose()` method is called 250 times per second. Optimize carefully.
</Warning>

### Best Practices

1. **Use non-blocking I/O**
   ```cpp theme={null}
   // Good: non-blocking UDP send
   socket->writeDatagram(data, addr, port);

   // Bad: blocking TCP send
   socket->write(data);
   socket->waitForBytesWritten();  // Blocks!
   ```

2. **Minimize allocations**
   ```cpp theme={null}
   // Good: reuse buffer
   class MyProtocol {
       QByteArray buffer;  // Reused across calls
       
       void pose(const double* pose, const double* raw) override {
           buffer.clear();
           // Fill buffer...
       }
   };
   ```

3. **Batch updates if needed**
   ```cpp theme={null}
   void pose(const double* pose, const double* raw) override
   {
       buffer_pose(pose);
       
       if (++frame_count % 10 == 0) {  // Send every 10th frame
           flush_buffer();
       }
   }
   ```

4. **Use atomic operations for shared memory**
   ```cpp theme={null}
   // Good: atomic update
   InterlockedExchange(&pData->value, new_value);

   // Bad: non-atomic
   pData->value = new_value;  // Race condition!
   ```

## See Also

* [Tracker Interface](/api/tracker-interface) - Capture tracking data
* [Filter Interface](/api/filter-interface) - Process tracking data
* [Metadata](/api/metadata) - Plugin metadata requirements
