[repos] lgprotocol: import shared protocol definitions
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

Add LGProtocol as a pinned submodule and consume its KVMFR protocol
definitions throughout the client, host, IDD, OBS, and profiler.

Keep Looking Glass framebuffer helpers local while removing duplicated
protocol headers and migrating users to KVMFR-scoped types.
This commit is contained in:
Geoffrey McRae
2026-09-01 20:58:25 +10:00
parent 8e43d41873
commit 8fbe3e7454
94 changed files with 619 additions and 2062 deletions

View File

@@ -1,370 +0,0 @@
/**
* 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_COMMON_KVMFR_
#define _H_LG_COMMON_KVMFR_
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "types.h"
#include "LGMPConfig.h"
#include "KVMFRClipboard.h"
#include "KVMFRInput.h"
#define KVMFR_MAGIC "KVMFR---"
#define KVMFR_VERSION 34
// Fallback used by producers that cannot report the source display's SDR
// white level. IDD frames override this with IDDCX_METADATA2::SdrWhiteLevel.
#define KVMFR_SDR_WHITE_LEVEL_DEFAULT LG_SDR_WHITE_LEVEL_DEFAULT
#define KVMFR_MAX_DAMAGE_RECTS LG_MAX_FRAME_DAMAGE_RECTS
#ifdef _MSC_VER
// don't warn on zero length arrays
#pragma warning(push)
#pragma warning(disable: 4200)
#endif
enum
{
CURSOR_FLAG_POSITION = 0x1,
CURSOR_FLAG_VISIBLE = 0x2,
CURSOR_FLAG_SHAPE = 0x4,
CURSOR_FLAG_COLOR_TRANSFORM = 0x8,
// CURSOR_FLAG_VISIBLE contains a new visibility state. This distinguishes
// an invisible cursor update from messages carrying unrelated cursor data.
CURSOR_FLAG_VISIBLE_VALID = 0x10
};
typedef uint32_t KVMFRCursorFlags;
enum
{
KVMFR_FEATURE_SETCURSORPOS = 0x1,
KVMFR_FEATURE_WINDOWSIZE = 0x2,
KVMFR_FEATURE_FRAME_SCHEDULE = 0x4,
KVMFR_FEATURE_INPUT = 0x8,
KVMFR_FEATURE_CLIPBOARD = 0x10
};
typedef uint32_t KVMFRFeatureFlags;
enum
{
KVMFR_MESSAGE_SETCURSORPOS,
KVMFR_MESSAGE_WINDOWSIZE,
KVMFR_MESSAGE_FRAME_SCHEDULE
};
typedef uint32_t KVMFRMessageType;
typedef struct KVMFR
{
char magic[8];
uint32_t version;
char hostver[32];
KVMFRFeatureFlags features;
//KVMFRRecords start here if there are any
}
KVMFR;
typedef struct KVMFRRecord
{
uint8_t type;
uint32_t size;
uint8_t data[];
}
KVMFRRecord;
enum
{
KVMFR_RECORD_VMINFO = 1,
KVMFR_RECORD_OSINFO
};
typedef enum
{
KVMFR_OS_LINUX,
KVMFR_OS_BSD,
KVMFR_OS_OSX,
KVMFR_OS_WINDOWS,
KVMFR_OS_OTHER
}
KVMFROS;
typedef struct KVMFRRecord_VMInfo
{
uint8_t uuid [16]; // the guest's UUID
char capture[32]; // the capture device in use
uint8_t cpus; // number of CPUs
uint8_t cores; // number of CPU cores
uint8_t sockets; // number of CPU sockets
char model[];
}
KVMFRRecord_VMInfo;
typedef struct KVMFRRecord_OSInfo
{
uint8_t os; // KVMFR_OS_*
char name[]; // friendly name
}
KVMFRRecord_OSInfo;
typedef struct KVMFRCursor
{
int16_t x, y; // cursor x & y position
CursorType type; // shape buffer data type
int8_t hx, hy; // shape hotspot x & y
uint32_t width; // width of the shape
uint32_t height; // height of the shape
uint32_t pitch; // row length in bytes of the shape
uint32_t sdrWhiteLevel; // cursor white level in nits for HDR composition
}
KVMFRCursor;
enum
{
KVMFR_COLOR_TRANSFORM_MATRIX = LG_COLOR_TRANSFORM_MATRIX,
KVMFR_COLOR_TRANSFORM_LUT = LG_COLOR_TRANSFORM_LUT,
};
typedef LGColorTransformFlags KVMFRColorTransformFlags;
// Optional payload appended to KVMFRCursor when
// CURSOR_FLAG_COLOR_TRANSFORM is present. The matrix is an XYZ-to-XYZ
// adjustment; the LUT is applied after encoding to the active wire transfer
// function. Four LUT components keep the payload directly uploadable as an
// RGBA32F texture, with alpha reserved and set to 1.0.
typedef LGColorTransform KVMFRColorTransform;
enum
{
FRAME_FLAG_BLOCK_SCREENSAVER = 0x1 ,
FRAME_FLAG_REQUEST_ACTIVATION = 0x2 ,
FRAME_FLAG_TRUNCATED = 0x4 , // ivshmem was too small for the frame
FRAME_FLAG_HDR = 0x8 , // RGBA10 may not be HDR
FRAME_FLAG_HDR_PQ = 0x10, // HDR PQ has been applied to the frame
FRAME_FLAG_HDR_METADATA = 0x20 // HDR static metadata fields are valid
};
typedef uint32_t KVMFRFrameFlags;
enum
{
KVMFR_FRAME_TIMING_PHASE_VALID = 0x1
};
typedef uint32_t KVMFRFrameTimingFlags;
typedef struct KVMFRFrame
{
/*
* Keep the fields consumed for every frame in the first cache line. The
* timing and HDR metadata below are diagnostic/conditional, while the large
* damage rectangle array is deliberately last.
*/
uint32_t formatVer; // the frame format version number
uint32_t frameSerial; // the unique frame number
FrameType type; // the frame data type
uint32_t screenWidth; // the client's screen width
uint32_t screenHeight; // the client's screen height
uint32_t dataWidth; // the packed frame width
uint32_t dataHeight; // the packed frame height
uint32_t frameWidth; // the unpacked frame width
uint32_t frameHeight; // the unpacked frame height
FrameRotation rotation; // the frame rotation
uint32_t stride; // the row stride (zero if compressed data)
uint32_t pitch; // the row pitch (stride in bytes or the compressed frame size)
uint32_t offset; // offset from the start of this header to the FrameBuffer header
KVMFRFrameFlags flags; // bit field combination of FRAME_FLAG_*
uint32_t damageRectsCount; // the number of damage rectangles (zero for full-frame damage)
// White level in nits for SDR content composited into an HDR frame, such as
// the hardware cursor. Valid for all frames; SDR modes normally report 80.
uint32_t sdrWhiteLevel;
/*
* Producer stage durations in nanoseconds. These are durations rather than
* absolute timestamps because the producer runs in a VM whose monotonic
* clock has a different epoch from the client. Durations can safely be
* combined with the client's local render duration for an end-to-end
* processing time.
*/
uint64_t captureTime;
uint64_t postProcessTime;
uint64_t copyTime;
// Non-copy preparation and publication time, excluding cadence hold time.
uint64_t readyTime;
// Time a prepared frame waited for its cadence publication target.
uint64_t holdTime;
// Time remaining until the cadence deadline when the frame was published.
uint64_t readyLeadTime;
// Published after the timing fields and matched against frameSerial by the
// client. timingValid is written last by the producer.
uint32_t timingSerial;
uint32_t timingValid;
KVMFRFrameTimingFlags timingFlags;
// Keep the conditional HDR block and damage rectangles on separate cache
// lines from the producer timing fields.
uint8_t timingReserved[4];
// HDR static metadata (valid when FRAME_FLAG_HDR_METADATA is set)
// Display color primaries in 0.00002 units (SMPTE ST 2086 format)
uint16_t hdrDisplayPrimary[3][2]; // Rx,Ry, Gx,Gy, Bx,By
uint16_t hdrWhitePoint[2]; // Wx, Wy
// Mastering display luminances follow SMPTE ST 2086 units: the maximum is
// in whole cd/m², the minimum in 0.0001 cd/m². (Note: the DXGI docs
// describe MaxMasteringLuminance as 0.0001 cd/m², but IddCx/ST 2086 provide
// it in whole cd/m².)
uint32_t hdrMaxDisplayLuminance; // Max mastering display luminance (cd/m²)
uint32_t hdrMinDisplayLuminance; // Min mastering display luminance (0.0001 cd/m²)
uint32_t hdrMaxContentLightLevel; // MaxCLL (cd/m²)
uint32_t hdrMaxFrameAverageLightLevel; // MaxFALL (cd/m²)
// Producer cadence identity. Phase feedback is permitted only when the
// matching frame has KVMFR_FRAME_TIMING_PHASE_VALID set.
uint32_t scheduleGeneration;
uint32_t scheduleEpoch;
uint32_t scheduleDeadlineSerial;
uint8_t hdrReserved[20];
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
}
KVMFRFrame;
#if defined(__cplusplus)
static_assert(offsetof(KVMFRFrame, captureTime) == 64,
"KVMFRFrame hot fields must fit in one cache line");
static_assert(offsetof(KVMFRFrame, holdTime) == 96,
"KVMFRFrame hold timing layout changed");
static_assert(offsetof(KVMFRFrame, readyLeadTime) == 104,
"KVMFRFrame ready lead timing layout changed");
static_assert(offsetof(KVMFRFrame, timingSerial) == 112,
"KVMFRFrame timing publication layout changed");
static_assert(offsetof(KVMFRFrame, timingFlags) == 120,
"KVMFRFrame timing flags layout changed");
static_assert(offsetof(KVMFRFrame, scheduleDeadlineSerial) == 168,
"KVMFRFrame schedule identity layout changed");
static_assert(offsetof(KVMFRFrame, hdrDisplayPrimary) == 128,
"KVMFRFrame HDR metadata must be cache-line aligned");
static_assert(offsetof(KVMFRFrame, damageRects) == 192,
"KVMFRFrame damage rectangles must be cache-line aligned");
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(offsetof(KVMFRFrame, captureTime) == 64,
"KVMFRFrame hot fields must fit in one cache line");
_Static_assert(offsetof(KVMFRFrame, holdTime) == 96,
"KVMFRFrame hold timing layout changed");
_Static_assert(offsetof(KVMFRFrame, readyLeadTime) == 104,
"KVMFRFrame ready lead timing layout changed");
_Static_assert(offsetof(KVMFRFrame, timingSerial) == 112,
"KVMFRFrame timing publication layout changed");
_Static_assert(offsetof(KVMFRFrame, timingFlags) == 120,
"KVMFRFrame timing flags layout changed");
_Static_assert(offsetof(KVMFRFrame, scheduleDeadlineSerial) == 168,
"KVMFRFrame schedule identity layout changed");
_Static_assert(offsetof(KVMFRFrame, hdrDisplayPrimary) == 128,
"KVMFRFrame HDR metadata must be cache-line aligned");
_Static_assert(offsetof(KVMFRFrame, damageRects) == 192,
"KVMFRFrame damage rectangles must be cache-line aligned");
#endif
typedef struct KVMFRMessage
{
KVMFRMessageType type;
}
KVMFRMessage;
typedef struct KVMFRSetCursorPos
{
KVMFRMessage msg;
int32_t x, y;
}
KVMFRSetCursorPos;
typedef struct KVMFRWindowSize
{
KVMFRMessage msg;
uint32_t w, h;
}
KVMFRWindowSize;
enum
{
KVMFR_FRAME_SCHEDULE_ACTIVE = 0x1,
KVMFR_FRAME_SCHEDULE_RELEASE = 0x2,
KVMFR_FRAME_SCHEDULE_RESET = 0x4,
KVMFR_FRAME_SCHEDULE_IMMEDIATE = 0x8
};
typedef uint32_t KVMFRFrameScheduleFlags;
/*
* Client presentation scheduling is expressed entirely as durations. Client
* and producer monotonic clocks have unrelated epochs and must never be
* compared directly.
*/
typedef struct KVMFRFrameSchedule
{
KVMFRMessage msg;
uint32_t clientID;
uint32_t generation;
KVMFRFrameScheduleFlags flags;
uint64_t period; // requested presentation period (ns)
uint64_t targetSlack; // desired ready-to-render lead (ns)
// Positive when the frame arrived early, negative when it arrived late.
int64_t phaseError; // ready-to-render phase error (ns)
uint32_t feedbackFrameSerial;
uint32_t feedbackScheduleEpoch;
// Identifies the exact producer deadline paired with feedbackFrameSerial.
uint32_t feedbackDeadlineSerial;
uint32_t lease; // lease duration (ms)
uint8_t reserved[8];
}
KVMFRFrameSchedule;
#if defined(__cplusplus)
static_assert(offsetof(KVMFRFrameSchedule, feedbackDeadlineSerial) == 48,
"KVMFR frame schedule feedback identity layout changed");
static_assert(offsetof(KVMFRFrameSchedule, lease) == 52,
"KVMFR frame schedule lease layout changed");
static_assert(sizeof(KVMFRFrameSchedule) == 64,
"KVMFR frame schedule must fit in one LGMP control message");
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(offsetof(KVMFRFrameSchedule, feedbackDeadlineSerial) == 48,
"KVMFR frame schedule feedback identity layout changed");
_Static_assert(offsetof(KVMFRFrameSchedule, lease) == 52,
"KVMFR frame schedule lease layout changed");
_Static_assert(sizeof(KVMFRFrameSchedule) == 64,
"KVMFR frame schedule must fit in one LGMP control message");
#endif
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif

View File

@@ -1,438 +0,0 @@
/**
* 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_COMMON_KVMFR_CLIPBOARD_
#define _H_LG_COMMON_KVMFR_CLIPBOARD_
#pragma once
#include "KVMFRStream.h"
#include <stddef.h>
#include <stdint.h>
#define KVMFR_CLIPBOARD_VERSION 4U
#define KVMFR_CLIPBOARD_STREAM_VERSION 1U
/* Physical stream geometry. Keep these independent from the maximum logical
* request and representation sizes below: one response may span several
* stream slots. The generic names are also used by the Helper/IDD clipboard
* ring, which intentionally has the same geometry. */
#define KVMFR_CLIPBOARD_STREAM_SLOT_COUNT 4U
#define KVMFR_CLIPBOARD_STREAM_SLOT_BYTES (256U * 1024U)
#define KVMFR_CLIPBOARD_STREAM_WINDOW_BYTES \
(KVMFR_CLIPBOARD_STREAM_SLOT_COUNT * \
KVMFR_CLIPBOARD_STREAM_SLOT_BYTES)
#define KVMFR_CLIPBOARD_SLOT_COUNT \
KVMFR_CLIPBOARD_STREAM_SLOT_COUNT
#define KVMFR_CLIPBOARD_DATA_BYTES \
KVMFR_CLIPBOARD_STREAM_SLOT_BYTES
/* Keep text and image chunks within the core X11 request limit. File data can
* use the full slot without passing through XChangeProperty. */
#define KVMFR_CLIPBOARD_REPRESENTATION_BYTES (64U * 1024U)
#define KVMFR_CLIPBOARD_SIZE_UNKNOWN UINT64_MAX
/* Logical request size. A READ response may require multiple physical
* stream records and must not be constrained to a single slot. */
#define KVMFR_CLIPBOARD_FILE_READ_BYTES (1024U * 1024U)
#define KVMFR_CLIPBOARD_FILE_ROOT_NODE UINT64_C(0)
#define KVMFR_CLIPBOARD_FILE_MAX_ACQUISITIONS 8U
#define KVMFR_CLIPBOARD_FILE_MAX_REQUESTS 32U
/* Transfer IDs are selected by the side which sends REQUEST. Keeping the
* namespaces disjoint makes a simultaneous transfer in each direction
* unambiguous when CANCEL and DATA cross in flight. */
#define KVMFR_CLIPBOARD_TRANSFER_HELPER (UINT64_C(1) << 63)
static inline int kvmfrClipboardTransferFromHelper(uint64_t transfer)
{
return (transfer & KVMFR_CLIPBOARD_TRANSFER_HELPER) != 0;
}
static inline int kvmfrClipboardTransferFromClient(uint64_t transfer)
{
return transfer != 0 && !kvmfrClipboardTransferFromHelper(transfer);
}
enum
{
KVMFR_CLIPBOARD_FORMAT_NONE = 0,
KVMFR_CLIPBOARD_FORMAT_TEXT = 1,
KVMFR_CLIPBOARD_FORMAT_PNG = 2,
KVMFR_CLIPBOARD_FORMAT_BMP = 3,
KVMFR_CLIPBOARD_FORMAT_TIFF = 4,
KVMFR_CLIPBOARD_FORMAT_JPEG = 5,
KVMFR_CLIPBOARD_FORMAT_FILES = 6
};
typedef uint32_t KVMFRClipboardFormat;
enum
{
KVMFR_CLIPBOARD_FORMAT_MASK_TEXT = 1U << 0,
KVMFR_CLIPBOARD_FORMAT_MASK_PNG = 1U << 1,
KVMFR_CLIPBOARD_FORMAT_MASK_BMP = 1U << 2,
KVMFR_CLIPBOARD_FORMAT_MASK_TIFF = 1U << 3,
KVMFR_CLIPBOARD_FORMAT_MASK_JPEG = 1U << 4,
KVMFR_CLIPBOARD_FORMAT_MASK_FILES = 1U << 5,
KVMFR_CLIPBOARD_FORMAT_MASK_ALL =
KVMFR_CLIPBOARD_FORMAT_MASK_TEXT |
KVMFR_CLIPBOARD_FORMAT_MASK_PNG |
KVMFR_CLIPBOARD_FORMAT_MASK_BMP |
KVMFR_CLIPBOARD_FORMAT_MASK_TIFF |
KVMFR_CLIPBOARD_FORMAT_MASK_JPEG |
KVMFR_CLIPBOARD_FORMAT_MASK_FILES,
};
typedef uint32_t KVMFRClipboardFormatFlags;
static inline int kvmfrClipboardFormatValid(KVMFRClipboardFormat format)
{
return format >= KVMFR_CLIPBOARD_FORMAT_TEXT &&
format <= KVMFR_CLIPBOARD_FORMAT_FILES;
}
static inline int kvmfrClipboardRepresentationFormatValid(
KVMFRClipboardFormat format)
{
return format >= KVMFR_CLIPBOARD_FORMAT_TEXT &&
format <= KVMFR_CLIPBOARD_FORMAT_JPEG;
}
static inline KVMFRClipboardFormatFlags kvmfrClipboardFormatFlag(
KVMFRClipboardFormat format)
{
return kvmfrClipboardFormatValid(format) ?
(KVMFRClipboardFormatFlags)(1U << (format - 1U)) : 0U;
}
enum
{
KVMFR_CLIPBOARD_MESSAGE_CLAIM = 1,
KVMFR_CLIPBOARD_MESSAGE_RELEASE = 2,
KVMFR_CLIPBOARD_MESSAGE_KEEPALIVE = 3,
KVMFR_CLIPBOARD_MESSAGE_OFFER = 4,
KVMFR_CLIPBOARD_MESSAGE_CLEAR = 5,
KVMFR_CLIPBOARD_MESSAGE_REQUEST = 6,
KVMFR_CLIPBOARD_MESSAGE_DATA = 7,
KVMFR_CLIPBOARD_MESSAGE_CANCEL = 8,
KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE = 9,
KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED = 10,
KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE = 11,
KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST = 12,
KVMFR_CLIPBOARD_MESSAGE_FILE_DATA = 13,
KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL = 14
};
typedef uint32_t KVMFRClipboardMessageType;
enum
{
KVMFR_CLIPBOARD_FLAG_BEGIN = 1U << 0,
KVMFR_CLIPBOARD_FLAG_END = 1U << 1
};
typedef uint32_t KVMFRClipboardFlags;
/*
* Bidirectional control record. Client-to-host records must fit LGMP's
* 64-byte client message area. Host-to-client records use a small payload
* pool and the same layout. DATA and FILE_DATA records are carried only by
* the duplex streams advertised in KVMFRClipboardStatus; all other records
* use the clipboard queue. CLAIM flags are reserved and must be zero.
*/
typedef struct KVMFRClipboardMessage
{
uint32_t version;
KVMFRClipboardMessageType type;
uint32_t generation;
uint32_t sequence;
uint64_t clipboardGeneration;
uint64_t transfer;
uint64_t offset;
uint64_t size;
KVMFRClipboardFormat format;
uint32_t flags;
uint32_t token;
uint32_t length;
}
KVMFRClipboardMessage;
enum
{
KVMFR_CLIPBOARD_FILE_OP_LIST = 1,
KVMFR_CLIPBOARD_FILE_OP_READ = 2
};
typedef uint32_t KVMFRClipboardFileOperation;
enum
{
KVMFR_CLIPBOARD_FILE_TYPE_REGULAR = 1,
KVMFR_CLIPBOARD_FILE_TYPE_DIRECTORY = 2
};
typedef uint32_t KVMFRClipboardFileType;
enum
{
KVMFR_CLIPBOARD_FILE_ERROR_NONE = 0,
KVMFR_CLIPBOARD_FILE_ERROR_NOT_FOUND = 1,
KVMFR_CLIPBOARD_FILE_ERROR_ACCESS = 2,
KVMFR_CLIPBOARD_FILE_ERROR_NOT_DIRECTORY = 3,
KVMFR_CLIPBOARD_FILE_ERROR_IS_DIRECTORY = 4,
KVMFR_CLIPBOARD_FILE_ERROR_IO = 5,
KVMFR_CLIPBOARD_FILE_ERROR_INVALID = 6,
KVMFR_CLIPBOARD_FILE_ERROR_NO_MEMORY = 7,
KVMFR_CLIPBOARD_FILE_ERROR_NO_SPACE = 8,
KVMFR_CLIPBOARD_FILE_ERROR_DISCONNECTED = 9,
KVMFR_CLIPBOARD_FILE_ERROR_CANCELLED = 10,
KVMFR_CLIPBOARD_FILE_ERROR_NOT_SUPPORTED = 11,
KVMFR_CLIPBOARD_FILE_ERROR_STALE = 12
};
typedef uint32_t KVMFRClipboardFileError;
static inline int kvmfrClipboardFileErrorValid(
KVMFRClipboardFileError error)
{
return error <= KVMFR_CLIPBOARD_FILE_ERROR_STALE;
}
/*
* A LIST response is a stream of these headers, each immediately followed by
* nameLength bytes of an unescaped UTF-8 path component and then zero padding
* to the next eight-byte boundary. Node zero is the dataset's synthetic root
* and is never returned as an entry. Times are nanoseconds since the Unix
* epoch. A zero creation time means the source cannot report it.
*/
typedef struct KVMFRClipboardFileEntry
{
uint64_t node;
uint64_t size;
uint64_t createdNs;
uint64_t modifiedNs;
KVMFRClipboardFileType type;
uint32_t nameLength;
}
KVMFRClipboardFileEntry;
#define KVMFR_CLIPBOARD_FILE_ENTRY_ALIGN 8U
#define KVMFR_CLIPBOARD_FILE_ENTRY_BYTES(nameLength) \
((uint64_t)(sizeof(KVMFRClipboardFileEntry) + (uint64_t)(nameLength) + \
(KVMFR_CLIPBOARD_FILE_ENTRY_ALIGN - 1U)) & \
~(uint64_t)(KVMFR_CLIPBOARD_FILE_ENTRY_ALIGN - 1U))
/* File message field use:
*
* FILE_ACQUIRE / FILE_ACQUIRED / FILE_RELEASE:
* clipboardGeneration identifies the immutable dataset and transfer is the
* acquisition ID selected by the requester. format is FILES. ACQUIRED token
* is a KVMFRClipboardFileError; all other fields are zero.
*
* FILE_REQUEST:
* clipboardGeneration identifies the acquired dataset, transfer is a unique
* request ID, size is the opaque node ID, token is LIST or READ, and format
* is FILES. LIST requires offset and flags to be zero. READ uses offset as
* the file offset and flags as the requested byte count (at most
* KVMFR_CLIPBOARD_FILE_READ_BYTES). length is always zero because REQUEST
* has no attached payload.
*
* FILE_DATA:
* clipboardGeneration, transfer, format and token echo FILE_REQUEST. offset
* is relative to this response (not the source file), sequence starts at
* zero, and size follows DATA's BEGIN/END total-size rules. Chunks for
* different request IDs may be interleaved.
*
* FILE_CANCEL:
* clipboardGeneration and transfer identify an acquisition or request,
* format is FILES, and token is a non-zero KVMFRClipboardFileError.
*/
static inline int kvmfrClipboardFileOperationValid(
KVMFRClipboardFileOperation operation)
{
return operation == KVMFR_CLIPBOARD_FILE_OP_LIST ||
operation == KVMFR_CLIPBOARD_FILE_OP_READ;
}
static inline int kvmfrClipboardFileTransferValid(uint64_t transfer)
{
return transfer != 0;
}
static inline int kvmfrClipboardFileMessageType(
KVMFRClipboardMessageType type)
{
return type >= KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE &&
type <= KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL;
}
static inline int kvmfrClipboardFileMessageValid(
const KVMFRClipboardMessage * message)
{
if (!message || message->version != KVMFR_CLIPBOARD_VERSION ||
!message->clipboardGeneration ||
!kvmfrClipboardFileTransferValid(message->transfer) ||
message->format != KVMFR_CLIPBOARD_FORMAT_FILES ||
message->length > KVMFR_CLIPBOARD_DATA_BYTES)
return 0;
switch (message->type)
{
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRE:
case KVMFR_CLIPBOARD_MESSAGE_FILE_RELEASE:
return !message->sequence && !message->offset && !message->size &&
!message->flags && !message->token && !message->length;
case KVMFR_CLIPBOARD_MESSAGE_FILE_ACQUIRED:
return !message->sequence && !message->offset && !message->size &&
!message->flags &&
kvmfrClipboardFileErrorValid(message->token) && !message->length;
case KVMFR_CLIPBOARD_MESSAGE_FILE_REQUEST:
if (message->sequence ||
!kvmfrClipboardFileOperationValid(message->token) ||
message->length ||
(message->token == KVMFR_CLIPBOARD_FILE_OP_READ &&
!message->size))
return 0;
if (message->token == KVMFR_CLIPBOARD_FILE_OP_LIST)
return !message->offset && !message->flags;
return message->flags &&
message->flags <= KVMFR_CLIPBOARD_FILE_READ_BYTES &&
message->offset <= UINT64_MAX - message->flags;
case KVMFR_CLIPBOARD_MESSAGE_FILE_DATA:
{
if (!kvmfrClipboardFileOperationValid(message->token) ||
(message->flags & ~(KVMFR_CLIPBOARD_FLAG_BEGIN |
KVMFR_CLIPBOARD_FLAG_END)) ||
(!message->length &&
!(message->flags & KVMFR_CLIPBOARD_FLAG_END)) ||
message->offset > UINT64_MAX - message->length)
return 0;
const uint64_t end = message->offset + message->length;
if (message->flags & KVMFR_CLIPBOARD_FLAG_END)
return message->size == end;
if (!(message->flags & KVMFR_CLIPBOARD_FLAG_BEGIN))
return message->size == KVMFR_CLIPBOARD_SIZE_UNKNOWN;
return message->size == KVMFR_CLIPBOARD_SIZE_UNKNOWN ||
message->size >= end;
}
case KVMFR_CLIPBOARD_MESSAGE_FILE_CANCEL:
return !message->sequence && !message->offset && !message->size &&
!message->flags && message->token &&
kvmfrClipboardFileErrorValid(message->token) && !message->length;
default:
return 0;
}
}
/* Type-specific fields:
* CLAIM: token is the endpoint generation from the latest status.
* OFFER: token is KVMFRClipboardFormatFlags.
* REQUEST: format and transfer identify the requested representation.
* DATA: size is an optional total hint on BEGIN and authoritative on END;
* offset/length describe this record's borrowed payload.
* CANCEL: token is a KVMFRClipboardCancelReason-compatible reason. */
enum
{
KVMFR_CLIPBOARD_STATUS_AVAILABLE = 1U << 0,
KVMFR_CLIPBOARD_STATUS_HAS_OWNER = 1U << 1
};
typedef uint32_t KVMFRClipboardStatusFlags;
/* The duplex stream descriptors are always valid. Their slot size includes
* KVMFRClipboardSlotHeader as well as slotBytes of payload. */
typedef struct KVMFRClipboardStatus
{
uint32_t version;
KVMFRClipboardStatusFlags flags;
uint32_t generation;
uint32_t ownerClientID;
uint32_t ownerGeneration;
uint32_t lease;
KVMFRClipboardFormatFlags formats;
uint32_t slotBytes;
uint32_t streamVersion;
uint32_t streamSlotCount;
uint32_t reserved[6];
KVMFRStreamDescriptor hostToClient;
KVMFRStreamDescriptor clientToHost;
}
KVMFRClipboardStatus;
/* The data immediately following this header is length bytes long. */
typedef KVMFRClipboardMessage KVMFRClipboardSlotHeader;
#define KVMFR_CLIPBOARD_STREAM_RECORD_BYTES \
(sizeof(KVMFRClipboardSlotHeader) + KVMFR_CLIPBOARD_STREAM_SLOT_BYTES)
enum
{
KVMFR_CLIPBOARD_QUEUE_STATUS = 1,
KVMFR_CLIPBOARD_QUEUE_MESSAGE = 2
};
typedef uint32_t KVMFRClipboardQueueType;
#define KVMFR_CLIPBOARD_QUEUE_UDATA(type, serial) \
((((uint64_t)(type)) << 32) | (uint32_t)(serial))
#define KVMFR_CLIPBOARD_QUEUE_TYPE(udata) \
((KVMFRClipboardQueueType)((uint64_t)(udata) >> 32))
#define KVMFR_CLIPBOARD_QUEUE_SERIAL(udata) ((uint32_t)(udata))
#if defined(__cplusplus)
static_assert(sizeof(KVMFRClipboardMessage) == 64,
"KVMFR clipboard control message layout changed");
static_assert(sizeof(KVMFRClipboardFileEntry) == 40,
"KVMFR clipboard file entry layout changed");
static_assert(offsetof(KVMFRClipboardStatus, streamVersion) == 32,
"KVMFR clipboard stream status layout changed");
static_assert(offsetof(KVMFRClipboardStatus, hostToClient) == 64,
"KVMFR clipboard stream discovery layout changed");
static_assert(sizeof(KVMFRClipboardStatus) == 128,
"KVMFR clipboard status layout changed");
static_assert(sizeof(KVMFRClipboardSlotHeader) == 64,
"KVMFR clipboard slot header layout changed");
static_assert(sizeof(KVMFRClipboardMessage) <= 64,
"KVMFR clipboard message must fit one LGMP control message");
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(KVMFRClipboardMessage) == 64,
"KVMFR clipboard control message layout changed");
_Static_assert(sizeof(KVMFRClipboardFileEntry) == 40,
"KVMFR clipboard file entry layout changed");
_Static_assert(offsetof(KVMFRClipboardStatus, streamVersion) == 32,
"KVMFR clipboard stream status layout changed");
_Static_assert(offsetof(KVMFRClipboardStatus, hostToClient) == 64,
"KVMFR clipboard stream discovery layout changed");
_Static_assert(sizeof(KVMFRClipboardStatus) == 128,
"KVMFR clipboard status layout changed");
_Static_assert(sizeof(KVMFRClipboardSlotHeader) == 64,
"KVMFR clipboard slot header layout changed");
_Static_assert(sizeof(KVMFRClipboardMessage) <= 64,
"KVMFR clipboard message must fit one LGMP control message");
#endif
#endif

View File

@@ -1,246 +0,0 @@
/**
* 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_COMMON_KVMFR_INPUT_
#define _H_LG_COMMON_KVMFR_INPUT_
#pragma once
#include "KVMFRStream.h"
#include <stddef.h>
#include <stdint.h>
#define KVMFR_INPUT_VERSION 3
#define KVMFR_INPUT_KEYBOARD_LEDS_VERSION 3
#define KVMFR_INPUT_STREAM_VERSION 1
#define KVMFR_INPUT_STREAM_ENDPOINT_COUNT 8
#define KVMFR_INPUT_STREAM_SLOT_COUNT 128
#define KVMFR_INPUT_STREAM_SLOT_SIZE 64
#define KVMFR_INPUT_MOUSE_BUTTON_COUNT 32
#define KVMFR_INPUT_MOUSE_ABSOLUTE_MAX 32767
#define KVMFR_INPUT_KEYBOARD_KEY_COUNT 6
#define KVMFR_INPUT_KEYBOARD_USAGE_MAX 0xe7
typedef uint32_t KVMFRInputMouseButtons;
/*
* Bits 0..31 correspond to USB HID mouse button usages 1..32. Wheel motion
* is carried separately and must not be represented as a button.
*/
typedef struct KVMFRInputMouseRelative
{
KVMFRInputMouseButtons buttons;
int32_t deltaX;
int32_t deltaY;
int32_t wheel;
}
KVMFRInputMouseRelative;
typedef struct KVMFRInputMouseAbsolute
{
KVMFRInputMouseButtons buttons;
uint16_t x;
uint16_t y;
int32_t wheel;
uint32_t reserved;
}
KVMFRInputMouseAbsolute;
typedef struct KVMFRInputKeyboard
{
uint8_t modifiers;
uint8_t keys[KVMFR_INPUT_KEYBOARD_KEY_COUNT];
uint8_t reserved[9];
}
KVMFRInputKeyboard;
typedef union KVMFRInputPayload
{
KVMFRInputMouseRelative mouseRelative;
KVMFRInputMouseAbsolute mouseAbsolute;
KVMFRInputKeyboard keyboard;
uint8_t reserved[16];
}
KVMFRInputPayload;
enum
{
KVMFR_INPUT_MESSAGE_CLAIM = 1,
KVMFR_INPUT_MESSAGE_RELEASE = 2,
KVMFR_INPUT_MESSAGE_KEEPALIVE = 3,
KVMFR_INPUT_MESSAGE_RESET = 4,
KVMFR_INPUT_MESSAGE_MOUSE_RELATIVE = 5,
KVMFR_INPUT_MESSAGE_MOUSE_ABSOLUTE = 6,
KVMFR_INPUT_MESSAGE_KEYBOARD = 7
};
typedef uint32_t KVMFRInputMessageType;
/*
* Input messages are carried by the client's ordered stream endpoint. CLAIM
* is sequence one, reports, KEEPALIVE and RESET increment the sequence, and
* RELEASE is the final record.
*/
typedef struct KVMFRInputMessage
{
KVMFRInputMessageType type;
// Non-zero client input activation generation.
uint32_t generation;
// Non-zero ordered sequence within generation, beginning at one.
uint32_t sequence;
// Must be zero.
uint32_t reserved;
KVMFRInputPayload payload;
}
KVMFRInputMessage;
enum
{
KVMFR_INPUT_CAP_MOUSE_RELATIVE = 0x1,
KVMFR_INPUT_CAP_MOUSE_ABSOLUTE = 0x2,
KVMFR_INPUT_CAP_KEYBOARD = 0x4
};
typedef uint32_t KVMFRInputCapabilityFlags;
enum
{
KVMFR_INPUT_KEYBOARD_LED_NUM_LOCK = 0x1,
KVMFR_INPUT_KEYBOARD_LED_CAPS_LOCK = 0x2,
KVMFR_INPUT_KEYBOARD_LED_SCROLL_LOCK = 0x4,
KVMFR_INPUT_KEYBOARD_LED_COMPOSE = 0x8,
KVMFR_INPUT_KEYBOARD_LED_KANA = 0x10,
};
typedef uint8_t KVMFRInputKeyboardLEDFlags;
enum
{
KVMFR_INPUT_STATUS_AVAILABLE = 0x1,
KVMFR_INPUT_STATUS_HAS_OWNER = 0x2,
KVMFR_INPUT_STATUS_KEYBOARD_LEDS_VALID = 0x4,
};
typedef uint32_t KVMFRInputStatusFlags;
enum
{
KVMFR_INPUT_STREAM_ENDPOINT_AVAILABLE = 0x1,
KVMFR_INPUT_STREAM_ENDPOINT_BOUND = 0x2,
};
typedef uint32_t KVMFRInputStreamEndpointFlags;
/*
* A bound endpoint is usable only by boundClientID for bindingGeneration. The
* host changes bindingGeneration before reusing an endpoint so stale writers
* can never be accepted as a new client.
*/
typedef struct KVMFRInputStreamEndpoint
{
KVMFRStreamDescriptor stream;
uint32_t boundClientID;
uint32_t bindingGeneration;
KVMFRInputStreamEndpointFlags flags;
uint32_t reserved;
}
KVMFRInputStreamEndpoint;
/*
* Published through the LGMP input queue for status and stream discovery.
* KVMFRInputMessage records are carried only by the bound stream endpoint.
*/
typedef struct KVMFRInputStatus
{
uint32_t version;
KVMFRInputCapabilityFlags capabilities;
KVMFRInputStatusFlags flags;
uint32_t generation;
uint32_t ownerClientID;
uint32_t ownerGeneration;
// Input ownership lease duration in milliseconds.
uint32_t lease;
uint32_t maxButtons;
uint32_t streamVersion;
uint32_t streamEndpointCount;
uint32_t streamGeneration;
// USB HID keyboard LED bits; valid only when the status flag is set.
uint8_t keyboardLEDs;
uint8_t statusReserved[3];
uint32_t streamReserved[4];
KVMFRInputStreamEndpoint streamEndpoint[
KVMFR_INPUT_STREAM_ENDPOINT_COUNT];
}
KVMFRInputStatus;
#if defined(__cplusplus)
static_assert(sizeof(KVMFRInputMouseRelative) == 16,
"KVMFR relative mouse input layout changed");
static_assert(sizeof(KVMFRInputMouseAbsolute) == 16,
"KVMFR absolute mouse input layout changed");
static_assert(sizeof(KVMFRInputKeyboard) == 16,
"KVMFR keyboard input layout changed");
static_assert(sizeof(KVMFRInputPayload) == 16,
"KVMFR input payload layout changed");
static_assert(offsetof(KVMFRInputMessage, payload) == 16,
"KVMFR input message header layout changed");
static_assert(sizeof(KVMFRInputMessage) == 32,
"KVMFR input message layout changed");
static_assert(sizeof(KVMFRInputStreamEndpoint) == 48,
"KVMFR input stream endpoint layout changed");
static_assert(offsetof(KVMFRInputStatus, streamVersion) == 32,
"KVMFR input status stream layout changed");
static_assert(offsetof(KVMFRInputStatus, keyboardLEDs) == 44,
"KVMFR input status keyboard LED layout changed");
static_assert(offsetof(KVMFRInputStatus, streamEndpoint) == 64,
"KVMFR input stream discovery layout changed");
static_assert(sizeof(KVMFRInputStatus) == 448,
"KVMFR input status layout changed");
static_assert(sizeof(KVMFRInputMessage) <= KVMFR_INPUT_STREAM_SLOT_SIZE,
"KVMFR input message must fit in one stream record");
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(sizeof(KVMFRInputMouseRelative) == 16,
"KVMFR relative mouse input layout changed");
_Static_assert(sizeof(KVMFRInputMouseAbsolute) == 16,
"KVMFR absolute mouse input layout changed");
_Static_assert(sizeof(KVMFRInputKeyboard) == 16,
"KVMFR keyboard input layout changed");
_Static_assert(sizeof(KVMFRInputPayload) == 16,
"KVMFR input payload layout changed");
_Static_assert(offsetof(KVMFRInputMessage, payload) == 16,
"KVMFR input message header layout changed");
_Static_assert(sizeof(KVMFRInputMessage) == 32,
"KVMFR input message layout changed");
_Static_assert(sizeof(KVMFRInputStreamEndpoint) == 48,
"KVMFR input stream endpoint layout changed");
_Static_assert(offsetof(KVMFRInputStatus, streamVersion) == 32,
"KVMFR input status stream layout changed");
_Static_assert(offsetof(KVMFRInputStatus, keyboardLEDs) == 44,
"KVMFR input status keyboard LED layout changed");
_Static_assert(offsetof(KVMFRInputStatus, streamEndpoint) == 64,
"KVMFR input stream discovery layout changed");
_Static_assert(sizeof(KVMFRInputStatus) == 448,
"KVMFR input status layout changed");
_Static_assert(sizeof(KVMFRInputMessage) <= KVMFR_INPUT_STREAM_SLOT_SIZE,
"KVMFR input message must fit in one stream record");
#endif
#endif

View File

@@ -1,263 +0,0 @@
/**
* 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_COMMON_KVMFR_RECOVERY_
#define _H_LG_COMMON_KVMFR_RECOVERY_
#pragma once
#include <stddef.h>
#include <stdint.h>
#define KVMFR_R_MAGIC "KVMFRRCV"
#define KVMFR_R_VERSION 1U
#define KVMFR_R_READY 1U
#define KVMFR_R_REGION_SIZE 65536U
#define KVMFR_R_LINE_SIZE 64U
#define KVMFR_R_HEARTBEAT_MS 250U
#define KVMFR_R_REQ_SLOTS 16U
enum
{
KVMFR_R_CAP_DISPLAY = 0x1U
};
enum
{
KVMFR_R_REQ_WRITING = 0x1U,
KVMFR_R_REQ_FIRST = 0x2U
};
enum
{
KVMFR_R_REQ_NONE = 0,
KVMFR_R_REQ_NORMAL = 1,
KVMFR_R_REQ_RECOVERY = 2
};
enum
{
KVMFR_R_STATE_UNKNOWN = 0,
KVMFR_R_STATE_NORMAL = 1,
KVMFR_R_STATE_SWITCHING = 2,
KVMFR_R_STATE_ACTIVE = 3,
KVMFR_R_STATE_FAILED = 4
};
enum
{
KVMFR_R_ERR_NONE = 0,
KVMFR_R_ERR_UNSUPPORTED = 1,
KVMFR_R_ERR_HELPER_UNAVAILABLE = 2,
KVMFR_R_ERR_TOPOLOGY_FAILED = 3,
KVMFR_R_ERR_NO_FALLBACK_DISPLAY = 4,
KVMFR_R_ERR_BUSY = 5,
KVMFR_R_ERR_CAPACITY = 6
};
/*
* The recovery region is outside LGMP and remains stable across LGMP and
* KVMFR protocol changes. Header, information, and status lines are
* producer-owned. Request lines are client-owned except that the producer
* atomically clears a completed slot's serial. Shared fields are plain
* fixed-width values; synchronization is supplied externally rather than
* embedded in the wire layout.
*/
typedef struct KVMFRRHeader
{
char magic[8];
uint16_t abiVersion;
uint16_t structSize;
uint32_t capabilities;
uint32_t lgmpVersion;
uint32_t kvmfrVersion;
uint64_t session;
uint8_t uuid[16];
uint32_t heartbeat;
uint32_t reserved[2];
uint32_t ready;
}
KVMFRRHeader;
/*
* Initialized by the IDD before publishing KVMFRRHeader::ready with
* release ordering. Clients acquire ready before reading either producer
* line. The heartbeat is an independently published 32-bit counter. The
* version string is NUL-terminated.
*/
typedef struct KVMFRRInfo
{
char version[48];
uint8_t reserved[16];
}
KVMFRRInfo;
/*
* Written by clients. Ticket is an even monotonic counter used to allocate a
* unique request serial. Zero is skipped when it wraps.
*/
typedef struct KVMFRRReqHead
{
uint32_t ticket;
uint8_t reserved[60];
}
KVMFRRReqHead;
/*
* Written by clients. A writer claims an empty slot by changing serial from
* zero to its odd ticket, writes the payload, then release-publishes the even
* ticket. The IDD ignores odd slots and atomically clears completed even
* slots. An interrupted writer only consumes its own slot and cannot block or
* corrupt another writer. Session rejects requests from an old producer
* instance.
*/
typedef struct KVMFRRRequest
{
uint32_t serial;
uint32_t request;
uint64_t session;
uint8_t reserved[48];
}
KVMFRRRequest;
/*
* Written only by the IDD. Serial is an independent publication generation:
* the IDD makes it odd before changing the payload, then publishes the next
* nonzero even value with release ordering. Clients use it to take a coherent
* snapshot. AckSerial identifies the request being acknowledged and does not
* provide publication ordering because it remains unchanged as a request
* moves through states.
*/
typedef struct KVMFRRStatus
{
uint32_t ackSerial;
uint32_t ackRequest;
uint32_t state;
uint32_t error;
uint64_t session;
uint32_t serial;
uint8_t reserved[36];
}
KVMFRRStatus;
typedef struct KVMFRR
{
KVMFRRHeader header;
KVMFRRInfo info;
KVMFRRReqHead req;
KVMFRRRequest requests[KVMFR_R_REQ_SLOTS];
KVMFRRStatus status;
}
KVMFRR;
#if defined(__cplusplus)
static_assert(KVMFR_R_REGION_SIZE % KVMFR_R_LINE_SIZE == 0,
"KVMFR recovery region must contain whole cache lines");
static_assert(sizeof(KVMFRRHeader) == KVMFR_R_LINE_SIZE,
"KVMFR recovery header must occupy one cache line");
static_assert(offsetof(KVMFRRHeader, lgmpVersion) == 16,
"KVMFR recovery protocol version layout changed");
static_assert(offsetof(KVMFRRHeader, session) == 24,
"KVMFR recovery session layout changed");
static_assert(offsetof(KVMFRRHeader, uuid) == 32,
"KVMFR recovery UUID layout changed");
static_assert(offsetof(KVMFRRHeader, heartbeat) == 48,
"KVMFR recovery heartbeat layout changed");
static_assert(offsetof(KVMFRRHeader, ready) == 60,
"KVMFR recovery publication layout changed");
static_assert(sizeof(KVMFRRInfo) == KVMFR_R_LINE_SIZE,
"KVMFR recovery producer information must occupy one cache line");
static_assert(sizeof(KVMFRRReqHead) == KVMFR_R_LINE_SIZE,
"KVMFR recovery request header must occupy one cache line");
static_assert(sizeof(KVMFRRRequest) == KVMFR_R_LINE_SIZE,
"KVMFR recovery request must occupy one cache line");
static_assert(offsetof(KVMFRRRequest, session) == 8,
"KVMFR recovery request session layout changed");
static_assert(offsetof(KVMFRRRequest, request) == 4,
"KVMFR recovery request publication layout changed");
static_assert(sizeof(KVMFRRStatus) == KVMFR_R_LINE_SIZE,
"KVMFR recovery status must occupy one cache line");
static_assert(offsetof(KVMFRRStatus, session) == 16,
"KVMFR recovery status session layout changed");
static_assert(offsetof(KVMFRRStatus, serial) == 24,
"KVMFR recovery status publication layout changed");
static_assert(offsetof(KVMFRR, info) == KVMFR_R_LINE_SIZE,
"KVMFR recovery producer information must be cache-line aligned");
static_assert(offsetof(KVMFRR, req) == KVMFR_R_LINE_SIZE * 2,
"KVMFR recovery request header must be cache-line aligned");
static_assert(offsetof(KVMFRR, requests) == KVMFR_R_LINE_SIZE * 3,
"KVMFR recovery request must be cache-line aligned");
static_assert(offsetof(KVMFRR, status) ==
KVMFR_R_LINE_SIZE * (3 + KVMFR_R_REQ_SLOTS),
"KVMFR recovery status must be cache-line aligned");
static_assert(sizeof(KVMFRR) ==
KVMFR_R_LINE_SIZE * (4 + KVMFR_R_REQ_SLOTS),
"KVMFR recovery layout changed");
static_assert(sizeof(KVMFRR) <= KVMFR_R_REGION_SIZE,
"KVMFR recovery data must fit in its reserved region");
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(KVMFR_R_REGION_SIZE % KVMFR_R_LINE_SIZE == 0,
"KVMFR recovery region must contain whole cache lines");
_Static_assert(sizeof(KVMFRRHeader) == KVMFR_R_LINE_SIZE,
"KVMFR recovery header must occupy one cache line");
_Static_assert(offsetof(KVMFRRHeader, lgmpVersion) == 16,
"KVMFR recovery protocol version layout changed");
_Static_assert(offsetof(KVMFRRHeader, session) == 24,
"KVMFR recovery session layout changed");
_Static_assert(offsetof(KVMFRRHeader, uuid) == 32,
"KVMFR recovery UUID layout changed");
_Static_assert(offsetof(KVMFRRHeader, heartbeat) == 48,
"KVMFR recovery heartbeat layout changed");
_Static_assert(offsetof(KVMFRRHeader, ready) == 60,
"KVMFR recovery publication layout changed");
_Static_assert(sizeof(KVMFRRInfo) == KVMFR_R_LINE_SIZE,
"KVMFR recovery producer information must occupy one cache line");
_Static_assert(sizeof(KVMFRRReqHead) == KVMFR_R_LINE_SIZE,
"KVMFR recovery request header must occupy one cache line");
_Static_assert(sizeof(KVMFRRRequest) == KVMFR_R_LINE_SIZE,
"KVMFR recovery request must occupy one cache line");
_Static_assert(offsetof(KVMFRRRequest, session) == 8,
"KVMFR recovery request session layout changed");
_Static_assert(offsetof(KVMFRRRequest, request) == 4,
"KVMFR recovery request publication layout changed");
_Static_assert(sizeof(KVMFRRStatus) == KVMFR_R_LINE_SIZE,
"KVMFR recovery status must occupy one cache line");
_Static_assert(offsetof(KVMFRRStatus, session) == 16,
"KVMFR recovery status session layout changed");
_Static_assert(offsetof(KVMFRRStatus, serial) == 24,
"KVMFR recovery status publication layout changed");
_Static_assert(offsetof(KVMFRR, info) == KVMFR_R_LINE_SIZE,
"KVMFR recovery producer information must be cache-line aligned");
_Static_assert(offsetof(KVMFRR, req) == KVMFR_R_LINE_SIZE * 2,
"KVMFR recovery request header must be cache-line aligned");
_Static_assert(offsetof(KVMFRR, requests) == KVMFR_R_LINE_SIZE * 3,
"KVMFR recovery request must be cache-line aligned");
_Static_assert(offsetof(KVMFRR, status) ==
KVMFR_R_LINE_SIZE * (3 + KVMFR_R_REQ_SLOTS),
"KVMFR recovery status must be cache-line aligned");
_Static_assert(sizeof(KVMFRR) ==
KVMFR_R_LINE_SIZE * (4 + KVMFR_R_REQ_SLOTS),
"KVMFR recovery layout changed");
_Static_assert(sizeof(KVMFRR) <= KVMFR_R_REGION_SIZE,
"KVMFR recovery data must fit in its reserved region");
#endif
#endif

View File

@@ -1,68 +0,0 @@
/**
* 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_COMMON_KVMFR_STREAM_
#define _H_LG_COMMON_KVMFR_STREAM_
#pragma once
#include <stddef.h>
#include <stdint.h>
/*
* Transport-neutral wire copy of an exported LGMP stream descriptor. Keep
* common protocol headers independent of LGMP; the LGMP transports convert
* this structure field by field at their integration boundary.
*/
typedef struct KVMFRStreamDescriptor
{
uint32_t magic;
uint16_t version;
uint16_t size;
uint32_t offset;
uint32_t regionSize;
uint32_t direction;
uint32_t policy;
uint32_t slotCount;
uint32_t slotSize;
}
KVMFRStreamDescriptor;
#if defined(__cplusplus)
static_assert(offsetof(KVMFRStreamDescriptor, magic) == 0,
"KVMFR stream descriptor magic layout changed");
static_assert(offsetof(KVMFRStreamDescriptor, offset) == 8,
"KVMFR stream descriptor offset layout changed");
static_assert(offsetof(KVMFRStreamDescriptor, slotSize) == 28,
"KVMFR stream descriptor geometry layout changed");
static_assert(sizeof(KVMFRStreamDescriptor) == 32,
"KVMFR stream descriptor layout changed");
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
_Static_assert(offsetof(KVMFRStreamDescriptor, magic) == 0,
"KVMFR stream descriptor magic layout changed");
_Static_assert(offsetof(KVMFRStreamDescriptor, offset) == 8,
"KVMFR stream descriptor offset layout changed");
_Static_assert(offsetof(KVMFRStreamDescriptor, slotSize) == 28,
"KVMFR stream descriptor geometry layout changed");
_Static_assert(sizeof(KVMFRStreamDescriptor) == 32,
"KVMFR stream descriptor layout changed");
#endif
#endif

View File

@@ -1,39 +0,0 @@
/**
* 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_COMMON_LGMP_CONFIG_
#define _H_LG_COMMON_LGMP_CONFIG_
#define LGMP_Q_POINTER 1
#define LGMP_Q_FRAME 2
// Base ID for LGMP_Q_FRAME_LEN independent owner-delivery queues.
#define LGMP_Q_FRAME_OWNER 3
#define LGMP_Q_INPUT 5
#define LGMP_Q_CLIPBOARD 6
// Two delivery lanes plus a spare buffer let the timing owner continue to
// alternate buffers while a secondary client holds the shared delivery.
#define LGMP_Q_FRAME_LEN 2
#define LGMP_Q_FRAME_BUFFER_LEN 3
#define LGMP_Q_POINTER_LEN 32
#define LGMP_Q_INPUT_LEN 4
#define LGMP_Q_CLIPBOARD_LEN 32
#endif

View File

@@ -24,86 +24,80 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdatomic.h>
#include <LGProtocol/KVMFR.h>
#define FB_CHUNK_SIZE 1048576 // 1MB
#define FB_SPIN_LIMIT 10000 // 10ms
#define FB_WP_TYPE atomic_uint_least32_t
#define FB_WP_SIZE sizeof(FB_WP_TYPE)
typedef struct stFrameBuffer
{
FB_WP_TYPE wp;
uint8_t data[0];
} FrameBuffer;
typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size);
/**
* Wait for the framebuffer to fill to the specified size
*/
bool framebuffer_wait(const FrameBuffer * frame, size_t size);
bool framebuffer_wait(const KVMFRFrameBuffer * frame, size_t size);
/**
* Wait for the framebuffer to fill to the specified size and accumulate the
* nanoseconds spent waiting for the producer in `waitTimeNs`.
*/
bool framebuffer_wait_timed(const FrameBuffer * frame, size_t size,
bool framebuffer_wait_timed(const KVMFRFrameBuffer * frame, size_t size,
uint64_t * waitTimeNs);
/**
* Read `size` bytes from the KVMFRFrame into the dst buffer
*/
bool framebuffer_read_linear(const FrameBuffer * frame, void * restrict dst,
size_t size);
bool framebuffer_read_linear(const KVMFRFrameBuffer * frame,
void * restrict dst, size_t size);
/**
* Read data from the KVMFRFrame into the dst buffer
*/
bool framebuffer_read(const FrameBuffer * frame, void * dst, size_t dstpitch,
size_t height, size_t width, size_t bpp, size_t pitch);
bool framebuffer_read(const KVMFRFrameBuffer * frame, void * dst,
size_t dstpitch, size_t height, size_t width, size_t bpp, size_t pitch);
/**
* Read data from the KVMFRFrame and accumulate the nanoseconds spent waiting
* for the producer in `waitTimeNs`.
*/
bool framebuffer_read_timed(const FrameBuffer * frame, void * dst,
bool framebuffer_read_timed(const KVMFRFrameBuffer * frame, void * dst,
size_t dstpitch, size_t height, size_t width, size_t bpp, size_t pitch,
uint64_t * waitTimeNs);
/**
* Read data from the KVMFRFrame using a callback
*/
bool framebuffer_read_fn(const FrameBuffer * frame, size_t height, size_t width,
size_t bpp, size_t pitch, FrameBufferReadFn fn, void * opaque);
bool framebuffer_read_fn(const KVMFRFrameBuffer * frame, size_t height,
size_t width, size_t bpp, size_t pitch, FrameBufferReadFn fn,
void * opaque);
/**
* Prepare the framebuffer for writing
*/
void framebuffer_prepare(FrameBuffer * frame);
void framebuffer_prepare(KVMFRFrameBuffer * frame);
/**
* Write data from the src buffer into the KVMFRFrame
*/
extern bool (*framebuffer_write)(FrameBuffer * frame,
extern bool (*framebuffer_write)(KVMFRFrameBuffer * frame,
const void * restrict src, size_t size);
/**
* Gets the underlying data buffer of the framebuffer.
* For custom read routines only.
*/
const uint8_t * framebuffer_get_buffer(const FrameBuffer * frame);
const uint8_t * framebuffer_get_buffer(const KVMFRFrameBuffer * frame);
/**
* Gets the underlying data buffer of the framebuffer.
* For custom write routines only.
*/
uint8_t * framebuffer_get_data(FrameBuffer * frame);
uint8_t * framebuffer_get_data(KVMFRFrameBuffer * frame);
/**
* Sets the write pointer of the framebuffer.
* For custom write routines only.
*/
void framebuffer_set_write_ptr(FrameBuffer * frame, size_t size);
void framebuffer_set_write_ptr(KVMFRFrameBuffer * frame, size_t size);
#endif

View File

@@ -33,21 +33,22 @@ extern void (*rectCopyUnaligned)(
void rectScaleOutward(struct Rect * rect, double scale);
bool rectsBufferToFramebuffer(const FrameDamageRect * rects, int count, int bpp,
FrameBuffer * frame, int dstPitch, int height,
bool rectsBufferToFramebuffer(const KVMFRFrameDamageRect * rects, int count,
int bpp, KVMFRFrameBuffer * frame, int dstPitch, int height,
const uint8_t * src, int srcPitch);
bool rectsFramebufferToBuffer(const FrameDamageRect * rects, int count, int bpp,
bool rectsFramebufferToBuffer(const KVMFRFrameDamageRect * rects, int count,
int bpp,
uint8_t * dst, int dstPitch, int height,
const FrameBuffer * frame, int srcPitch);
const KVMFRFrameBuffer * frame, int srcPitch);
/* As above, accumulating producer wait time in nanoseconds. */
bool rectsFramebufferToBufferTimed(const FrameDamageRect * rects,
bool rectsFramebufferToBufferTimed(const KVMFRFrameDamageRect * rects,
int count, int bpp,
uint8_t * dst, int dstPitch, int height,
const FrameBuffer * frame, int srcPitch, uint64_t * waitTimeNs);
const KVMFRFrameBuffer * frame, int srcPitch, uint64_t * waitTimeNs);
int rectsMergeOverlapping(FrameDamageRect * rects, int count);
int rectsRejectContained(FrameDamageRect * rects, int count);
int rectsMergeOverlapping(KVMFRFrameDamageRect * rects, int count);
int rectsRejectContained(KVMFRFrameDamageRect * rects, int count);
#endif

View File

@@ -23,6 +23,8 @@
#include <stdint.h>
#include <LGProtocol/KVMFRTypes.h>
typedef void (*LG_FrameReleaseFn)(void * opaque, uint64_t handle);
struct Point
@@ -50,67 +52,8 @@ struct Border
int left, top, right, bottom;
};
typedef enum FrameType
{
FRAME_TYPE_INVALID ,
FRAME_TYPE_BGRA , // BGRA interleaved: B,G,R,A 32bpp
FRAME_TYPE_RGBA , // RGBA interleaved: R,G,B,A 32bpp
FRAME_TYPE_RGBA10 , // RGBA interleaved: R,G,B,A 10,10,10,2 bpp
FRAME_TYPE_RGBA16F , // RGBA interleaved: R,G,B,A 16,16,16,16 bpp float
FRAME_TYPE_BGR_32 , // BGR 24-bpp in 32bpp
FRAME_TYPE_RGB_24 , // RGB 24-bpp
FRAME_TYPE_MAX , // sentinel value
}
FrameType;
typedef enum FrameRotation
{
FRAME_ROT_0,
FRAME_ROT_90,
FRAME_ROT_180,
FRAME_ROT_270
}
FrameRotation;
typedef struct FrameDamageRect
{
uint32_t x;
uint32_t y;
uint32_t width;
uint32_t height;
}
FrameDamageRect;
extern const char * FrameTypeStr[FRAME_TYPE_MAX];
typedef enum CursorType
{
CURSOR_TYPE_COLOR ,
CURSOR_TYPE_MONOCHROME ,
CURSOR_TYPE_MASKED_COLOR
}
CursorType;
enum
{
LG_COLOR_TRANSFORM_MATRIX = 0x1,
LG_COLOR_TRANSFORM_LUT = 0x2,
};
typedef uint32_t LGColorTransformFlags;
#define LG_SDR_WHITE_LEVEL_DEFAULT 203
#define LG_MAX_FRAME_DAMAGE_RECTS 64
typedef struct LGColorTransform
{
LGColorTransformFlags flags;
float matrix[3][4];
float scalar;
float lut[4096][4];
}
LGColorTransform;
typedef struct StringPair
{
const char * name;