ack ----- Mensaje original ----- > 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. > --- > This patch on purpose doesn't change spice-common, it requires at > least > spice-common 7eba850ad562f3c5a29621af09d93535a76ff99c . > > 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(-) > > diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c > index 6b96a2e..8d9c073 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 36db4e3..98f8cea 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 5634db5..2400c3a 100644 > --- a/server/red_worker.c > +++ b/server/red_worker.c > @@ -45,6 +45,7 @@ > #include <netinet/tcp.h> > #include <setjmp.h> > #include <openssl/ssl.h> > +#include <inttypes.h> > > #include <spice/protocol.h> > #include <spice/qxl_dev.h> > @@ -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) { > @@ -8697,6 +8736,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); > @@ -8765,6 +8831,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"); > } > @@ -9321,6 +9393,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); > } > @@ -9932,6 +10005,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"); > } > @@ -9982,6 +10062,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: > @@ -10578,6 +10665,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) > { > @@ -10601,7 +10807,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); > @@ -10849,6 +11058,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) > { > @@ -11203,6 +11442,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); > } > > > @@ -11241,6 +11485,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 fd23ede..6c5b839 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 99a7271..a45951c 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 abaffbd..ec28f4a 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; > -- > 1.7.10.1 > > _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel