//
// Aspia Project
// Copyright (C) 2016-2024 Dmitry Chapyshev <dmitry@aspia.ru>
//
// 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 3 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, see <https://www.gnu.org/licenses/>.
//

syntax = "proto3";

option optimize_for = LITE_RUNTIME;

package proto;

message KeyEvent
{
    // The keyboard (Caps/Num) lock states.
    enum Flags
    {
        UNKNOWN  = 0;
        CAPSLOCK = 1;
        NUMLOCK  = 2;
        PRESSED  = 4;
    }

    uint32 usb_keycode = 1;
    uint32 flags = 2;
}

message TextEvent
{
    string text = 1;
}

message MouseEvent
{
    enum ButtonMask
    {
        EMPTY          = 0;
        LEFT_BUTTON    = 1;
        MIDDLE_BUTTON  = 2;
        RIGHT_BUTTON   = 4;
        WHEEL_UP       = 8;
        WHEEL_DOWN     = 16;
        BACK_BUTTON    = 32;
        FORWARD_BUTTON = 64;
    }

    uint32 mask = 1; // Button mask.
    int32 x = 2;     // x position.
    int32 y = 3;     // y position.
}

message TouchEventPoint
{
    // The ID for the touch point.
    uint32 id = 1;

    // The absolute position of the touch point. These values on-the-wire are host physical pixel
    // coordinates: the top-left monitor on the system always starts from (0, 0).
    float x = 2;
    float y = 3;

    // The size of the touch point, used to aid hit-testing. Scaled to match the size on host.
    float radius_x = 4;
    float radius_y = 5;

    // Angle in degrees from the y-axis of the touch point.
    float angle = 6;

    // The pressure of the touch point. The value should be in [0.0, 1.0].
    float pressure = 7;
}

message TouchEvent
{
    // A START event means that this event reports all the touch points that were just added, e.g. a
    // finger started touching the display.
    // A MOVE event means that the touch points that have been STARTed moved, e.g. multiple fingers
    // on the screen moved.
    // An END event means that the touch points that have been STARTed ended. e.g. a finger went off
    // the screen.
    // A CANCEL event means that the touch points that have been STARTed were canceled, e.g. a finger
    // went off the screen.
    // Cancel event is simlar to END but slighly different. For example, Android
    // MotionEvent's ACTION_CANCEL documentation mentions that a cancel should be
    // treated as an ACTION_UP (END) event but might not perform the exact same
    // actions as a normal ACTION_UP event.
    enum TouchEventType
    {
        TOUCH_POINT_UNKNOWN = 0;
        TOUCH_POINT_START   = 1;
        TOUCH_POINT_MOVE    = 2;
        TOUCH_POINT_END     = 3;
        TOUCH_POINT_CANCEL  = 4;
    };

    TouchEventType event_type = 1;

    // Only the changed touch points are added to this field.
    // Given the existing touch point APIs (e.g. Android and PPAPI)
    // for START, END, and CANCEL events the size of this field will typically be 1, but for MOVE
    // events it is likely to have multiple points.
    repeated TouchEventPoint touch_points = 2;
}

message ClipboardEvent
{
    string mime_type = 1;
    bytes data = 2;
}

message CursorShape
{
    enum Flags
    {
        UNKNOWN     = 0;
        RESET_CACHE = 64;
        CACHE       = 128;
    }

    // We use only the first byte in this field.
    // If bit 7 is set to 1, then bits 0-4 contain the cursor index in the cache.
    // If bit 7 is not set, then the cursor image is received.
    // If bit 6 is set to 1, then the command to reset the contents of the cache
    // is received, and bits 0-4 contain a new cache size.
    // Bit 5 is reserved.
    uint32 flags = 1;

    // Width, height (in screen pixels) of the cursor.
    int32 width  = 2;
    int32 height = 3;

    // X,Y coordinates (relative to upper-left corner) of the cursor hotspot.
    int32 hotspot_x = 4;
    int32 hotspot_y = 5;

    // Cursor pixmap data in 32-bit BGRA format compressed with Zstd.
    bytes data = 6;

    // Screen DPI for current cursor.
    int32 dpi_x = 7;
    int32 dpi_y = 8;
}

message CursorPosition
{
    int32 x = 1;
    int32 y = 2;
}

message Size
{
    int32 width  = 1;
    int32 height = 2;
}

message Rect
{
    int32 x      = 1;
    int32 y      = 2;
    int32 width  = 3;
    int32 height = 4;
}

message PixelFormat
{
    uint32 bits_per_pixel = 1;

    uint32 red_max   = 2;
    uint32 green_max = 3;
    uint32 blue_max  = 4;

    uint32 red_shift   = 5;
    uint32 green_shift = 6;
    uint32 blue_shift  = 7;
}

// Identifies how the image was encoded.
enum VideoEncoding
{
    VIDEO_ENCODING_UNKNOWN = 0;
    VIDEO_ENCODING_ZSTD    = 1;
    VIDEO_ENCODING_VP8     = 2;
    VIDEO_ENCODING_VP9     = 4;
}

message VideoPacketFormat
{
    Rect video_rect          = 1;
    PixelFormat pixel_format = 2;
    Size screen_size         = 3;
    uint32 capturer_type     = 4;
}

enum VideoErrorCode
{
    VIDEO_ERROR_CODE_OK        = 0;
    VIDEO_ERROR_CODE_PAUSED    = 1;
    VIDEO_ERROR_CODE_TEMPORARY = 2;
    VIDEO_ERROR_CODE_PERMANENT = 3;
}

message VideoPacket
{
    VideoEncoding encoding = 1;

    // If the screen size or the pixel format has changed, the field must be filled.
    VideoPacketFormat format = 2;

    // The list of changed rectangles (areas) of the screen.
    repeated Rect dirty_rect = 3;

    // Video packet data.
    bytes data = 4;

    // If there is no error, then it takes the value VIDEO_ERROR_CODE_OK.
    // If the field has any other value, then all other fields are ignored.
    VideoErrorCode error_code = 5;
}

enum AudioEncoding
{
    AUDIO_ENCODING_UNKNOWN = 0;
    AUDIO_ENCODING_DEFAULT = 1;
    AUDIO_ENCODING_RAW     = 2;
    AUDIO_ENCODING_OPUS    = 3;
}

message AudioPacket
{
    enum SamplingRate
    {
        SAMPLING_RATE_INVALID = 0;
        SAMPLING_RATE_44100   = 44100;
        SAMPLING_RATE_48000   = 48000;
        SAMPLING_RATE_96000   = 96000;
        SAMPLING_RATE_192000  = 192000;
    }

    enum BytesPerSample
    {
        BYTES_PER_SAMPLE_INVALID = 0;
        BYTES_PER_SAMPLE_2       = 2;
    }

    enum Channels
    {
        CHANNELS_INVALID  = 0;
        CHANNELS_MONO     = 1;
        CHANNELS_STEREO   = 2;
        CHANNELS_SURROUND = 3;
        CHANNELS_4_0      = 4;
        CHANNELS_4_1      = 5;
        CHANNELS_5_1      = 6;
        CHANNELS_6_1      = 7;
        CHANNELS_7_1      = 8;
    }

    int32 timestamp                 = 1;
    repeated bytes data             = 2;
    AudioEncoding encoding          = 3;
    SamplingRate sampling_rate      = 4;
    BytesPerSample bytes_per_sample = 5;
    Channels channels               = 6;
}

message DesktopExtension
{
    string name = 1;
    bytes data  = 2;
}

message DesktopCapabilities
{
    enum OsType
    {
        OS_TYPE_WINDOWS = 0;
        OS_TYPE_LINUX   = 1;
        OS_TYPE_MACOSX  = 2;
    }

    message Flag
    {
        string name = 1;
        bool value  = 2;
    }

    string extensions      = 1;
    uint32 video_encodings = 2;
    uint32 audio_encodings = 3;
    OsType os_type         = 4;
    repeated Flag flag     = 5;
}

enum DesktopFlags
{
    NO_FLAGS                  = 0;
    ENABLE_CURSOR_SHAPE       = 1;
    ENABLE_CLIPBOARD          = 2;
    DISABLE_DESKTOP_EFFECTS   = 4;
    DISABLE_DESKTOP_WALLPAPER = 8;
    DISABLE_FONT_SMOOTHING    = 16;
    BLOCK_REMOTE_INPUT        = 32;
    LOCK_AT_DISCONNECT        = 64;
    CURSOR_POSITION           = 128;
    CLEAR_CLIPBOARD           = 256;
}

message DesktopConfig
{
    uint32 flags                 = 1;
    VideoEncoding video_encoding = 2;
    PixelFormat pixel_format     = 3;
    uint32 update_interval       = 4; // Deprecated. Must be equal to 30.
    uint32 compress_ratio        = 5;
    uint32 scale_factor          = 6; // Deprecated. Must be equal to 100.
    AudioEncoding audio_encoding = 7;
}

message HostToClient
{
    VideoPacket video_packet         = 1;
    AudioPacket audio_packet         = 2;
    CursorShape cursor_shape         = 3;
    ClipboardEvent clipboard_event   = 4;
    DesktopExtension extension       = 5;
    DesktopCapabilities capabilities = 6;
    CursorPosition cursor_position   = 7;
}

message ClientToHost
{
    MouseEvent mouse_event         = 1;
    KeyEvent key_event             = 2;
    TouchEvent touch_event         = 3;
    TextEvent text_event           = 4;
    ClipboardEvent clipboard_event = 5;
    DesktopExtension extension     = 6;
    DesktopConfig config           = 7;
    AudioPacket audio_packet       = 8;
}
