/**
 * 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
