1、连接notify::cursor通知与回调函数(spice-widget.c)
static void channel_new(SpiceSession *s, SpiceChannel *channel, SpiceDisplay *display)
{
...
if (SPICE_IS_CURSOR_CHANNEL(channel)) {
gpointer cursor_shape;
if (id != d->channel_id)
return;
d->cursor = SPICE_CURSOR_CHANNEL(channel);
spice_g_signal_connect_object(channel, "notify::cursor",
G_CALLBACK(cursor_set), display, 0);
spice_g_signal_connect_object(channel, "cursor-move",
G_CALLBACK(cursor_move), display, 0);
spice_g_signal_connect_object(channel, "cursor-hide",
G_CALLBACK(cursor_hide), display, 0);
spice_g_signal_connect_object(channel, "cursor-reset",
G_CALLBACK(cursor_reset), display, 0);
spice_channel_connect(channel);
g_object_get(G_OBJECT(channel), "cursor", &cursor_shape, NULL);
if (cursor_shape != NULL) {
g_boxed_free(SPICE_TYPE_CURSOR_SHAPE, cursor_shape);
cursor_set(d->cursor, NULL, display);
}
return;
}
...
}
2、cursor_channel消息回调函数(channel-cursor.c)
static void channel_set_handlers(SpiceChannelClass *klass)
{
static const spice_msg_handler handlers[] = {
[ SPICE_MSG_CURSOR_INIT ] = cursor_handle_init,
[ SPICE_MSG_CURSOR_RESET ] = cursor_handle_reset,
[ SPICE_MSG_CURSOR_SET ] = cursor_handle_set,
[ SPICE_MSG_CURSOR_MOVE ] = cursor_handle_move,
[ SPICE_MSG_CURSOR_HIDE ] = cursor_handle_hide,
[ SPICE_MSG_CURSOR_TRAIL ] = cursor_handle_trail,
[ SPICE_MSG_CURSOR_INVAL_ONE ] = cursor_handle_inval_one,
[ SPICE_MSG_CURSOR_INVAL_ALL ] = cursor_handle_inval_all,
};
spice_channel_set_handlers(klass, handlers, G_N_ELEMENTS(handlers));
}
cursor_handle_set的回调函数实现如下:(channel-cursor.c)
/* coroutine context */
static void cursor_handle_set(SpiceChannel *channel, SpiceMsgIn *in)
{
SpiceMsgCursorSet *set = spice_msg_in_parsed(in);
SpiceCursorChannelPrivate *c = SPICE_CURSOR_CHANNEL(channel)->priv;
display_cursor *cursor;
g_return_if_fail(c->init_done == TRUE);
cursor = set_cursor(channel, &set->cursor);
if (cursor)
emit_cursor_set(channel, cursor);
else
g_coroutine_signal_emit(channel, signals[SPICE_CURSOR_HIDE], 0);
if (cursor)
display_cursor_unref(cursor);
}
发送鼠标的通知和SPICE_CURSOR_SET信号(channel-cursor.c)
/* coroutine context */
static void emit_cursor_set(SpiceChannel *channel, display_cursor *cursor)
{
SpiceCursorChannelPrivate *c;
g_return_if_fail(cursor != NULL);
c = SPICE_CURSOR_CHANNEL(channel)->priv;
c->last_cursor.type = cursor->hdr.type;
c->last_cursor.width = cursor->hdr.width;
c->last_cursor.height = cursor->hdr.height;
c->last_cursor.hot_spot_x = cursor->hdr.hot_spot_x;
c->last_cursor.hot_spot_y = cursor->hdr.hot_spot_y;
g_free(c->last_cursor.data);
c->last_cursor.data = g_memdup(cursor->data, cursor->hdr.width * cursor->hdr.height * 4);
g_coroutine_object_notify(G_OBJECT(channel), "cursor");
g_coroutine_signal_emit(channel, signals[SPICE_CURSOR_SET], 0,
cursor->hdr.width, cursor->hdr.height,
cursor->hdr.hot_spot_x, cursor->hdr.hot_spot_y,
cursor->default_cursor ? NULL : cursor->data);
}
3、将鼠标图片数据保存到mouse_pixbuf中(channel-cursor.c)
static void cursor_set(SpiceCursorChannel *channel,
G_GNUC_UNUSED GParamSpec *pspec,
gpointer data)
{
SpiceDisplay *display = data;
SpiceDisplayPrivate *d = display->priv;
SpiceCursorShape *cursor_shape;
g_object_get(G_OBJECT(channel), "cursor", &cursor_shape, NULL);
if (G_UNLIKELY(cursor_shape == NULL || cursor_shape->data == NULL)) {
if (cursor_shape != NULL) {
g_boxed_free(SPICE_TYPE_CURSOR_SHAPE, cursor_shape);
}
return;
}
cursor_invalidate(display);
g_clear_object(&d->mouse_pixbuf);
d->mouse_pixbuf = gdk_pixbuf_new_from_data(cursor_shape->data,
GDK_COLORSPACE_RGB,
TRUE, 8,
cursor_shape->width,
cursor_shape->height,
cursor_shape->width * 4,
cursor_shape_destroy, cursor_shape);
d->mouse_hotspot.x = cursor_shape->hot_spot_x;
d->mouse_hotspot.y = cursor_shape->hot_spot_y;
update_mouse_cursor(display);
}
4、update_mouse_cursor通过gdk_cairo_surface_create_from_pixbuf画鼠标形状
static void update_mouse_cursor(SpiceDisplay *display)
{
SpiceDisplayPrivate *d = display->priv;
GdkCursor *cursor = NULL;
cairo_t *cursor_ctx;
cairo_surface_t *surface, *target;
double scale;
gint scale_factor;
gint hotspot_x, hotspot_y;
if (G_UNLIKELY(!d->mouse_pixbuf)) {
return;
}
if (!d->ready || !d->monitor_ready) {
return;
}
spice_display_get_scaling(display, &scale, NULL, NULL, NULL, NULL);
scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
scale = MAX(0.5, scale);
cairo_surface_destroy(d->cursor_surface);
/* 缩放鼠标形状 */
surface = gdk_cairo_surface_create_from_pixbuf(d->mouse_pixbuf, 0, gtk_widget_get_window(GTK_WIDGET(display)));
target = cairo_image_surface_create(cairo_image_surface_get_format(surface),
scale * gdk_pixbuf_get_width(d->mouse_pixbuf),
scale * gdk_pixbuf_get_height(d->mouse_pixbuf));
cairo_surface_set_device_scale(target, scale_factor, scale_factor);
cursor_ctx = cairo_create(target);
cairo_scale(cursor_ctx, scale, scale);
cairo_set_source_surface(cursor_ctx, surface, 0, 0);
cairo_paint(cursor_ctx);
d->cursor_surface = cairo_surface_reference(cairo_get_target(cursor_ctx));
cairo_surface_destroy(target);
cairo_surface_destroy(surface);
cairo_destroy(cursor_ctx);
hotspot_x = d->mouse_hotspot.x * scale;
hotspot_y = d->mouse_hotspot.y * scale;
#ifdef GDK_WINDOWING_X11
/* undo hotspot scaling in gdkcursor */
if (GDK_IS_X11_DISPLAY(gtk_widget_get_display(GTK_WIDGET(display)))) {
hotspot_x /= scale_factor;
hotspot_y /= scale_factor;
}
#endif
cursor = gdk_cursor_new_from_surface(gtk_widget_get_display(GTK_WIDGET(display)),
d->cursor_surface,
hotspot_x,
hotspot_y);
#if HAVE_EGL
if (egl_enabled(d))
spice_egl_cursor_set(display);
#endif
if (d->show_cursor) {
/* unhide */
g_clear_object(&d->show_cursor);
if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER) {
/* keep a hidden cursor, will be shown in cursor_move() */
d->show_cursor = cursor;
return;
}
}
if (d->mouse_cursor != NULL) {
g_object_unref(d->mouse_cursor);
}
d->mouse_cursor = cursor;
update_mouse_pointer(display);
cursor_invalidate(display);
}