/* built in types: int8, uint8, 16, 32, 64 */ typedef fixed28_4 int32 @ctype(SPICE_FIXED28_4); struct Point { int32 x; int32 y; }; struct Point16 { int16 x; int16 y; }; struct PointFix { fixed28_4 x; fixed28_4 y; }; struct Rect { int32 top; int32 left; int32 bottom; int32 right; }; enum32 link_err { OK, ERROR, INVALID_MAGIC, INVALID_DATA, VERSION_MISMATCH, NEED_SECURED, NEED_UNSECURED, PERMISSION_DENIED, BAD_CONNECTION_ID, CHANNEL_NOT_AVAILABLE }; enum32 warn_code { WARN_GENERAL } @prefix(SPICE_); enum32 info_code { INFO_GENERAL } @prefix(SPICE_); flags32 migrate_flags { NEED_FLUSH, NEED_DATA_TRANSFER } @prefix(SPICE_MIGRATE_); enum32 notify_severity { INFO, WARN, ERROR, }; enum32 notify_visibility { LOW, MEDIUM, HIGH, }; flags32 mouse_mode { SERVER, CLIENT, }; enum16 pubkey_type { INVALID, RSA, RSA2, DSA, DSA1, DSA2, DSA3, DSA4, DH, EC, }; message Empty { }; message Data { uint8 data[] @end @ctype(uint8_t); } @nocopy; struct ChannelWait { uint8 channel_type; uint8 channel_id; uint64 message_serial; } @ctype(SpiceWaitForChannel); channel BaseChannel { server: message { migrate_flags flags; } migrate; Data migrate_data; message { uint32 generation; uint32 window; } set_ack; message { uint32 id; uint64 timestamp; uint8 data[] @end @ctype(uint8_t) @as_ptr(data_len); } ping; message { uint8 wait_count; ChannelWait wait_list[wait_count] @end; } wait_for_channels; message { uint64 time_stamp; link_err reason; } @ctype(SpiceMsgDisconnect) disconnecting; message { uint64 time_stamp; notify_severity severity; notify_visibility visibilty; uint32 what; /* error_code/warn_code/info_code */ uint32 message_len; uint8 message[message_len] @end @nomarshal; uint8 zero @end @ctype(uint8_t) @zero @nomarshal; } notify; client: message { uint32 generation; } ack_sync; Empty ack; message { uint32 id; uint64 timestamp; } @ctype(SpiceMsgPing) pong; Empty migrate_flush_mark; Data migrate_data; message { uint64 time_stamp; link_err reason; } @ctype(SpiceMsgDisconnect) disconnecting; }; struct ChannelId { uint8 type; uint8 id; }; channel MainChannel : BaseChannel { server: message { uint16 port; uint16 sport; uint32 host_offset; uint32 host_size; pubkey_type pub_key_type @minor(2); uint32 pub_key_offset @minor(2); uint32 pub_key_size @minor(2); uint8 host_data[host_size] @end @ctype(uint8_t) @zero_terminated @nomarshal; uint8 pub_key_data[pub_key_size] @minor(2) @end @ctype(uint8_t) @zero_terminated @nomarshal; } @ctype(SpiceMsgMainMigrationBegin) migrate_begin = 101; Empty migrate_cancel; message { uint32 session_id; uint32 display_channels_hint; uint32 supported_mouse_modes; uint32 current_mouse_mode; uint32 agent_connected; uint32 agent_tokens; uint32 multi_media_time; uint32 ram_hint; } init; message { uint32 num_of_channels; ChannelId channels[num_of_channels] @end; } @ctype(SpiceMsgChannels) channels_list; message { mouse_mode supported_modes; mouse_mode current_mode @unique_flag; } mouse_mode; message { uint32 time; } @ctype(SpiceMsgMainMultiMediaTime) multi_media_time; Empty agent_connected; message { link_err error_code; } @ctype(SpiceMsgMainAgentDisconnect) agent_disconnected; Data agent_data; message { uint32 num_tokens; } @ctype(SpiceMsgMainAgentTokens) agent_token; message { uint16 port; uint16 sport; uint32 host_offset; uint32 host_size; uint32 cert_subject_offset; uint32 cert_subject_size; uint8 host_data[host_size] @end @ctype(uint8_t) @zero_terminated; uint8 cert_subject_data[cert_subject_size] @end @ctype(uint8_t) @zero_terminated; } @ctype(SpiceMsgMainMigrationSwitchHost) migrate_switch_host; client: message { uint64 cache_size; } @ctype(SpiceMsgcClientInfo) client_info = 101; Empty migrate_connected; Empty migrate_connect_error; Empty attach_channels; message { mouse_mode mode; } mouse_mode_request; message { uint32 num_tokens; } agent_start; Data agent_data; message { uint32 num_tokens; } @ctype(SpiceMsgcMainAgentTokens) agent_token; }; enum32 clip_type { NONE, RECTS }; flags32 path_flags { /* TODO: C enum names changes */ BEGIN = 0, END = 1, CLOSE = 3, BEZIER = 4, } @prefix(SPICE_PATH_); enum32 video_codec_type { MJPEG = 1, }; flags32 stream_flags { TOP_DOWN = 0, }; enum32 brush_type { NONE, SOLID, PATTERN, }; flags8 mask_flags { INVERS, }; enum8 image_type { BITMAP, QUIC, RESERVED, LZ_PLT = 100, LZ_RGB, GLZ_RGB, FROM_CACHE, }; flags8 image_flags { CACHE_ME, }; enum8 bitmap_fmt { INVALID, 1BIT_LE, 1BIT_BE, 4BIT_LE, 4BIT_BE, 8BIT /* 8bit indexed mode */, 16BIT, /* 0555 mode */ 24BIT /* 3 byte, brg */, 32BIT /* 4 byte, xrgb in little endian format */, RGBA /* 4 byte, argb in little endian format */ }; flags8 bitmap_flags { PAL_CACHE_ME, PAL_FROM_CACHE, TOP_DOWN, }; enum8 image_scale_mode { INTERPOLATE, NEAREST, }; flags16 ropd { INVERS_SRC, INVERS_BRUSH, INVERS_DEST, OP_PUT, OP_OR, OP_AND, OP_XOR, OP_BLACKNESS, OP_WHITENESS, OP_INVERS, INVERS_RES, }; flags8 line_flags { STYLED = 3, START_WITH_GAP = 2, }; enum8 line_cap { ROUND, SQUARE, BUTT, }; enum8 line_join { ROUND, BEVEL, MITER, }; flags16 string_flags { RASTER_A1, RASTER_A4, RASTER_A8, RASTER_TOP_DOWN, }; enum8 resource_type { INVALID, PIXMAP } @prefix(SPICE_RES_TYPE_); struct ClipRects { uint32 num_rects; Rect rects[num_rects] @end; }; struct PathSegment { path_flags flags; uint32 count; PointFix points[count] @end; } @ctype(SpicePathSeg); struct Path { uint32 size; PathSegment segments[bytes(size)] @end; }; struct Clip { clip_type type; switch (type) { case RECTS: ClipRects *data @outvar(cliprects); default: uint64 data @zero; } u @anon; }; struct DisplayBase { uint32 surface_id @virtual(0); Rect box; Clip clip; } @ctype(SpiceMsgDisplayBase); struct ResourceID { uint8 type; uint64 id; }; struct WaitForChannel { uint8 channel_type; uint8 channel_id; uint64 message_serial; }; struct Palette { uint64 unique; uint16 num_ents; uint32 ents[num_ents] @end; }; struct BitmapData { bitmap_fmt format; bitmap_flags flags; uint32 x; uint32 y; uint32 stride; switch (flags) { case PAL_FROM_CACHE: uint64 palette; default: Palette *palette @outvar(bitmap); } pal @anon; uint8 *data[image_size(8, stride, y)] @nocopy; /* pointer to array, not array of pointers as in C */ } @ctype(SpiceBitmap); struct BinaryData { uint32 data_size; uint8 data[data_size] @end @nomarshal; } @ctype(SpiceQUICData); struct LZPLTData { bitmap_flags flags; uint32 data_size; switch (flags) { case PAL_FROM_CACHE: uint64 palette; default: Palette *palette @nonnull @outvar(lzplt); } pal @anon; uint8 data[data_size] @end @nomarshal; }; struct Image { uint64 id; image_type type; image_flags flags; uint32 width; uint32 height; switch (type) { case BITMAP: BitmapData bitmap_data @ctype(SpiceBitmap); case QUIC: case LZ_RGB: case GLZ_RGB: BinaryData binary_data @ctype(SpiceQUICData); case LZ_PLT: LZPLTData lzplt_data @ctype(SpiceLZPLTData); } u @end; } @ctype(SpiceImageDescriptor); struct Pattern { Image *pat @nonnull; Point pos; }; struct Brush { brush_type type; switch (type) { case SOLID: uint32 color; case PATTERN: Pattern pattern; } u @fixedsize; }; struct QMask { mask_flags flags; Point pos; Image *bitmap; }; struct LineAttr { line_flags flags; line_join join_style; line_cap end_style; uint8 style_nseg; fixed28_4 width; fixed28_4 miter_limit; fixed28_4 *style[style_nseg]; }; struct RasterGlyphA1 { Point render_pos; Point glyph_origin; uint16 width; uint16 height; uint8 data[image_size(1, width, height)] @end; } @ctype(SpiceRasterGlyph); struct RasterGlyphA4 { Point render_pos; Point glyph_origin; uint16 width; uint16 height; uint8 data[image_size(4, width, height)] @end; } @ctype(SpiceRasterGlyph); struct RasterGlyphA8 { Point render_pos; Point glyph_origin; uint16 width; uint16 height; uint8 data[image_size(8, width, height)] @end; } @ctype(SpiceRasterGlyph); struct String { uint16 length; string_flags flags; /* Special: Only one of a1/a4/a8 set */ switch (flags) { case RASTER_A1: RasterGlyphA1 glyphs[length] @ctype(SpiceRasterGlyph); case RASTER_A4: RasterGlyphA4 glyphs[length] @ctype(SpiceRasterGlyph); case RASTER_A8: RasterGlyphA8 glyphs[length] @ctype(SpiceRasterGlyph); } u @end @nomarshal; }; channel DisplayChannel : BaseChannel { server: message { uint32 x_res; uint32 y_res; uint32 bits; } mode = 101; Empty mark; Empty reset; message { DisplayBase base; Point src_pos; } copy_bits; message { uint16 count; ResourceID resources[count] @end; } @ctype(SpiceResourceList) inval_list; message { uint8 wait_count; WaitForChannel wait_list[wait_count] @end; } @ctype(SpiceMsgWaitForChannels) inval_all_pixmaps; message { uint64 id; } @ctype(SpiceMsgDisplayInvalOne) inval_palette; Empty inval_all_palettes; message { uint32 surface_id @virtual(0); uint32 id; stream_flags flags; video_codec_type codec_type; uint64 stamp; uint32 stream_width; uint32 stream_height; uint32 src_width; uint32 src_height; Rect dest; Clip clip; } stream_create = 122; message { uint32 id; uint32 multi_media_time; uint32 data_size; uint32 pad_size; uint8 data[data_size] @end @nomarshal; uint8 padding[pad_size] @end @ctype(uint8_t) @nomarshal; /* Uhm, why are we sending padding over network? */ } stream_data; message { uint32 id; Clip clip; } stream_clip; message { uint32 id; } stream_destroy; Empty stream_destroy_all; message { DisplayBase base; struct Fill { Brush brush @outvar(brush); uint16 rop_descriptor; QMask mask @outvar(mask); } data; } draw_fill = 302; message { DisplayBase base; struct Opaque { Image *src_bitmap; Rect src_area; Brush brush; ropd rop_descriptor; image_scale_mode scale_mode; QMask mask @outvar(mask); } data; } draw_opaque; message { DisplayBase base; struct Copy { Image *src_bitmap; Rect src_area; ropd rop_descriptor; image_scale_mode scale_mode; QMask mask @outvar(mask); } data; } draw_copy; message { DisplayBase base; struct Blend { Image *src_bitmap; Rect src_area; ropd rop_descriptor; image_scale_mode scale_mode; QMask mask @outvar(mask); } @ctype(SpiceCopy) data; } draw_blend; message { DisplayBase base; struct Blackness { QMask mask @outvar(mask); } data; } draw_blackness; message { DisplayBase base; struct Whiteness { QMask mask @outvar(mask); } data; } draw_whiteness; message { DisplayBase base; struct Invers { QMask mask @outvar(mask); } data; } draw_invers; message { DisplayBase base; struct Rop3 { Image *src_bitmap; Rect src_area; Brush brush; uint8 rop3; image_scale_mode scale_mode; QMask mask @outvar(mask); } data; } draw_rop3; message { DisplayBase base; struct Stroke { Path *path; LineAttr attr; Brush brush; uint16 fore_mode; uint16 back_mode; } data; } draw_stroke; message { DisplayBase base; struct Text { String *str; Rect back_area; Brush fore_brush @outvar(fore_brush); Brush back_brush @outvar(back_brush); uint16 fore_mode; uint16 back_mode; } data; } draw_text; message { DisplayBase base; struct Transparent { Image *src_bitmap; Rect src_area; uint32 src_color; uint32 true_color; } data; } draw_transparent; message { DisplayBase base; struct AlphaBlnd { int8 alpha_flags @virtual(0); uint8 alpha; Image *src_bitmap; Rect src_area; } data; } draw_alpha_blend; client: message { uint8 pixmap_cache_id; int64 pixmap_cache_size; //in pixels uint8 glz_dictionary_id; int32 glz_dictionary_window_size; // in pixels } init = 101; }; flags32 keyboard_modifier_flags { SCROLL_LOCK, NUM_LOCK, CAPS_LOCK }; enum32 mouse_button { INVALID, LEFT, MIDDLE, RIGHT, UP, DOWN, }; flags32 mouse_button_mask { LEFT, MIDDLE, RIGHT }; channel InputsChannel : BaseChannel { client: message { uint32 code; } @ctype(SpiceMsgcKeyDown) key_down = 101; message { uint32 code; } @ctype(SpiceMsgcKeyUp) key_up; message { keyboard_modifier_flags modifiers; } @ctype(SpiceMsgcKeyModifiers) key_modifiers; message { int32 dx; int32 dy; mouse_button_mask buttons_state; } @ctype(SpiceMsgcMouseMotion) mouse_motion = 111; message { uint32 x; uint32 y; mouse_button_mask buttons_state; uint8 display_id; } @ctype(SpiceMsgcMousePosition) mouse_position; message { mouse_button button; mouse_button_mask buttons_state; } @ctype(SpiceMsgcMousePress) mouse_press; message { mouse_button button; mouse_button_mask buttons_state; } @ctype(SpiceMsgcMouseRelease) mouse_release; server: message { keyboard_modifier_flags keyboard_modifiers; } init = 101; message { keyboard_modifier_flags modifiers; } key_modifiers; Empty mouse_motion_ack = 111; }; enum16 cursor_type { ALPHA, MONO, COLOR4, COLOR8, COLOR16, COLOR24, COLOR32, }; flags32 cursor_flags { NONE, /* Means no cursor */ CACHE_ME, FROM_CACHE, }; struct CursorHeader { uint64 unique; cursor_type type; uint16 width; uint16 height; uint16 hot_spot_x; uint16 hot_spot_y; }; struct Cursor { cursor_flags flags; CursorHeader header; uint8 data[] @end @as_ptr(data_size); }; channel CursorChannel : BaseChannel { server: message { Point16 position; uint16 trail_length; uint16 trail_frequency; uint8 visible; Cursor cursor; } init = 101; Empty reset; message { Point16 position; uint8 visible; Cursor cursor; } set; message { Point16 position; } move; Empty hide; message { uint16 length; uint16 frequency; } trail; message { uint64 id; } @ctype(SpiceMsgDisplayInvalOne) inval_one; Empty inval_all; }; enum32 audio_data_mode { INVALID, RAW, CELT_0_5_1, }; enum32 audio_fmt { INVALID, S16, }; channel PlaybackChannel : BaseChannel { server: message { uint32 time; uint8 data[] @end @as_ptr(data_size); } @ctype(SpiceMsgPlaybackPacket) data = 101; message { uint32 time; audio_data_mode mode; uint8 data[] @end @as_ptr(data_size); } mode; message { uint32 channels; audio_fmt format; uint32 frequency; uint32 time; } start; Empty stop; }; channel RecordChannel : BaseChannel { server: message { uint32 channels; audio_fmt format; uint32 frequency; } start = 101; Empty stop; client: message { uint32 time; uint8 data[] @end @nomarshal @as_ptr(data_size); } @ctype(SpiceMsgcRecordPacket) data = 101; message { uint32 time; audio_data_mode mode; uint8 data[] @end @as_ptr(data_size); } mode; message { uint32 time; } start_mark; }; protocol Spice { MainChannel main = 1; DisplayChannel display; InputsChannel inputs; CursorChannel cursor; PlaybackChannel playback; RecordChannel record; };