From 0acea04d71f28e98b15620e31902dd8e2196c637 Mon Sep 17 00:00:00 2001 From: Alon Levy Date: Thu, 7 Jun 2012 15:04:51 +0300 Subject: server: add monitors_config support a SpiceMsgDisplayMonitorsConfig is sent on two occasions: * as a result of a spice_qxl_monitors_config_async * whenever a client connects and there is a previously set monitors config Sending the new message is protected by a new cap, SPICE_DISPLAY_CAP_MONITORS_CONFIG More elaborately: spice_qxl_monitors_config_async receives a QXLPHYSICAL address of a QXLMonitorsConfig struct and reads it, caching it in the RedWorker, and sending it to all clients. Whenever a new client connects it receives a SpiceMsgDisplayMonitorsConfig message as well. --- server/red_dispatcher.c | 25 +++++ server/red_dispatcher.h | 5 + server/red_worker.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++- server/red_worker.h | 2 + server/spice-server.syms | 4 + server/spice.h | 3 + 6 files changed, 285 insertions(+), 1 deletion(-) (limited to 'server') diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c index 6b96a2e8..8d9c073a 100644 --- a/server/red_dispatcher.c +++ b/server/red_dispatcher.c @@ -648,6 +648,21 @@ static void red_dispatcher_flush_surfaces_async(RedDispatcher *dispatcher, uint6 dispatcher_send_message(&dispatcher->dispatcher, message, &payload); } +static void red_dispatcher_monitors_config_async(RedDispatcher *dispatcher, + QXLPHYSICAL monitors_config, + int group_id, + uint64_t cookie) +{ + RedWorkerMessageMonitorsConfigAsync payload; + RedWorkerMessage message = RED_WORKER_MESSAGE_MONITORS_CONFIG_ASYNC; + + payload.base.cmd = async_command_alloc(dispatcher, message, cookie); + payload.monitors_config = monitors_config; + payload.group_id = group_id; + + dispatcher_send_message(&dispatcher->dispatcher, message, &payload); +} + static void red_dispatcher_stop(RedDispatcher *dispatcher) { RedWorkerMessageStop payload; @@ -908,6 +923,13 @@ void spice_qxl_flush_surfaces_async(QXLInstance *instance, uint64_t cookie) red_dispatcher_flush_surfaces_async(instance->st->dispatcher, cookie); } +SPICE_GNUC_VISIBLE +void spice_qxl_monitors_config_async(QXLInstance *instance, QXLPHYSICAL monitors_config, + int group_id, uint64_t cookie) +{ + red_dispatcher_monitors_config_async(instance->st->dispatcher, monitors_config, group_id, cookie); +} + void red_dispatcher_async_complete(struct RedDispatcher *dispatcher, AsyncCommand *async_command) { @@ -935,6 +957,8 @@ void red_dispatcher_async_complete(struct RedDispatcher *dispatcher, break; case RED_WORKER_MESSAGE_FLUSH_SURFACES_ASYNC: break; + case RED_WORKER_MESSAGE_MONITORS_CONFIG_ASYNC: + break; default: spice_warning("unexpected message %d", async_command->message); } @@ -1054,6 +1078,7 @@ RedDispatcher *red_dispatcher_init(QXLInstance *qxl) client_cbs.migrate = red_dispatcher_display_migrate; red_channel_register_client_cbs(display_channel, &client_cbs); red_channel_set_data(display_channel, red_dispatcher); + red_channel_set_cap(display_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG); reds_register_channel(display_channel); } diff --git a/server/red_dispatcher.h b/server/red_dispatcher.h index 36db4e34..98f8ceaf 100644 --- a/server/red_dispatcher.h +++ b/server/red_dispatcher.h @@ -189,5 +189,10 @@ typedef struct RedWorkerMessageDestroySurfaceWaitAsync { typedef struct RedWorkerMessageResetMemslots { } RedWorkerMessageResetMemslots; +typedef struct RedWorkerMessageMonitorsConfigAsync { + RedWorkerMessageAsync base; + QXLPHYSICAL monitors_config; + int group_id; +} RedWorkerMessageMonitorsConfigAsync; #endif diff --git a/server/red_worker.c b/server/red_worker.c index e2397401..90094626 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -267,6 +268,7 @@ enum { PIPE_ITEM_TYPE_INVAL_PALLET_CACHE, PIPE_ITEM_TYPE_CREATE_SURFACE, PIPE_ITEM_TYPE_DESTROY_SURFACE, + PIPE_ITEM_TYPE_MONITORS_CONFIG, }; typedef struct VerbItem { @@ -313,6 +315,19 @@ typedef struct SurfaceDestroyItem { PipeItem pipe_item; } SurfaceDestroyItem; +typedef struct MonitorsConfig { + int refs; + struct RedWorker *worker; + int count; + int max_allowed; + QXLHead heads[0]; +} MonitorsConfig; + +typedef struct MonitorsConfigItem { + PipeItem pipe_item; + MonitorsConfig *monitors_config; +} MonitorsConfigItem; + typedef struct CursorItem { uint32_t group_id; int refs; @@ -899,6 +914,8 @@ typedef struct RedWorker { uint32_t n_surfaces; SpiceImageSurfaces image_surfaces; + MonitorsConfig *monitors_config; + Ring current_list; uint32_t current_size; uint32_t drawable_count; @@ -970,6 +987,8 @@ typedef struct RedWorker { uint64_t *wakeup_counter; uint64_t *command_counter; #endif + + int driver_has_monitors_config; } RedWorker; typedef enum { @@ -1031,6 +1050,8 @@ static void red_wait_pipe_item_sent(RedChannelClient *rcc, PipeItem *item); static void dump_bitmap(RedWorker *worker, SpiceBitmap *bitmap, uint32_t group_id); #endif +static void red_push_monitors_config(DisplayChannelClient *dcc); + /* * Macros to make iterating over stuff easier * The two collections we iterate over: @@ -1194,6 +1215,24 @@ static void print_compress_stats(DisplayChannel *display_channel) #endif +static MonitorsConfig *monitors_config_getref(MonitorsConfig *monitors_config) +{ + monitors_config->refs++; + + return monitors_config; +} + +static void monitors_config_decref(MonitorsConfig *monitors_config) +{ + if (--monitors_config->refs > 0) { + return; + } + + spice_debug("removing worker monitors config"); + monitors_config->worker->monitors_config = NULL; + free(monitors_config); +} + static inline int is_primary_surface(RedWorker *worker, uint32_t surface_id) { if (surface_id == 0) { @@ -8696,6 +8735,33 @@ static void red_marshall_surface_destroy(RedChannelClient *rcc, spice_marshall_msg_display_surface_destroy(base_marshaller, &surface_destroy); } +static void red_marshall_monitors_config(RedChannelClient *rcc, SpiceMarshaller *base_marshaller, + MonitorsConfig *monitors_config) +{ + int heads_size = sizeof(SpiceHead) * monitors_config->count; + int i; + SpiceMsgDisplayMonitorsConfig *msg = spice_malloc0(sizeof(*msg) + heads_size); + int count = 0; // ignore monitors_config->count, it may contain zero width monitors, remove them now + + red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_MONITORS_CONFIG, NULL); + for (i = 0 ; i < monitors_config->count; ++i) { + if (monitors_config->heads[i].width == 0 || monitors_config->heads[i].height == 0) { + continue; + } + msg->heads[count].id = monitors_config->heads[i].id; + msg->heads[count].surface_id = monitors_config->heads[i].surface_id; + msg->heads[count].width = monitors_config->heads[i].width; + msg->heads[count].height = monitors_config->heads[i].height; + msg->heads[count].x = monitors_config->heads[i].x; + msg->heads[count].y = monitors_config->heads[i].y; + count++; + } + msg->count = count; + msg->max_allowed = monitors_config->max_allowed; + spice_marshall_msg_display_monitors_config(base_marshaller, msg); + free(msg); +} + static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item) { SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); @@ -8764,6 +8830,12 @@ static void display_channel_send_item(RedChannelClient *rcc, PipeItem *pipe_item red_marshall_surface_destroy(rcc, m, surface_destroy->surface_destroy.surface_id); break; } + case PIPE_ITEM_TYPE_MONITORS_CONFIG: { + MonitorsConfigItem *monconf_item = SPICE_CONTAINEROF(pipe_item, + MonitorsConfigItem, pipe_item); + red_marshall_monitors_config(rcc, m, monconf_item->monitors_config); + break; + } default: spice_error("invalid pipe item type"); } @@ -9320,6 +9392,7 @@ static void on_new_display_channel_client(DisplayChannelClient *dcc) red_current_flush(worker, 0); push_new_primary_surface(dcc); red_push_surface_image(dcc, 0); + red_push_monitors_config(dcc); red_pipe_add_verb(rcc, SPICE_MSG_DISPLAY_MARK); red_disply_start_streams(dcc); } @@ -9931,6 +10004,13 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient case PIPE_ITEM_TYPE_VERB: free(item); break; + case PIPE_ITEM_TYPE_MONITORS_CONFIG: { + MonitorsConfigItem *monconf_item = SPICE_CONTAINEROF(item, + MonitorsConfigItem, pipe_item); + monitors_config_decref(monconf_item->monitors_config); + free(item); + break; + } default: spice_critical("invalid item type"); } @@ -9981,6 +10061,13 @@ static void display_channel_client_release_item_before_push(DisplayChannelClient free(surface_destroy); break; } + case PIPE_ITEM_TYPE_MONITORS_CONFIG: { + MonitorsConfigItem *monconf_item = SPICE_CONTAINEROF(item, + MonitorsConfigItem, pipe_item); + monitors_config_decref(monconf_item->monitors_config); + free(item); + break; + } case PIPE_ITEM_TYPE_INVAL_ONE: case PIPE_ITEM_TYPE_VERB: case PIPE_ITEM_TYPE_MIGRATE: @@ -10577,6 +10664,125 @@ void handle_dev_destroy_surfaces(void *opaque, void *payload) dev_destroy_surfaces(worker); } +static MonitorsConfigItem *get_monitors_config_item( + RedChannel* channel, MonitorsConfig *monitors_config) +{ + MonitorsConfigItem *mci; + + mci = (MonitorsConfigItem *)spice_malloc(sizeof(*mci)); + mci->monitors_config = monitors_config; + + red_channel_pipe_item_init(channel, + &mci->pipe_item, PIPE_ITEM_TYPE_MONITORS_CONFIG); + return mci; +} + +static inline void red_monitors_config_item_add(DisplayChannelClient *dcc) +{ + MonitorsConfigItem *mci; + RedWorker *worker = dcc->common.worker; + + mci = get_monitors_config_item(dcc->common.base.channel, + monitors_config_getref(worker->monitors_config)); + red_channel_client_pipe_add(&dcc->common.base, &mci->pipe_item); +} + +static void worker_update_monitors_config(RedWorker *worker, + QXLMonitorsConfig *dev_monitors_config) +{ + int heads_size; + MonitorsConfig *monitors_config; + int real_count = 0; + int i; + + if (worker->monitors_config) { + monitors_config_decref(worker->monitors_config); + } + + spice_debug("monitors config %d(%d)", + dev_monitors_config->count, + dev_monitors_config->max_allowed); + for (i = 0; i < dev_monitors_config->count; i++) { + spice_debug("+%d+%d %dx%d", + dev_monitors_config->heads[i].x, + dev_monitors_config->heads[i].y, + dev_monitors_config->heads[i].width, + dev_monitors_config->heads[i].height); + } + + // Ignore any empty sized monitors at the end of the config. + // 4: {w1,h1},{w2,h2},{0,0},{0,0} -> 2: {w1,h1},{w2,h2} + for (i = dev_monitors_config->count ; i > 0 ; --i) { + if (dev_monitors_config->heads[i - 1].width > 0 && + dev_monitors_config->heads[i - 1].height > 0) { + real_count = i; + break; + } + } + heads_size = real_count * sizeof(QXLHead); + spice_debug("new working monitor config (count: %d, real: %d)", + dev_monitors_config->count, real_count); + worker->monitors_config = monitors_config = + spice_malloc(sizeof(*monitors_config) + heads_size); + monitors_config->refs = 1; + monitors_config->worker = worker; + monitors_config->count = dev_monitors_config->count; + monitors_config->max_allowed = dev_monitors_config->max_allowed; + memcpy(monitors_config->heads, dev_monitors_config->heads, heads_size); +} + +static void red_push_monitors_config(DisplayChannelClient *dcc) +{ + MonitorsConfig *monitors_config = DCC_TO_WORKER(dcc)->monitors_config; + + spice_return_if_fail(monitors_config != NULL); + + if (!red_channel_client_test_remote_cap(&dcc->common.base, + SPICE_DISPLAY_CAP_MONITORS_CONFIG)) { + return; + } + red_monitors_config_item_add(dcc); + red_channel_client_push(&dcc->common.base); +} + +static void red_worker_push_monitors_config(RedWorker *worker) +{ + DisplayChannelClient *dcc; + RingItem *item; + + WORKER_FOREACH_DCC(worker, item, dcc) { + red_push_monitors_config(dcc); + } +} + +static void set_monitors_config_to_primary(RedWorker *worker) +{ + QXLHead *head; + DrawContext *context; + + if (!worker->surfaces[0].context.canvas) { + spice_warning("%s: no primary surface", __FUNCTION__); + return; + } + if (worker->monitors_config) { + monitors_config_decref(worker->monitors_config); + } + context = &worker->surfaces[0].context; + worker->monitors_config = + spice_malloc(sizeof(*worker->monitors_config) + sizeof(QXLHead)); + worker->monitors_config->refs = 1; + worker->monitors_config->worker = worker; + worker->monitors_config->count = 1; + worker->monitors_config->max_allowed = 1; + head = worker->monitors_config->heads; + head->id = 0; + head->surface_id = 0; + head->width = context->width; + head->height = context->height; + head->x = 0; + head->y = 0; +} + static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id, QXLDevSurfaceCreate surface) { @@ -10600,7 +10806,10 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id, red_create_surface(worker, 0, surface.width, surface.height, surface.stride, surface.format, line_0, surface.flags & QXL_SURF_FLAG_KEEP_DATA, TRUE); - + set_monitors_config_to_primary(worker); + if (!worker->driver_has_monitors_config) { + red_worker_push_monitors_config(worker); + } if (display_is_connected(worker)) { red_pipes_add_verb(&worker->display_channel->common.base, SPICE_MSG_DISPLAY_MARK); @@ -10848,6 +11057,36 @@ void handle_dev_display_migrate(void *opaque, void *payload) red_migrate_display(worker, rcc); } +static void handle_dev_monitors_config_async(void *opaque, void *payload) +{ + RedWorkerMessageMonitorsConfigAsync *msg = payload; + RedWorker *worker = opaque; + int min_size = sizeof(QXLMonitorsConfig) + sizeof(QXLHead); + int error; + QXLMonitorsConfig *dev_monitors_config = + (QXLMonitorsConfig*)get_virt(&worker->mem_slots, msg->monitors_config, + min_size, msg->group_id, &error); + + if (error) { + /* TODO: raise guest bug (requires added QXL interface) */ + return; + } + worker->driver_has_monitors_config = 1; + if (dev_monitors_config->count == 0) { + spice_warning("ignoring an empty monitors config message from driver"); + return; + } + if (dev_monitors_config->count > dev_monitors_config->max_allowed) { + spice_warning("ignoring malformed monitors_config from driver, " + "count > max_allowed %d > %d", + dev_monitors_config->count, + dev_monitors_config->max_allowed); + return; + } + worker_update_monitors_config(worker, dev_monitors_config); + red_worker_push_monitors_config(worker); +} + /* TODO: special, perhaps use another dispatcher? */ void handle_dev_cursor_channel_create(void *opaque, void *payload) { @@ -11202,6 +11441,11 @@ static void register_callbacks(Dispatcher *dispatcher) handle_dev_reset_memslots, sizeof(RedWorkerMessageResetMemslots), DISPATCHER_NONE); + dispatcher_register_handler(dispatcher, + RED_WORKER_MESSAGE_MONITORS_CONFIG_ASYNC, + handle_dev_monitors_config_async, + sizeof(RedWorkerMessageMonitorsConfigAsync), + DISPATCHER_ASYNC); } @@ -11240,6 +11484,7 @@ static void red_init(RedWorker *worker, WorkerInitData *init_data) worker->jpeg_state = init_data->jpeg_state; worker->zlib_glz_state = init_data->zlib_glz_state; worker->streaming_video = init_data->streaming_video; + worker->driver_has_monitors_config = 0; ring_init(&worker->current_list); image_cache_init(&worker->image_cache); image_surface_init(worker); diff --git a/server/red_worker.h b/server/red_worker.h index fd23ede9..6c5b839f 100644 --- a/server/red_worker.h +++ b/server/red_worker.h @@ -85,6 +85,8 @@ enum { RED_WORKER_MESSAGE_DISPLAY_CHANNEL_CREATE, RED_WORKER_MESSAGE_CURSOR_CHANNEL_CREATE, + RED_WORKER_MESSAGE_MONITORS_CONFIG_ASYNC, + RED_WORKER_MESSAGE_COUNT // LAST }; diff --git a/server/spice-server.syms b/server/spice-server.syms index 99a72718..a45951c1 100644 --- a/server/spice-server.syms +++ b/server/spice-server.syms @@ -112,3 +112,7 @@ global: SPICE_SERVER_0.10.3 { spice_server_is_server_mouse; } SPICE_SERVER_0.10.2; + +SPICE_SERVER_0.10.4 { + spice_qxl_monitors_config_async; +} SPICE_SERVER_0.10.3; diff --git a/server/spice.h b/server/spice.h index abaffbdd..ec28f4ab 100644 --- a/server/spice.h +++ b/server/spice.h @@ -161,6 +161,9 @@ void spice_qxl_create_primary_surface_async(QXLInstance *instance, uint32_t surf void spice_qxl_destroy_surface_async(QXLInstance *instance, uint32_t surface_id, uint64_t cookie); /* suspend and resolution change on windows drivers */ void spice_qxl_flush_surfaces_async(QXLInstance *instance, uint64_t cookie); +/* since spice 0.12.0 */ +void spice_qxl_monitors_config_async(QXLInstance *instance, QXLPHYSICAL monitors_config, + int group_id, uint64_t cookie); typedef struct QXLDrawArea { uint8_t *buf; -- cgit