static void spice_display_init(SpiceDisplay *display)
{
GtkWidget *widget = GTK_WIDGET(display);
GtkWidget *area;
SpiceDisplayPrivate *d;
GtkTargetEntry targets = { "text/uri-list", 0, 0 };
d = display->priv = spice_display_get_instance_private(display);
d->stack = GTK_STACK(gtk_stack_new());
gtk_container_add(GTK_CONTAINER(display), GTK_WIDGET(d->stack));
//画的区域
area = gtk_drawing_area_new();
g_object_connect(area,
"signal::draw", draw_event, display,
"signal::realize", drawing_area_realize, display,
NULL);
gtk_stack_add_named(d->stack, area, "draw-area");
gtk_stack_set_visible_child(d->stack, area);
#if HAVE_EGL
area = gtk_gl_area_new();
gtk_gl_area_set_required_version(GTK_GL_AREA(area), 3, 2);
gtk_gl_area_set_auto_render(GTK_GL_AREA(area), false);
g_object_connect(area,
"signal::render", gl_area_render, display,
"signal::realize", gl_area_realize, display,
NULL);
gtk_stack_add_named(d->stack, area, "gl-area");
#endif
area = gtk_drawing_area_new();
gtk_stack_add_named(d->stack, area, "gst-area");
g_object_connect(area,
"signal::draw", gst_draw_event, display,
"signal::size-allocate", gst_size_allocate, display,
NULL);
d->label = gtk_label_new(NULL);
gtk_label_set_selectable(GTK_LABEL(d->label), true);
gtk_stack_add_named(d->stack, d->label, "label");
gtk_widget_show_all(widget);
g_signal_connect(display, "grab-broken-event", G_CALLBACK(grab_broken), NULL);
g_signal_connect(display, "grab-notify", G_CALLBACK(grab_notify), NULL);
gtk_drag_dest_set(widget, GTK_DEST_DEFAULT_ALL, &targets, 1, GDK_ACTION_COPY);
g_signal_connect(display, "drag-data-received",
G_CALLBACK(drag_data_received_callback), NULL);
g_signal_connect(display, "size-allocate", G_CALLBACK(size_allocate), NULL);
gtk_widget_add_events(widget,
GDK_POINTER_MOTION_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_BUTTON_MOTION_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK |
GDK_KEY_PRESS_MASK |
/* on Wayland, only smooth-scroll events are emitted */
GDK_SMOOTH_SCROLL_MASK |
GDK_SCROLL_MASK);
gtk_widget_set_can_focus(widget, true);
gtk_event_box_set_above_child(GTK_EVENT_BOX(widget), true);
d->grabseq = spice_grab_sequence_new_from_string("Control_L+Alt_L");
d->activeseq = g_new0(gboolean, d->grabseq->nkeysyms);
#ifdef HAVE_WAYLAND_PROTOCOLS
if GDK_IS_WAYLAND_DISPLAY(gtk_widget_get_display(widget))
spice_wayland_extensions_init(widget);
#endif
}
2、spice-lib告诉spice-gtk显示区域进行刷新
static void invalidate(SpiceChannel *channel, gint x, gint y, gint w, gint h, gpointer data)
{
SpiceDisplay *display = data;
SpiceDisplayPrivate *d = display->priv;
int display_x, display_y;
int x1, y1, x2, y2;
double s;
gint scale_factor;
GdkRectangle rect = {
.x = x,
.y = y,
.width = w,
.height = h
};
#if HAVE_EGL
set_egl_enabled(display, false);
#endif
if (!gtk_widget_get_window(GTK_WIDGET(display)))
return;
if (!gdk_rectangle_intersect(&rect, &d->area, &rect))
return;
if (d->canvas.convert)
do_color_convert(display, &rect);
scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
spice_display_get_scaling(display, &s,
&display_x, &display_y,
NULL, NULL);
display_x /= scale_factor;
display_y /= scale_factor;
if (s * scale_factor > 1) {
rect.x -= 1;
rect.y -= 1;
rect.width += 2;
rect.height += 2;
}
x1 = floor ((rect.x - d->area.x) * s) / scale_factor;
y1 = floor ((rect.y - d->area.y) * s) / scale_factor;
x2 = ceil ((rect.x - d->area.x + rect.width) * s) / scale_factor;
y2 = ceil ((rect.y - d->area.y + rect.height) * s) / scale_factor;
queue_draw_area(display,
display_x + x1, display_y + y1,
x2 - x1, y2 - y1);
}
放入将刷新的区域(x,y,width和height)告诉gtk主线程的画图队列
static void queue_draw_area(SpiceDisplay *display, gint x, gint y,
gint width, gint height)
{
if (!gtk_widget_get_has_window(GTK_WIDGET(display))) {
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(display), &allocation);
x += allocation.x;
y += allocation.y;
}
//触发调用signal::draw信号
gtk_widget_queue_draw_area(GTK_WIDGET(display),x, y, width, height);
}
3、gtk主线程调用画图事件回调函数
draw_event
static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer data)
{
SpiceDisplay *display = SPICE_DISPLAY(data);
SpiceDisplayPrivate *d = display->priv;
g_return_val_if_fail(d != NULL, false);
#if HAVE_EGL
if (egl_enabled(d) &&
g_str_equal(gtk_stack_get_visible_child_name(d->stack), "draw-area")) {
spice_egl_update_display(display);
return false;
}
#endif
if (d->mark == 0 || d->canvas.data == NULL ||
d->area.width == 0 || d->area.height == 0)
return false;
spice_cairo_draw_event(display, cr);
update_mouse_pointer(display);
return true;
}
4、调用cairo库最终画图片,图片数据从
d->canvas.surface中获取
G_GNUC_INTERNAL
void spice_cairo_draw_event(SpiceDisplay *display, cairo_t *cr)
{
SpiceDisplayPrivate *d = display->priv;
cairo_rectangle_int_t rect;
cairo_region_t *region;
double s;
int x, y;
int ww, wh;
int w, h;
gint scale_factor;
scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
spice_display_get_scaling(display, &s, &x, &y, &w, &h);
/* convert physical pixel to logical */
x /= scale_factor;
y /= scale_factor;
w /= scale_factor;
h /= scale_factor;
ww = gtk_widget_get_allocated_width(GTK_WIDGET(display));
wh = gtk_widget_get_allocated_height(GTK_WIDGET(display));
/* We need to paint the bg color around the image */
rect.x = 0;
rect.y = 0;
rect.width = ww;
rect.height = wh;
region = cairo_region_create_rectangle(&rect);
/* Optionally cut out the inner area where the pixmap
will be drawn. This avoids 'flashing' since we're
not double-buffering. */
if (d->canvas.surface) {
rect.x = x;
rect.y = y;
rect.width = w;
rect.height = h;
cairo_region_subtract_rectangle(region, &rect);
}
gdk_cairo_region (cr, region);
cairo_region_destroy (region);
/* Need to set a real solid color, because the default is usually
transparent these days, and non-double buffered windows can't
render transparently */
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_fill(cr);
/* Draw the display */
if (d->canvas.surface) {
cairo_translate(cr, x, y);
cairo_rectangle(cr, 0, 0, w, h);
cairo_scale(cr, s, s);
if (!d->canvas.convert)
cairo_translate(cr, -d->area.x, -d->area.y);
cairo_set_source_surface(cr, d->canvas.surface, 0, 0);
cairo_fill(cr);
if (d->mouse_mode == SPICE_MOUSE_MODE_SERVER &&
d->mouse_guest_x != -1 && d->mouse_guest_y != -1 &&
!d->show_cursor &&
spice_gtk_session_get_pointer_grabbed(d->gtk_session)) {
cairo_surface_t *surface = d->cursor_surface;
if (surface != NULL) {
cairo_set_source_surface(cr, surface,
(double)(d->mouse_guest_x - d->mouse_hotspot.x) / scale_factor,
(double)(d->mouse_guest_y - d->mouse_hotspot.y) / scale_factor);
cairo_paint(cr);
}
}
}
}
5、绑定数据
5.1、在更新图片创建cairo对象(spice-widget-cairo.c)
static void update_area(SpiceDisplay *display,
gint x, gint y, gint width, gint height)
{
SpiceDisplayPrivate *d = display->priv;
GdkRectangle primary;
DISPLAY_DEBUG(display, "update area +%d+%d %dx%d", x, y, width, height);
d->area = (GdkRectangle) {
.x = x,
.y = y,
.width = width,
.height = height
};
#if HAVE_EGL
if (egl_enabled(d)) {
const SpiceGlScanout *so =
spice_display_channel_get_gl_scanout(d->display);
g_return_if_fail(so != NULL);
primary = (GdkRectangle) {
.width = so->width,
.height = so->height
};
} else
#endif
{
primary = (GdkRectangle) {
.width = d->canvas.width,
.height = d->canvas.height
};
}
DISPLAY_DEBUG(display, "primary: %dx%d", primary.width, primary.height);
if (!gdk_rectangle_intersect(&primary, &d->area, &d->area)) {
DISPLAY_DEBUG(display, "The monitor area is not intersecting primary surface");
memset(&d->area, '\0', sizeof(d->area));
set_monitor_ready(display, false);
return;
}
if (!egl_enabled(d)) {
spice_cairo_image_destroy(display);
if (gtk_widget_get_realized(GTK_WIDGET(display)))
update_image(display);
}
update_size_request(display);
set_monitor_ready(display, true);
}
5.2、创建cairo的image
static void update_image(SpiceDisplay *display)
{
SpiceDisplayPrivate *d = display->priv;
spice_cairo_image_create(display);
if (d->canvas.convert)
do_color_convert(display, &d->area);
}
5.3、cairo_image_surface_create_for_data通过
d->canvas.data
数据创建cairo的操作对象
d->canvas.surface
G_GNUC_INTERNAL
int spice_cairo_image_create(SpiceDisplay *display)
{
SpiceDisplayPrivate *d = display->priv;
gint scale_factor;
if (d->canvas.surface != NULL)
return 0;
if (d->canvas.format == SPICE_SURFACE_FMT_16_555 ||
d->canvas.format == SPICE_SURFACE_FMT_16_565) {
d->canvas.convert = TRUE;
d->canvas.data = g_malloc0(d->area.width * d->area.height * 4);
d->canvas.surface = cairo_image_surface_create_for_data
(d->canvas.data, CAIRO_FORMAT_RGB24,
d->area.width, d->area.height, d->area.width * 4);
} else {
d->canvas.convert = FALSE;
d->canvas.surface = cairo_image_surface_create_for_data
(d->canvas.data, CAIRO_FORMAT_RGB24,
d->canvas.width, d->canvas.height, d->canvas.stride);
}
scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(display));
cairo_surface_set_device_scale(d->canvas.surface, scale_factor, scale_factor);
return 0;
}