Files
LookingGlass/client/include/interface/transport.h
Geoffrey McRae e2ee9244ed
Some checks failed
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Has been cancelled
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Has been cancelled
build / module (push) Has been cancelled
build / host-linux (push) Has been cancelled
build / host-windows-cross (push) Has been cancelled
build / host-windows-native (push) Has been cancelled
build / idd (push) Has been cancelled
build / obs (clang) (push) Has been cancelled
build / obs (gcc) (push) Has been cancelled
build / docs (push) Has been cancelled
[client] recovery: add force keybind
Add a configured escape key plus Shift+R command that requests display
recovery from the LGMP transport and switches video to SPICE.

Track the request on the main thread, report its asynchronous result,
and return its producer instance with the request serial for exact
correlation.

Preserve normal escape+R rotation behavior, including custom Shift
escape keys.
2026-08-20 16:53:10 +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, uint64_t * instance, 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