Files
LookingGlass/client/include/interface/transport.h
2026-08-14 10:22:37 +10:00

510 lines
15 KiB
C

/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _H_LG_CLIENT_TRANSPORT_
#define _H_LG_CLIENT_TRANSPORT_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "common/framebuffer.h"
#include "common/types.h"
#include "interface/audio.h"
#include "interface/clipboard.h"
#include "interface/input.h"
#define LG_TRANSPORT_MAX_DAMAGE_RECTS LG_MAX_FRAME_DAMAGE_RECTS
typedef struct LG_Transport LG_Transport;
typedef struct LG_RendererInterop LG_RendererInterop;
typedef bool (*LG_TransportCancelledFn)(void * opaque);
typedef enum LG_TransportStatus
{
LG_TRANSPORT_OK,
LG_TRANSPORT_TIMEOUT,
LG_TRANSPORT_UNAVAILABLE,
LG_TRANSPORT_INVALID_VERSION,
LG_TRANSPORT_DISCONNECTED,
LG_TRANSPORT_END,
LG_TRANSPORT_ERROR,
}
LG_TransportStatus;
typedef enum LG_TransportGuestOS
{
LG_TRANSPORT_OS_LINUX,
LG_TRANSPORT_OS_BSD,
LG_TRANSPORT_OS_OSX,
LG_TRANSPORT_OS_WINDOWS,
LG_TRANSPORT_OS_OTHER,
}
LG_TransportGuestOS;
enum
{
LG_TRANSPORT_FEATURE_SET_CURSOR_POS = 0x1,
LG_TRANSPORT_FEATURE_WINDOW_SIZE = 0x2,
LG_TRANSPORT_FEATURE_FRAME_SCHEDULE = 0x4,
LG_TRANSPORT_FEATURE_INPUT = 0x8,
LG_TRANSPORT_FEATURE_CLIPBOARD = 0x10,
};
typedef uint32_t LG_TransportFeatureFlags;
typedef struct LG_VersionMismatch
{
bool valid;
char component[16];
uint32_t expectedVersion;
uint32_t currentVersion;
}
LG_VersionMismatch;
#define LG_RECOVERY_MAX_VERSIONS 4
enum
{
LG_RECOVERY_CAP_DISPLAY = 0x1,
};
typedef uint32_t LG_RecoveryCaps;
typedef enum LG_RecoveryRequest
{
LG_RECOVERY_REQ_NONE,
LG_RECOVERY_REQ_NORMAL,
LG_RECOVERY_REQ_RECOVERY,
}
LG_RecoveryRequest;
typedef enum LG_RecoveryState
{
LG_RECOVERY_STATE_UNKNOWN,
LG_RECOVERY_STATE_NORMAL,
LG_RECOVERY_STATE_SWITCHING,
LG_RECOVERY_STATE_ACTIVE,
LG_RECOVERY_STATE_FAILED,
}
LG_RecoveryState;
typedef enum LG_RecoveryError
{
LG_RECOVERY_ERR_NONE,
LG_RECOVERY_ERR_UNSUPPORTED,
LG_RECOVERY_ERR_HELPER_UNAVAILABLE,
LG_RECOVERY_ERR_TOPOLOGY_FAILED,
LG_RECOVERY_ERR_NO_FALLBACK_DISPLAY,
LG_RECOVERY_ERR_BUSY,
LG_RECOVERY_ERR_CAPACITY,
}
LG_RecoveryError;
typedef struct LG_RecoveryVersion
{
char component[16];
uint32_t version;
}
LG_RecoveryVersion;
typedef struct LG_RecoveryInfo
{
uint32_t abiVersion;
LG_RecoveryCaps capabilities;
uint64_t instance;
uint32_t heartbeat;
bool uuidValid;
uint8_t uuid[16];
char producerVersion[64];
uint32_t versionCount;
LG_RecoveryVersion versions[LG_RECOVERY_MAX_VERSIONS];
uint32_t requestSerial;
LG_RecoveryRequest request;
uint32_t ackSerial;
LG_RecoveryRequest ackRequest;
LG_RecoveryState state;
LG_RecoveryError error;
}
LG_RecoveryInfo;
typedef struct LG_TransportSession
{
char version[32];
char name[256];
LG_TransportFeatureFlags features;
bool uuidValid;
uint8_t uuid[16];
LG_TransportGuestOS os;
char osName[64];
char capture[32];
char cpuModel[256];
uint8_t cpus;
uint8_t cores;
uint8_t sockets;
LG_VersionMismatch versionMismatch;
}
LG_TransportSession;
enum
{
LG_TRANSPORT_FRAME_BLOCK_SCREENSAVER = 0x1,
LG_TRANSPORT_FRAME_REQUEST_ACTIVATION = 0x2,
LG_TRANSPORT_FRAME_TRUNCATED = 0x4,
};
typedef uint32_t LG_TransportFrameFlags;
typedef struct LG_TransportFrameTiming
{
/* The producer fields through readyLeadTime are available and coherent. */
bool valid;
/* receiveTime and prepareTime are available and coherent. */
bool providerValid;
bool phaseValid;
uint32_t scheduleGeneration;
uint32_t scheduleEpoch;
uint32_t scheduleDeadlineSerial;
uint64_t captureTime;
uint64_t postProcessTime;
uint64_t copyTime;
uint64_t readyTime;
uint64_t holdTime;
uint64_t readyLeadTime;
/* Non-overlapping work performed while acquiring and preparing the returned
* payload. These do not include status callbacks or renderer import time. */
uint64_t receiveTime;
uint64_t prepareTime;
}
LG_TransportFrameTiming;
typedef struct LG_TransportFrameFormat
{
uint32_t version;
FrameType type;
uint32_t screenWidth;
uint32_t screenHeight;
uint32_t dataWidth;
uint32_t dataHeight;
uint32_t frameWidth;
uint32_t frameHeight;
FrameRotation rotation;
uint32_t stride;
uint32_t pitch;
bool hdr;
bool hdrPQ;
bool hdrMetadata;
uint16_t hdrDisplayPrimary[3][2];
uint16_t hdrWhitePoint[2];
uint32_t hdrMaxDisplayLuminance;
uint32_t hdrMinDisplayLuminance;
uint32_t hdrMaxContentLightLevel;
uint32_t hdrMaxFrameAverageLightLevel;
uint32_t sdrWhiteLevel;
}
LG_TransportFrameFormat;
typedef struct LG_TransportFrame
{
uint64_t serial;
/* When setStatusListener is present, matches LG_VideoComponentStatus::epoch
* for the frame endpoint which returned this payload; otherwise may be 0. */
uint64_t epoch;
uint64_t timestamp;
uint32_t scheduleEpoch;
uint32_t scheduleDeadlineSerial;
LG_TransportFrameFlags flags;
bool scheduleOwner;
// Backend-owned immutable metadata, valid until releaseFrame.
const LG_TransportFrameFormat * format;
const FrameBuffer * framebuffer;
int dmaFD;
const FrameDamageRect * damageRects;
uint32_t damageRectsCount;
/* A transport may keep the frame payload alive asynchronously after
* onFrame returns. The callback is idempotent and releases that ownership. */
LG_FrameReleaseFn releaseFn;
void * releaseOpaque;
uint64_t releaseHandle;
}
LG_TransportFrame;
enum
{
LG_TRANSPORT_POINTER_POSITION = 0x1,
LG_TRANSPORT_POINTER_VISIBLE = 0x2,
LG_TRANSPORT_POINTER_SHAPE = 0x4,
LG_TRANSPORT_POINTER_COLOR_TRANSFORM = 0x8,
LG_TRANSPORT_POINTER_VISIBLE_VALID = 0x10,
};
typedef uint32_t LG_TransportPointerFlags;
typedef struct LG_TransportPointer
{
/* When setStatusListener is present, matches LG_VideoComponentStatus::epoch
* for the pointer endpoint which returned this payload; otherwise may be 0.
*/
uint64_t epoch;
LG_TransportPointerFlags flags;
int16_t x;
int16_t y;
CursorType type;
int16_t hx;
int16_t hy;
uint32_t width;
uint32_t height;
uint32_t pitch;
uint32_t sdrWhiteLevel;
const uint8_t * shape;
const LGColorTransform * colorTransform;
}
LG_TransportPointer;
typedef struct LG_VideoComponentStatus
{
bool available;
/* Nonzero while available and changed whenever the endpoint is replaced or
* changes availability. */
uint64_t epoch;
/* LG_TRANSPORT_OK while available; otherwise the latest observed cause. */
LG_TransportStatus reason;
}
LG_VideoComponentStatus;
typedef struct LG_VideoStatus
{
LG_VideoComponentStatus frame;
LG_VideoComponentStatus pointer;
}
LG_VideoStatus;
typedef void (*LG_VideoStatusFn)(void * opaque,
const LG_VideoStatus * status);
typedef enum LG_TransportControlType
{
LG_TRANSPORT_CONTROL_SET_CURSOR_POS,
LG_TRANSPORT_CONTROL_WINDOW_SIZE,
LG_TRANSPORT_CONTROL_FRAME_SCHEDULE,
}
LG_TransportControlType;
enum
{
LG_TRANSPORT_FRAME_SCHEDULE_ACTIVE = 0x1,
LG_TRANSPORT_FRAME_SCHEDULE_RELEASE = 0x2,
LG_TRANSPORT_FRAME_SCHEDULE_RESET = 0x4,
LG_TRANSPORT_FRAME_SCHEDULE_IMMEDIATE = 0x8,
};
typedef uint32_t LG_TransportFrameScheduleFlags;
typedef struct LG_TransportControl
{
LG_TransportControlType type;
union
{
struct { int32_t x, y; } cursorPos;
struct { uint32_t width, height; } windowSize;
struct
{
uint32_t generation;
LG_TransportFrameScheduleFlags flags;
uint64_t period;
uint64_t targetSlack;
int64_t phaseError;
uint32_t feedbackFrameSerial;
uint32_t feedbackScheduleEpoch;
uint32_t feedbackDeadlineSerial;
uint32_t lease;
}
frameSchedule;
};
}
LG_TransportControl;
typedef uint64_t LG_TransportControlToken;
typedef struct LG_FrameOps
{
/* Registration is scoped to a connected session and must synchronously
* report the current status after releasing component state locks. Frame and
* pointer epochs advance independently whenever their endpoint is
* replaced or changes availability. Passing NULL unregisters the listener
* and synchronously quiesces its callbacks. Callbacks must be serialized.
* Providers without a listener are assumed to keep every component they
* expose available for the session. */
void (*setStatusListener)(LG_Transport * transport,
LG_VideoStatusFn callback, void * callbackOpaque);
bool (*supportsDMA)(LG_Transport * transport);
bool (*attachRenderer)(LG_Transport * transport,
const LG_RendererInterop * interop);
void (*detachRenderer)(LG_Transport * transport);
LG_TransportStatus (*nextFrame)(LG_Transport * transport, bool useDMA,
LG_TransportFrame * frame);
/* Read producer timings after the renderer has consumed the frame. Some
* transports publish the frame while its asynchronous copy is in progress,
* so these values are intentionally sampled late. */
void (*getFrameTiming)(LG_Transport * transport,
const LG_TransportFrame * frame, LG_TransportFrameTiming * timing);
void (*releaseFrame)(LG_Transport * transport, LG_TransportFrame * frame);
/* Required, thread-safe cancellation of a blocking nextFrame call. This
* does not release a frame already returned to the consumer. */
void (*cancelFrameWait)(LG_Transport * transport);
/* Called by the frame consumer as it exits. A backend may release transient
* stream resources; nextFrame must reacquire them when the consumer
* restarts. */
void (*stopFrame)(LG_Transport * transport);
/* Pointer operations are optional. When absent, pointer.available must be
* false for providers implementing the status listener. */
LG_TransportStatus (*nextPointer)(LG_Transport * transport,
LG_TransportPointer * pointer);
void (*releasePointer)(LG_Transport * transport,
LG_TransportPointer * pointer);
/* Required whenever nextPointer is present; thread-safe cancellation of a
* blocking nextPointer call. */
void (*cancelPointerWait)(LG_Transport * transport);
/* Called by the pointer consumer as it exits. A backend may release
* transient stream resources; nextPointer must reacquire them when the
* consumer restarts. */
void (*stopPointer)(LG_Transport * transport);
}
LG_FrameOps;
typedef struct LG_SwSurfaceEventOps
{
void (*configure)(void * opaque, unsigned int width, unsigned int height);
void (*destroy)(void * opaque);
void (*drawFill)(void * opaque, int x, int y, int width, int height,
uint32_t color);
/* Bitmap data is BGRA32 and remains valid only for the callback. */
void (*drawBitmap)(void * opaque, bool topDown, int x, int y,
int width, int height, int stride, const void * data);
/* Pointer payloads remain valid only for the callback. */
void (*pointer)(void * opaque, const LG_TransportPointer * pointer);
}
LG_SwSurfaceEventOps;
typedef struct LG_SwSurfaceOps
{
/* detach synchronously quiesces all event callbacks. */
bool (*attach)(LG_Transport * transport,
const LG_SwSurfaceEventOps * events, void * opaque);
void (*detach)(LG_Transport * transport);
/* Activation may wait while the requested component becomes available.
* Deactivation must complete promptly without cancellation. */
bool (*setActive)(LG_Transport * transport, bool active);
/* Required. Interrupts a pending setActive call and causes it to return
* promptly. Safe to invoke repeatedly from another thread. */
void (*cancelPending)(LG_Transport * transport);
}
LG_SwSurfaceOps;
typedef enum LG_VideoType
{
LG_VIDEO_TYPE_FRAME,
LG_VIDEO_TYPE_SW_SURFACE,
}
LG_VideoType;
typedef struct LG_VideoOps
{
const char * name;
LG_VideoType type;
union
{
const LG_FrameOps * frame;
const LG_SwSurfaceOps * swSurface;
};
}
LG_VideoOps;
typedef struct LG_TransportOps
{
const char * name;
void (*setup)(void);
bool (*create)(LG_Transport ** transport);
void (*destroy)(LG_Transport ** transport);
LG_TransportStatus (*connect)(LG_Transport * transport,
LG_TransportSession * session);
/* Required session entry point. The callback must be observed promptly
* while establishing a session, and cancellation must bound this call. */
LG_TransportStatus (*connectCancellable)(LG_Transport * transport,
LG_TransportSession * session, LG_TransportCancelledFn cancelled,
void * opaque);
void (*disconnect)(LG_Transport * transport);
bool (*sessionValid)(LG_Transport * transport);
/* Queried after create. The returned operations remain valid until the
* transport is destroyed. NULL indicates that this transport has no video. */
const LG_VideoOps *(*getVideoOps)(LG_Transport * transport);
/* Queried after connect. The returned operations and opaque value remain
* valid until disconnect; NULL indicates that this session has no input. */
const LG_InputOps *(*getInputOps)(LG_Transport * transport,
void ** opaque);
/* Queried after connect. The returned operations and opaque value remain
* valid until disconnect; NULL indicates that this session has no audio. */
const LG_AudioOps *(*getAudioOps)(LG_Transport * transport,
void ** opaque);
/* Queried after connect. The returned operations and opaque value remain
* valid until disconnect; NULL indicates that this session has no
* clipboard. */
const LG_ClipboardOps *(*getClipboardOps)(LG_Transport * transport,
void ** opaque);
/* Recovery operations are independent of a transport session and may be
* used after create whenever the backing transport advertises them. */
LG_TransportStatus (*getRecoveryInfo)(LG_Transport * transport,
LG_RecoveryInfo * info);
LG_TransportStatus (*requestRecovery)(LG_Transport * transport,
LG_RecoveryRequest request, uint32_t * serial);
LG_TransportStatus (*sendControl)(LG_Transport * transport,
const LG_TransportControl * control, LG_TransportControlToken * token);
LG_TransportStatus (*controlStatus)(LG_Transport * transport,
LG_TransportControlToken token);
}
LG_TransportOps;
typedef struct LG_TransportInstance
{
LG_Transport * handle;
const LG_TransportOps * ops;
}
LG_TransportInstance;
void lgTransport_setup(void);
bool lgTransport_isValid(const char * name);
bool lgTransport_create(const char * name, LG_TransportInstance * instance);
void lgTransport_destroy(LG_TransportInstance * instance);
#endif