[Spice-commits] 26 commits - doc/reference gtk/channel-display.c gtk/channel-display.h gtk/channel-display-priv.h gtk/channel-main.c gtk/glib-compat.h gtk/map-file gtk/spice-channel.c gtk/spice-widget.c gtk/spice-widget-cairo.c gtk/spice-widget.h gtk/spice-widget-priv.h gtk/spice-widget-x11.c gtk/spicy.c spice-common
Marc-André Lureau
elmarco at kemper.freedesktop.org
Mon Jul 16 09:00:56 PDT 2012
doc/reference/spice-gtk-sections.txt | 1
gtk/channel-display-priv.h | 2
gtk/channel-display.c | 141 ++++++++++
gtk/channel-display.h | 24 +
gtk/channel-main.c | 95 ++++++-
gtk/glib-compat.h | 20 +
gtk/map-file | 2
gtk/spice-channel.c | 2
gtk/spice-widget-cairo.c | 37 +-
gtk/spice-widget-priv.h | 5
gtk/spice-widget-x11.c | 22 -
gtk/spice-widget.c | 470 ++++++++++++++++++++++-------------
gtk/spice-widget.h | 4
gtk/spicy.c | 115 +++++++-
spice-common | 2
15 files changed, 720 insertions(+), 222 deletions(-)
New commits:
commit fdb8813a438d801368f9521253f44cc12061d4dd
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Thu Jul 5 01:29:44 2012 +0200
Enable the display early when the widget is created
The spice_main_set_display_enabled() function is used to mark the
display/monitor config as enabled. In order to simplify client
implementation, the widget enables the matching display automatically
when the channel mark is received. This is only for legacy reason, and
my lack of understanding at that time. It could as well be enabled
earlier, when the widget is created. It wasn't really a good decision
to disable monitor when the mark is off, which can be toggled when the
primary surface is resize for example, and can cause some races..
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 5c12db9..02bb089 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -1790,7 +1790,6 @@ static void mark(SpiceDisplay *display, gint mark)
SPICE_DEBUG("widget mark: %d, %d:%d %p", mark, d->channel_id, d->monitor_id, display);
d->mark = mark;
- spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
update_ready(display);
}
@@ -1963,6 +1962,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
mark(display, primary.marked);
}
spice_channel_connect(channel);
+ spice_main_set_display_enabled(d->main, get_display_id(display), TRUE);
return;
}
commit 2a2900267f4f1b94213e856c793509d897cb50cd
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Thu Jul 5 01:24:44 2012 +0200
Add SpiceDisplay:ready property
There are several condition to meet in order to have a widget ready to
be displayed: the monitor must exist, the area must intersect, and the
display mark must be reached. This property will help clients to know
when the widget display is ready. Until now, it was relying on the
channel mark signal only, and had to deal with the rest of the
conditions without much help.
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index a44e5fe..87cf34e 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -52,6 +52,8 @@ struct _SpiceDisplayPrivate {
bool resize_guest_enable;
/* state */
+ gboolean ready;
+ gboolean monitor_ready;
enum SpiceSurfaceFmt format;
gint width, height, stride;
gint shmid;
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 048770c..5c12db9 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -102,7 +102,8 @@ enum {
PROP_SCALING,
PROP_DISABLE_INPUTS,
PROP_ZOOM_LEVEL,
- PROP_MONITOR_ID
+ PROP_MONITOR_ID,
+ PROP_READY
};
/* Signals */
@@ -175,6 +176,9 @@ static void spice_display_get_property(GObject *object,
case PROP_ZOOM_LEVEL:
g_value_set_int(value, d->zoom_level);
break;
+ case PROP_READY:
+ g_value_set_boolean(value, d->ready);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -227,6 +231,31 @@ static void update_keyboard_focus(SpiceDisplay *display, gboolean state)
spice_gtk_session_request_auto_usbredir(d->gtk_session, state);
}
+static void update_ready(SpiceDisplay *display)
+{
+ SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ gboolean ready;
+
+ ready = d->mark != 0 && d->monitor_ready;
+
+ if (d->ready == ready)
+ return;
+
+ if (ready && gtk_widget_get_window(GTK_WIDGET(display)))
+ gtk_widget_queue_draw(GTK_WIDGET(display));
+
+ d->ready = ready;
+ g_object_notify(G_OBJECT(display), "ready");
+}
+
+static void set_monitor_ready(SpiceDisplay *self, gboolean ready)
+{
+ SpiceDisplayPrivate *d = self->priv;
+
+ d->monitor_ready = ready;
+ update_ready(self);
+}
+
static void update_monitor_area(SpiceDisplay *display)
{
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
@@ -240,11 +269,11 @@ static void update_monitor_area(SpiceDisplay *display)
g_object_get(d->display, "monitors", &monitors, NULL);
if (monitors == NULL || d->monitor_id >= monitors->len) {
SPICE_DEBUG("update monitor: no monitor %d", d->monitor_id);
+ set_monitor_ready(display, false);
if (spice_channel_test_capability(d->display, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
SPICE_DEBUG("waiting until MonitorsConfig is received");
return;
}
- /* FIXME: mark false */
goto whole;
}
@@ -263,6 +292,7 @@ static void update_monitor_area(SpiceDisplay *display)
whole:
/* by display whole surface */
update_area(display, 0, 0, d->width, d->height);
+ set_monitor_ready(display, true);
}
static void spice_display_set_property(GObject *object,
@@ -1477,6 +1507,24 @@ static void spice_display_class_init(SpiceDisplayClass *klass)
G_PARAM_STATIC_STRINGS));
/**
+ * SpiceDisplay:ready:
+ *
+ * Indicate whether the display is ready to be shown. It takes
+ * into account several conditions, such as the channel display
+ * "mark" state, whether the monitor area is visible..
+ *
+ * Since: 0.13
+ **/
+ g_object_class_install_property
+ (gobject_class, PROP_READY,
+ g_param_spec_boolean("ready",
+ "Ready",
+ "Ready to display",
+ FALSE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* SpiceDisplay:auto-clipboard:
*
* When this is true the clipboard gets automatically shared between host
@@ -1660,7 +1708,7 @@ static void update_area(SpiceDisplay *display,
if (!gdk_rectangle_intersect(&primary, &area, &area)) {
SPICE_DEBUG("The monitor area is not intersecting primary surface");
memset(&d->area, '\0', sizeof(d->area));
- /* FIXME mark false? */
+ set_monitor_ready(display, false);
return;
}
@@ -1669,7 +1717,7 @@ static void update_area(SpiceDisplay *display,
spicex_image_create(display);
update_size_request(display);
- gtk_widget_queue_draw(GTK_WIDGET(display));
+ set_monitor_ready(display, true);
}
static void primary_create(SpiceChannel *channel, gint format,
@@ -1701,6 +1749,7 @@ static void primary_destroy(SpiceChannel *channel, gpointer data)
d->shmid = 0;
d->data = NULL;
d->data_origin = NULL;
+ set_monitor_ready(display, false);
}
static void invalidate(SpiceChannel *channel,
@@ -1742,8 +1791,7 @@ static void mark(SpiceDisplay *display, gint mark)
SPICE_DEBUG("widget mark: %d, %d:%d %p", mark, d->channel_id, d->monitor_id, display);
d->mark = mark;
spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
- if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
- gtk_widget_queue_draw(GTK_WIDGET(display));
+ update_ready(display);
}
static void cursor_set(SpiceCursorChannel *channel,
commit fe842771b2e2400fbc5ba8efd74550cde44276ee
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 27 17:06:34 2012 +0200
Handle MonitorsConfig::max_allowed
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 637a6cf..d77447a 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -72,6 +72,7 @@ struct _SpiceDisplayChannelPrivate {
gboolean mark;
guint mark_false_event_id;
GArray *monitors;
+ guint monitors_max;
#ifdef WIN32
HDC dc;
#endif
@@ -84,7 +85,8 @@ enum {
PROP_0,
PROP_WIDTH,
PROP_HEIGHT,
- PROP_MONITORS
+ PROP_MONITORS,
+ PROP_MONITORS_MAX
};
enum {
@@ -177,6 +179,10 @@ static void spice_display_get_property(GObject *object,
g_value_set_boxed(value, c->monitors);
break;
}
+ case PROP_MONITORS_MAX: {
+ g_value_set_uint(value, c->monitors_max);
+ break;
+ }
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -256,6 +262,24 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
G_PARAM_STATIC_STRINGS));
/**
+ * SpiceDisplayChannel:monitors-max:
+ *
+ * The maximum number of monitors the server or guest supports.
+ * May change during client lifetime, for instance guest may
+ * reboot or dynamically adjust this.
+ *
+ * Since: 0.13
+ */
+ g_object_class_install_property
+ (gobject_class, PROP_MONITORS_MAX,
+ g_param_spec_uint("monitors-max",
+ "Max display monitors",
+ "The current maximum number of monitors",
+ 1, G_MAXINT16, 1,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* SpiceDisplayChannel::display-primary-create:
* @display: the #SpiceDisplayChannel that emitted the signal
* @format: %SPICE_SURFACE_FMT_32_xRGB or %SPICE_SURFACE_FMT_16_555;
@@ -1479,8 +1503,9 @@ static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in
g_return_if_fail(config != NULL);
g_return_if_fail(config->count > 0);
- SPICE_DEBUG("monitors config: n: %d", config->count);
+ SPICE_DEBUG("monitors config: n: %d/%d", config->count, config->max_allowed);
+ c->monitors_max = config->max_allowed;
c->monitors = g_array_set_size(c->monitors, config->count);
for (i = 0; i < config->count; i++) {
commit 4ea133feb82c83a0ed0daaad381101b0afd5f3a5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Tue Jun 26 21:49:37 2012 +0200
main: send monitor config immediately
The only way this can be called currently is via the main/system
coroutine. Remove display timer if any.
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index d7830ae..0c15dfa 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -916,8 +916,6 @@ static void monitors_align(VDAgentMonConfig *monitors, int nmonitors)
*
* Returns: %TRUE on success.
**/
-/* any context: the message is not flushed immediately,
- you can wakeup() the channel coroutine or send_msg_queue() */
gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
{
SpiceMainChannelPrivate *c;
@@ -966,6 +964,12 @@ gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
agent_msg_queue(channel, VD_AGENT_MONITORS_CONFIG, size, mon);
free(mon);
+ spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE);
+ if (c->timer_id != 0) {
+ g_source_remove(c->timer_id);
+ c->timer_id = 0;
+ }
+
return TRUE;
}
@@ -1862,7 +1866,6 @@ static gboolean timer_set_display(gpointer data)
c->timer_id = 0;
if (c->agent_connected)
spice_main_send_monitor_config(SPICE_MAIN_CHANNEL(channel));
- spice_channel_wakeup(channel, FALSE);
return false;
}
commit be8ff99571478deb5c8d116134f65ed2b788dbd3
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 18:47:05 2012 +0200
Implement simple monitors alignment
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 3c173c6..d7830ae 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -15,6 +15,10 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
+#include <math.h>
+#include <spice/vd_agent.h>
+#include <common/rect.h>
+
#include "glib-compat.h"
#include "spice-client.h"
#include "spice-common.h"
@@ -24,8 +28,6 @@
#include "spice-channel-priv.h"
#include "spice-session-priv.h"
-#include <spice/vd_agent.h>
-
/**
* SECTION:channel-main
* @short_description: the main Spice channel
@@ -58,6 +60,7 @@ struct _SpiceMainChannelPrivate {
gboolean display_disable_font_smooth:1;
gboolean display_disable_animation:1;
gboolean disable_display_position:1;
+ gboolean disable_display_align:1;
int agent_tokens;
VDAgentMessage agent_msg; /* partial msg reconstruction */
@@ -102,6 +105,7 @@ enum {
PROP_DISPLAY_DISABLE_ANIMATION,
PROP_DISPLAY_COLOR_DEPTH,
PROP_DISABLE_DISPLAY_POSITION,
+ PROP_DISABLE_DISPLAY_ALIGN,
};
/* Signals */
@@ -203,6 +207,9 @@ static void spice_main_get_property(GObject *object,
case PROP_DISABLE_DISPLAY_POSITION:
g_value_set_boolean(value, c->disable_display_position);
break;
+ case PROP_DISABLE_DISPLAY_ALIGN:
+ g_value_set_boolean(value, c->disable_display_align);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -233,6 +240,9 @@ static void spice_main_set_property(GObject *gobject, guint prop_id,
case PROP_DISABLE_DISPLAY_POSITION:
c->disable_display_position = g_value_get_boolean(value);
break;
+ case PROP_DISABLE_DISPLAY_ALIGN:
+ c->disable_display_align = g_value_get_boolean(value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
break;
@@ -410,6 +420,23 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass)
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+ /**
+ * SpiceMainChannel:disable-display-align:
+ *
+ * Disable automatic horizontal display position alignment.
+ *
+ * Since: 0.13
+ */
+ g_object_class_install_property
+ (gobject_class, PROP_DISABLE_DISPLAY_ALIGN,
+ g_param_spec_boolean("disable-display-align",
+ "Disable display align",
+ "Disable display position alignment",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
/* TODO use notify instead */
/**
* SpiceMainChannel::main-mouse-update:
@@ -846,6 +873,37 @@ static void agent_msg_queue_many(SpiceMainChannel *channel, int type, const void
g_warn_if_fail(out == NULL);
}
+static int monitors_cmp(const void *p1, const void *p2)
+{
+ const VDAgentMonConfig *m1 = p1;
+ const VDAgentMonConfig *m2 = p2;
+ double d1 = sqrt(m1->x * m1->x + m1->y * m1->y);
+ double d2 = sqrt(m2->x * m2->x + m2->y * m2->y);
+
+ return d1 - d2;
+}
+
+static void monitors_align(VDAgentMonConfig *monitors, int nmonitors)
+{
+ gint i, x = 0;
+
+ if (nmonitors == 0)
+ return;
+
+ /* sort by distance from origin */
+ qsort(monitors, nmonitors, sizeof(VDAgentMonConfig), monitors_cmp);
+
+ /* super-KISS ltr alignment, feel free to improve */
+ for (i = 0; i < nmonitors; i++) {
+ monitors[i].x = x;
+ monitors[i].y = 0;
+ x += monitors[i].width;
+ g_debug("#%d +%d+%d-%dx%d", i, monitors[i].x, monitors[i].y,
+ monitors[i].width, monitors[i].height);
+ }
+}
+
+
#define agent_msg_queue(Channel, Type, Size, Data) \
agent_msg_queue_many((Channel), (Type), (Data), (Size), NULL)
@@ -882,7 +940,8 @@ gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
mon = spice_malloc0(size);
mon->num_of_monitors = monitors;
- if (c->disable_display_position == FALSE)
+ if (c->disable_display_position == FALSE ||
+ c->disable_display_align == FALSE)
mon->flags |= VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS;
j = 0;
@@ -901,6 +960,9 @@ gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
j++;
}
+ if (c->disable_display_align == FALSE)
+ monitors_align(mon->monitors, mon->num_of_monitors);
+
agent_msg_queue(channel, VD_AGENT_MONITORS_CONFIG, size, mon);
free(mon);
@@ -1845,6 +1907,10 @@ void spice_main_set_display(SpiceMainChannel *channel, int id,
g_return_if_fail(channel != NULL);
g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel));
+ g_return_if_fail(x >= 0);
+ g_return_if_fail(y >= 0);
+ g_return_if_fail(width >= 0);
+ g_return_if_fail(height >= 0);
c = SPICE_MAIN_CHANNEL(channel)->priv;
commit 547597a687065b70b9065c38ba1f393351bb92b1
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 15:16:43 2012 +0200
widget: use display monitors configuration
Use display::monitors property to manage display area. Call
update_area_monitor() to update the widget area depending on monitor
configuration
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index d33e79e..048770c 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -130,6 +130,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data);
static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data);
static void sync_keyboard_lock_modifiers(SpiceDisplay *display);
static void cursor_invalidate(SpiceDisplay *display);
+static void update_area(SpiceDisplay *display, gint x, gint y, gint width, gint height);
/* ---------------------------------------------------------------- */
@@ -226,6 +227,44 @@ static void update_keyboard_focus(SpiceDisplay *display, gboolean state)
spice_gtk_session_request_auto_usbredir(d->gtk_session, state);
}
+static void update_monitor_area(SpiceDisplay *display)
+{
+ SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ SpiceDisplayMonitorConfig *c;
+ GArray *monitors = NULL;
+
+ SPICE_DEBUG("update monitor area %d:%d", d->channel_id, d->monitor_id);
+ if (d->monitor_id < 0)
+ goto whole;
+
+ g_object_get(d->display, "monitors", &monitors, NULL);
+ if (monitors == NULL || d->monitor_id >= monitors->len) {
+ SPICE_DEBUG("update monitor: no monitor %d", d->monitor_id);
+ if (spice_channel_test_capability(d->display, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
+ SPICE_DEBUG("waiting until MonitorsConfig is received");
+ return;
+ }
+ /* FIXME: mark false */
+ goto whole;
+ }
+
+ c = &g_array_index(monitors, SpiceDisplayMonitorConfig, d->monitor_id);
+ g_return_if_fail(c != NULL);
+ if (c->surface_id != 0) {
+ g_warning("FIXME: only support monitor config with primary surface 0, "
+ "but given config surface %d", c->surface_id);
+ goto whole;
+ }
+
+ update_area(display, c->x, c->y, c->width, c->height);
+ g_clear_pointer(&monitors, g_array_unref);
+ return;
+
+whole:
+ /* by display whole surface */
+ update_area(display, 0, 0, d->width, d->height);
+}
+
static void spice_display_set_property(GObject *object,
guint prop_id,
const GValue *value,
@@ -245,6 +284,8 @@ static void spice_display_set_property(GObject *object,
break;
case PROP_MONITOR_ID:
d->monitor_id = g_value_get_int(value);
+ if (d->display) /* if constructed */
+ update_monitor_area(display);
break;
case PROP_KEYBOARD_GRAB:
d->keyboard_grab_enable = g_value_get_boolean(value);
@@ -808,13 +849,13 @@ static void recalc_geometry(GtkWidget *widget)
} else
zoom = (gdouble)d->zoom_level / 100;
- SPICE_DEBUG("monitors: id %d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
- d->channel_id, d->area.x, d->area.y, d->area.width, d->area.height,
+ SPICE_DEBUG("recalc geom monitor: %d:%d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
+ d->channel_id, d->monitor_id, d->area.x, d->area.y, d->area.width, d->area.height,
d->ww, d->wh, zoom, d->mx, d->my);
if (d->resize_guest_enable)
spice_main_set_display(d->main, get_display_id(display),
- 0, 0, d->ww / zoom, d->wh / zoom);
+ d->area.x, d->area.y, d->ww / zoom, d->wh / zoom);
}
/* ---------------------------------------------------------------- */
@@ -1645,8 +1686,7 @@ static void primary_create(SpiceChannel *channel, gint format,
d->height = height;
d->data_origin = d->data = imgdata;
- /* by default, display whole surface */
- update_area(display, 0, 0, width, height);
+ update_monitor_area(display);
}
static void primary_destroy(SpiceChannel *channel, gpointer data)
@@ -1694,13 +1734,12 @@ static void invalidate(SpiceChannel *channel,
x, y, w, h);
}
-static void mark(SpiceChannel *channel, gint mark, gpointer data)
+static void mark(SpiceDisplay *display, gint mark)
{
- SpiceDisplay *display = data;
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
g_return_if_fail(d != NULL);
- SPICE_DEBUG("widget mark: %d, channel %d", mark, d->channel_id);
+ SPICE_DEBUG("widget mark: %d, %d:%d %p", mark, d->channel_id, d->monitor_id, display);
d->mark = mark;
spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
@@ -1856,6 +1895,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
}
if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
+ SpiceDisplayPrimary primary;
if (id != d->channel_id)
return;
d->display = channel;
@@ -1866,7 +1906,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
spice_g_signal_connect_object(channel, "display-invalidate",
G_CALLBACK(invalidate), display, 0);
spice_g_signal_connect_object(channel, "display-mark",
- G_CALLBACK(mark), display, 0);
+ G_CALLBACK(mark), display, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+ spice_g_signal_connect_object(channel, "notify::monitors",
+ G_CALLBACK(update_monitor_area), display, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
+ if (spice_display_get_primary(channel, 0, &primary)) {
+ primary_create(channel, primary.format, primary.width, primary.height,
+ primary.stride, primary.shmid, primary.data, display);
+ mark(display, primary.marked);
+ }
spice_channel_connect(channel);
return;
}
commit 98287e581330dbdd81c3ef73d46c41f6658a3638
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Tue Jun 19 15:46:36 2012 +0200
spicy: disable display when deleting window
diff --git a/gtk/spicy.c b/gtk/spicy.c
index 0571cf7..5510f0c 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -97,6 +97,7 @@ G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT);
struct spice_connection {
SpiceSession *session;
SpiceGtkSession *gtk_session;
+ SpiceMainChannel *main;
SpiceWindow *wins[CHANNELID_MAX * MONITORID_MAX];
SpiceAudio *audio;
const char *mouse_state;
@@ -117,6 +118,7 @@ static void usb_connect_failed(GObject *object,
GError *error,
gpointer data);
static gboolean is_gtk_session_property(const gchar *property);
+static void del_window(spice_connection *conn, SpiceWindow *win);
/* options */
static gboolean fullscreen = false;
@@ -561,7 +563,11 @@ static gboolean delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{
SpiceWindow *win = data;
- connection_disconnect(win->conn);
+ if (win->monitor_id == 0)
+ connection_disconnect(win->conn);
+ else
+ del_window(win->conn, win);
+
return true;
}
@@ -1530,6 +1536,12 @@ static void del_window(spice_connection *conn, SpiceWindow *win)
g_debug("del display monitor %d:%d", win->id, win->monitor_id);
conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
+ if (win->id > 0)
+ spice_main_set_display_enabled(conn->main, win->id, FALSE);
+ else
+ spice_main_set_display_enabled(conn->main, win->monitor_id, FALSE);
+ spice_main_send_monitor_config(conn->main);
+
destroy_spice_window(win);
}
@@ -1576,6 +1588,7 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
if (SPICE_IS_MAIN_CHANNEL(channel)) {
SPICE_DEBUG("new main channel");
+ conn->main = SPICE_MAIN_CHANNEL(channel);
g_signal_connect(channel, "channel-event",
G_CALLBACK(main_channel_event), conn);
g_signal_connect(channel, "main-mouse-update",
@@ -1621,6 +1634,7 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
g_object_get(channel, "channel-id", &id, NULL);
if (SPICE_IS_MAIN_CHANNEL(channel)) {
SPICE_DEBUG("zap main channel");
+ conn->main = NULL;
}
if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
commit 569b15e1618e8740da5d89c73779854ae620227d
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 14:42:13 2012 +0200
spicy: learn to deal with monitors
diff --git a/gtk/spicy.c b/gtk/spicy.c
index 5ffe3b7..0571cf7 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -32,6 +32,7 @@
#include "smartcard-manager.h"
#endif
+#include "glib-compat.h"
#include "spice-widget.h"
#include "spice-gtk-session.h"
#include "spice-audio.h"
@@ -62,7 +63,8 @@ typedef struct _SpiceWindowClass SpiceWindowClass;
struct _SpiceWindow {
GObject object;
spice_connection *conn;
- int id;
+ gint id;
+ gint monitor_id;
GtkWidget *toplevel, *spice;
GtkWidget *menubar, *toolbar;
GtkWidget *ritem, *rmenu;
@@ -87,10 +89,15 @@ struct _SpiceWindowClass
G_DEFINE_TYPE (SpiceWindow, spice_window, G_TYPE_OBJECT);
+#define CHANNELID_MAX 4
+#define MONITORID_MAX 4
+
+// FIXME: turn this into an object, get rid of fixed wins array, use
+// signals to replace the various callback that iterate over wins array
struct spice_connection {
SpiceSession *session;
SpiceGtkSession *gtk_session;
- SpiceWindow *wins[4];
+ SpiceWindow *wins[CHANNELID_MAX * MONITORID_MAX];
SpiceAudio *audio;
const char *mouse_state;
const char *agent_state;
@@ -1141,7 +1148,7 @@ spice_window_init (SpiceWindow *self)
{
}
-static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceChannel *channel)
+static SpiceWindow *create_spice_window(spice_connection *conn, SpiceChannel *channel, int id, gint monitor_id)
{
char title[32];
SpiceWindow *win;
@@ -1154,12 +1161,13 @@ static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceCha
win = g_object_new(SPICE_TYPE_WINDOW, NULL);
win->id = id;
+ win->monitor_id = monitor_id;
win->conn = conn;
win->display_channel = channel;
/* toplevel */
win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- snprintf(title, sizeof(title), _("spice display %d"), id);
+ snprintf(title, sizeof(title), _("spice display %d:%d"), id, monitor_id);
gtk_window_set_title(GTK_WINDOW(win->toplevel), title);
g_signal_connect(G_OBJECT(win->toplevel), "window-state-event",
G_CALLBACK(window_state_cb), win);
@@ -1204,7 +1212,7 @@ static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceCha
#endif
/* spice display */
- win->spice = GTK_WIDGET(spice_display_new(conn->session, id));
+ win->spice = GTK_WIDGET(spice_display_new_with_monitor(conn->session, id, monitor_id));
g_signal_connect(win->spice, "configure-event", G_CALLBACK(configure_event_cb), win);
seq = spice_grab_sequence_new_from_string("Shift_L+F12");
spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
@@ -1324,7 +1332,10 @@ static SpiceWindow *create_spice_window(spice_connection *conn, int id, SpiceCha
static void destroy_spice_window(SpiceWindow *win)
{
- SPICE_DEBUG("destroy window (#%d)", win->id);
+ if (win == NULL)
+ return;
+
+ SPICE_DEBUG("destroy window (#%d:%d)", win->id, win->monitor_id);
g_object_unref(win->ag);
g_object_unref(win->ui);
gtk_widget_destroy(win->toplevel);
@@ -1490,6 +1501,70 @@ static void update_auto_usbredir_sensitive(spice_connection *conn)
#endif
}
+static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id)
+{
+ g_return_val_if_fail(channel_id < CHANNELID_MAX, NULL);
+ g_return_val_if_fail(monitor_id < MONITORID_MAX, NULL);
+
+ return conn->wins[channel_id * CHANNELID_MAX + monitor_id];
+}
+
+static void add_window(spice_connection *conn, SpiceWindow *win)
+{
+ g_return_if_fail(win != NULL);
+ g_return_if_fail(win->id < CHANNELID_MAX);
+ g_return_if_fail(win->monitor_id < MONITORID_MAX);
+ g_return_if_fail(conn->wins[win->id * CHANNELID_MAX + win->monitor_id] == NULL);
+
+ SPICE_DEBUG("add display monitor %d:%d", win->id, win->monitor_id);
+ conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = win;
+}
+
+static void del_window(spice_connection *conn, SpiceWindow *win)
+{
+ if (win == NULL)
+ return;
+
+ g_return_if_fail(win->id < CHANNELID_MAX);
+ g_return_if_fail(win->monitor_id < MONITORID_MAX);
+
+ g_debug("del display monitor %d:%d", win->id, win->monitor_id);
+ conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
+ destroy_spice_window(win);
+}
+
+static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
+ spice_connection *conn)
+{
+ GArray *monitors = NULL;
+ int id;
+ guint i;
+
+ g_object_get(display,
+ "channel-id", &id,
+ "monitors", &monitors,
+ NULL);
+ g_return_if_fail(monitors != NULL);
+
+ for (i = 0; i < monitors->len; i++) {
+ SpiceWindow *w;
+
+ if (!get_window(conn, id, i)) {
+ w = create_spice_window(conn, display, id, i);
+ add_window(conn, w);
+ spice_g_signal_connect_object(display, "display-mark",
+ G_CALLBACK(display_mark), w, 0);
+ gtk_widget_show(w->toplevel);
+ update_auto_usbredir_sensitive(conn);
+ }
+ }
+
+ for (; i < MONITORID_MAX; i++)
+ del_window(conn, get_window(conn, id, i));
+
+ g_clear_pointer(&monitors, g_array_unref);
+}
+
static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
{
spice_connection *conn = data;
@@ -1517,10 +1592,9 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
if (conn->wins[id] != NULL)
return;
SPICE_DEBUG("new display channel (#%d)", id);
- conn->wins[id] = create_spice_window(conn, id, channel);
- g_signal_connect(channel, "display-mark",
- G_CALLBACK(display_mark), conn->wins[id]);
- update_auto_usbredir_sensitive(conn);
+ g_signal_connect(channel, "notify::monitors",
+ G_CALLBACK(display_monitors), conn);
+ spice_channel_connect(channel);
}
if (SPICE_IS_INPUTS_CHANNEL(channel)) {
@@ -1552,11 +1626,8 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
if (id >= SPICE_N_ELEMENTS(conn->wins))
return;
- if (conn->wins[id] == NULL)
- return;
SPICE_DEBUG("zap display channel (#%d)", id);
- destroy_spice_window(conn->wins[id]);
- conn->wins[id] = NULL;
+ /* FIXME destroy widget only */
}
if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
commit 0b0ec738a9779dc3c37cf0573e3846be0729a5ce
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 14:49:07 2012 +0200
Make-up a MonitorConfig if the server doesn't provide one
This allows easier compatibility for clients that don't have to
check and interact differently depending on channel capabilities.
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 3cd133e..637a6cf 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -744,10 +744,21 @@ static int create_canvas(SpiceChannel *channel, display_surface *surface)
g_return_val_if_fail(surface->canvas != NULL, 0);
ring_add(&c->surfaces, &surface->link);
- if (surface->primary)
+ if (surface->primary) {
emit_main_context(channel, SPICE_DISPLAY_PRIMARY_CREATE,
surface->format, surface->width, surface->height,
surface->stride, surface->shmid, surface->data);
+
+ if (!spice_channel_test_capability(channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
+ g_array_set_size(c->monitors, 1);
+ SpiceDisplayMonitorConfig *config = &g_array_index(c->monitors, SpiceDisplayMonitorConfig, 0);
+ config->x = config->y = 0;
+ config->width = surface->width;
+ config->height = surface->height;
+ g_object_notify_main_context(G_OBJECT(channel), "monitors");
+ }
+ }
+
return 0;
}
commit 6106d1aaa4c4f18ae29865a70aca9212b088f756
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 14:47:26 2012 +0200
Handle SPICE_MSG_DISPLAY_MONITORS_CONFIG
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 675ea14..3cd133e 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -1458,6 +1458,37 @@ static void display_handle_surface_destroy(SpiceChannel *channel, SpiceMsgIn *in
free(surface);
}
+/* coroutine context */
+static void display_handle_monitors_config(SpiceChannel *channel, SpiceMsgIn *in)
+{
+ SpiceMsgDisplayMonitorsConfig *config = spice_msg_in_parsed(in);
+ SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+ guint i;
+
+ g_return_if_fail(config != NULL);
+ g_return_if_fail(config->count > 0);
+
+ SPICE_DEBUG("monitors config: n: %d", config->count);
+
+ c->monitors = g_array_set_size(c->monitors, config->count);
+
+ for (i = 0; i < config->count; i++) {
+ SpiceDisplayMonitorConfig *mc = &g_array_index(c->monitors, SpiceDisplayMonitorConfig, i);
+ SpiceHead *head = &config->heads[i];
+ SPICE_DEBUG("monitor id: %u, surface id: %u, +%u+%u-%ux%u",
+ head->id, head->surface_id,
+ head->x, head->y, head->width, head->height);
+ mc->id = head->id;
+ mc->surface_id = head->surface_id;
+ mc->x = head->x;
+ mc->y = head->y;
+ mc->width = head->width;
+ mc->height = head->height;
+ }
+
+ g_object_notify_main_context(G_OBJECT(channel), "monitors");
+}
+
static const spice_msg_handler display_handlers[] = {
[ SPICE_MSG_DISPLAY_MODE ] = display_handle_mode,
[ SPICE_MSG_DISPLAY_MARK ] = display_handle_mark,
@@ -1490,6 +1521,8 @@ static const spice_msg_handler display_handlers[] = {
[ SPICE_MSG_DISPLAY_SURFACE_CREATE ] = display_handle_surface_create,
[ SPICE_MSG_DISPLAY_SURFACE_DESTROY ] = display_handle_surface_destroy,
+
+ [ SPICE_MSG_DISPLAY_MONITORS_CONFIG ] = display_handle_monitors_config,
};
/* coroutine context */
commit 8ee26756499604c329af98cf8c19daff3d1caa7d
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Tue Jun 19 17:26:32 2012 +0200
Don't attempt to draw an invalid area
If we don't intersect, the area is invalid or of size 0.
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 6635e31..d33e79e 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -878,7 +878,8 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
g_return_val_if_fail(d != NULL, false);
- if (d->mark == 0 || d->data == NULL)
+ if (d->mark == 0 || d->data == NULL ||
+ d->area.width == 0 || d->area.height == 0)
return false;
g_return_val_if_fail(d->ximage != NULL, false);
@@ -894,7 +895,8 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
g_return_val_if_fail(d != NULL, false);
- if (d->mark == 0 || d->data == NULL)
+ if (d->mark == 0 || d->data == NULL ||
+ d->area.width == 0 || d->area.height == 0)
return false;
g_return_val_if_fail(d->ximage != NULL, false);
commit b79f3e148b5c9952a63de80c0a6959dc129c9cc0
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Mon Jun 18 19:27:06 2012 +0200
Use monitor_id to compute display_id
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 9e97b8b..3c173c6 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -1829,7 +1829,7 @@ gboolean spice_main_agent_test_capability(SpiceMainChannel *channel, guint32 cap
/**
* spice_main_set_display:
* @channel:
- * @id: display channel ID
+ * @id: display ID
* @x: x position
* @y: y position
* @width: display width
@@ -2013,7 +2013,7 @@ void spice_main_clipboard_selection_request(SpiceMainChannel *channel, guint sel
/**
* spice_main_set_display_enabled:
* @channel: a #SpiceMainChannel
- * @id: display channel ID (if -1: set all displays)
+ * @id: display ID (if -1: set all displays)
* @enabled: wether display @id is enabled
*
* When sending monitor configuration to agent guest, don't set
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 3febb66..6635e31 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -779,6 +779,19 @@ static void update_mouse_grab(SpiceDisplay *display)
try_mouse_ungrab(display);
}
+static gint get_display_id(SpiceDisplay *display)
+{
+ SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+
+ /* supported monitor_id only with display channel #0 */
+ if (d->channel_id == 0 && d->monitor_id >= 0)
+ return d->monitor_id;
+
+ g_return_val_if_fail(d->monitor_id <= 0, -1);
+
+ return d->channel_id;
+}
+
static void recalc_geometry(GtkWidget *widget)
{
SpiceDisplay *display = SPICE_DISPLAY(widget);
@@ -800,7 +813,7 @@ static void recalc_geometry(GtkWidget *widget)
d->ww, d->wh, zoom, d->mx, d->my);
if (d->resize_guest_enable)
- spice_main_set_display(d->main, d->channel_id,
+ spice_main_set_display(d->main, get_display_id(display),
0, 0, d->ww / zoom, d->wh / zoom);
}
@@ -1687,7 +1700,7 @@ static void mark(SpiceChannel *channel, gint mark, gpointer data)
SPICE_DEBUG("widget mark: %d, channel %d", mark, d->channel_id);
d->mark = mark;
- spice_main_set_display_enabled(d->main, d->channel_id, d->mark != 0);
+ spice_main_set_display_enabled(d->main, get_display_id(display), d->mark != 0);
if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
gtk_widget_queue_draw(GTK_WIDGET(display));
}
commit b1aaa08be76605d68ee1b4a7feb93078c1dff73e
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Mon Jun 18 19:25:48 2012 +0200
Document spice_main_send_monitor_config()
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 1d83bfa..9e97b8b 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -849,6 +849,15 @@ static void agent_msg_queue_many(SpiceMainChannel *channel, int type, const void
#define agent_msg_queue(Channel, Type, Size, Data) \
agent_msg_queue_many((Channel), (Type), (Data), (Size), NULL)
+/**
+ * spice_main_send_monitor_config:
+ * @channel:
+ *
+ * Send monitors configuration previously set with
+ * spice_main_set_display() and spice_main_set_display_enabled()
+ *
+ * Returns: %TRUE on success.
+ **/
/* any context: the message is not flushed immediately,
you can wakeup() the channel coroutine or send_msg_queue() */
gboolean spice_main_send_monitor_config(SpiceMainChannel *channel)
commit 8aacc276c5246a21ad64191e5f8fe47ae0f0f6e3
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 16:40:04 2012 +0200
widget: don't forget to disconnect all signals handlers
We forgot about display-mark. Use spice_g_signal_connect_object()
helper, which will disconnect properly in all circunstances.
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 7f7f17a..3febb66 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -126,9 +126,6 @@ static void update_mouse_grab(SpiceDisplay *display);
static void try_mouse_grab(SpiceDisplay *display);
static void try_mouse_ungrab(SpiceDisplay *display);
static void recalc_geometry(GtkWidget *widget);
-static void disconnect_main(SpiceDisplay *display);
-static void disconnect_cursor(SpiceDisplay *display);
-static void disconnect_display(SpiceDisplay *display);
static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data);
static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data);
static void sync_keyboard_lock_modifiers(SpiceDisplay *display);
@@ -313,21 +310,9 @@ static void spice_display_dispose(GObject *obj)
SPICE_DEBUG("spice display dispose");
- disconnect_main(display);
- disconnect_display(display);
- disconnect_cursor(display);
-
- if (d->session) {
- g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(channel_new),
- display);
- g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(channel_destroy),
- display);
- g_signal_handlers_disconnect_by_func(d->session, G_CALLBACK(session_inhibit_keyboard_grab_changed),
- display);
- g_object_unref(d->session);
- d->session = NULL;
- d->gtk_session = NULL;
- }
+ spicex_image_destroy(display);
+ g_clear_object(&d->session);
+ d->gtk_session = NULL;
G_OBJECT_CLASS(spice_display_parent_class)->dispose(obj);
}
@@ -442,10 +427,10 @@ spice_display_constructor(GType gtype,
if (!d->session)
g_error("SpiceDisplay constructed without a session");
- g_signal_connect(d->session, "channel-new",
- G_CALLBACK(channel_new), display);
- g_signal_connect(d->session, "channel-destroy",
- G_CALLBACK(channel_destroy), display);
+ spice_g_signal_connect_object(d->session, "channel-new",
+ G_CALLBACK(channel_new), display, 0);
+ spice_g_signal_connect_object(d->session, "channel-destroy",
+ G_CALLBACK(channel_destroy), display, 0);
list = spice_session_get_channels(d->session);
for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
if (SPICE_IS_MAIN_CHANNEL(it->data)) {
@@ -462,9 +447,9 @@ spice_display_constructor(GType gtype,
spice_g_signal_connect_object(d->gtk_session, "notify::auto-clipboard",
G_CALLBACK(gtk_session_property_changed), display, 0);
- g_signal_connect(d->session, "notify::inhibit-keyboard-grab",
- G_CALLBACK(session_inhibit_keyboard_grab_changed),
- display);
+ spice_g_signal_connect_object(d->session, "notify::inhibit-keyboard-grab",
+ G_CALLBACK(session_inhibit_keyboard_grab_changed),
+ display, 0);
return obj;
}
@@ -1840,50 +1825,6 @@ static void cursor_reset(SpiceCursorChannel *channel, gpointer data)
gdk_window_set_cursor(window, NULL);
}
-static void disconnect_main(SpiceDisplay *display)
-{
- SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-
- if (d->main == NULL)
- return;
- g_signal_handlers_disconnect_by_func(d->main, G_CALLBACK(update_mouse_mode),
- display);
- d->main = NULL;
-}
-
-static void disconnect_display(SpiceDisplay *display)
-{
- SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-
- if (d->display == NULL)
- return;
- g_signal_handlers_disconnect_by_func(d->display, G_CALLBACK(primary_create),
- display);
- g_signal_handlers_disconnect_by_func(d->display, G_CALLBACK(primary_destroy),
- display);
- g_signal_handlers_disconnect_by_func(d->display, G_CALLBACK(invalidate),
- display);
- primary_destroy(d->display, display);
- d->display = NULL;
-}
-
-static void disconnect_cursor(SpiceDisplay *display)
-{
- SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
-
- if (d->cursor == NULL)
- return;
- g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_set),
- display);
- g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_move),
- display);
- g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_hide),
- display);
- g_signal_handlers_disconnect_by_func(d->cursor, G_CALLBACK(cursor_reset),
- display);
- d->cursor = NULL;
-}
-
static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
{
SpiceDisplay *display = data;
@@ -1893,8 +1834,8 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
g_object_get(channel, "channel-id", &id, NULL);
if (SPICE_IS_MAIN_CHANNEL(channel)) {
d->main = SPICE_MAIN_CHANNEL(channel);
- g_signal_connect(channel, "main-mouse-update",
- G_CALLBACK(update_mouse_mode), display);
+ spice_g_signal_connect_object(channel, "main-mouse-update",
+ G_CALLBACK(update_mouse_mode), display, 0);
update_mouse_mode(channel, display);
return;
}
@@ -1903,14 +1844,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
if (id != d->channel_id)
return;
d->display = channel;
- g_signal_connect(channel, "display-primary-create",
- G_CALLBACK(primary_create), display);
- g_signal_connect(channel, "display-primary-destroy",
- G_CALLBACK(primary_destroy), display);
- g_signal_connect(channel, "display-invalidate",
- G_CALLBACK(invalidate), display);
- g_signal_connect(channel, "display-mark",
- G_CALLBACK(mark), display);
+ spice_g_signal_connect_object(channel, "display-primary-create",
+ G_CALLBACK(primary_create), display, 0);
+ spice_g_signal_connect_object(channel, "display-primary-destroy",
+ G_CALLBACK(primary_destroy), display, 0);
+ spice_g_signal_connect_object(channel, "display-invalidate",
+ G_CALLBACK(invalidate), display, 0);
+ spice_g_signal_connect_object(channel, "display-mark",
+ G_CALLBACK(mark), display, 0);
spice_channel_connect(channel);
return;
}
@@ -1919,14 +1860,14 @@ static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
if (id != d->channel_id)
return;
d->cursor = SPICE_CURSOR_CHANNEL(channel);
- g_signal_connect(channel, "cursor-set",
- G_CALLBACK(cursor_set), display);
- g_signal_connect(channel, "cursor-move",
- G_CALLBACK(cursor_move), display);
- g_signal_connect(channel, "cursor-hide",
- G_CALLBACK(cursor_hide), display);
- g_signal_connect(channel, "cursor-reset",
- G_CALLBACK(cursor_reset), display);
+ spice_g_signal_connect_object(channel, "cursor-set",
+ 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);
return;
}
@@ -1959,21 +1900,22 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
SPICE_DEBUG("channel_destroy %d", id);
if (SPICE_IS_MAIN_CHANNEL(channel)) {
- disconnect_main(display);
+ d->main = NULL;
return;
}
if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
if (id != d->channel_id)
return;
- disconnect_display(display);
+ primary_destroy(d->display, display);
+ d->display = NULL;
return;
}
if (SPICE_IS_CURSOR_CHANNEL(channel)) {
if (id != d->channel_id)
return;
- disconnect_cursor(display);
+ d->cursor = NULL;
return;
}
commit 69df4d4133234e5c09ffc15d015845e586785979
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 15:53:59 2012 +0200
spice_channel_connect() success if state >= CONNECTING
We may have several widget trying to re-connect the channels now.
It is fine to return successfully if we are already connecting or
connected.
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 793e36f..54d406d 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2314,7 +2314,7 @@ gboolean spice_channel_connect(SpiceChannel *channel)
g_return_val_if_fail(SPICE_IS_CHANNEL(channel), FALSE);
SpiceChannelPrivate *c = channel->priv;
- if (c->state == SPICE_CHANNEL_STATE_CONNECTING)
+ if (c->state >= SPICE_CHANNEL_STATE_CONNECTING)
return TRUE;
return channel_connect(channel);
commit 66deab923d168509988f472c9275d27ae6f2530d
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 15:39:21 2012 +0200
widget: add monitor property with ctor
diff --git a/doc/reference/spice-gtk-sections.txt b/doc/reference/spice-gtk-sections.txt
index a339d32..daf575c 100644
--- a/doc/reference/spice-gtk-sections.txt
+++ b/doc/reference/spice-gtk-sections.txt
@@ -336,6 +336,7 @@ SpiceDisplay
SpiceDisplayClass
SpiceDisplayKeyEvent
spice_display_new
+spice_display_new_with_monitor
spice_display_mouse_ungrab
spice_display_copy_to_guest
spice_display_paste_from_guest
diff --git a/gtk/map-file b/gtk/map-file
index c58bab5..0d48bb3 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -25,6 +25,7 @@ spice_display_get_type;
spice_display_key_event_get_type;
spice_display_mouse_ungrab;
spice_display_new;
+spice_display_new_with_monitor;
spice_display_paste_from_guest;
spice_display_send_keys;
spice_display_set_grab_keys;
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index a94db68..a44e5fe 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -43,6 +43,7 @@ G_BEGIN_DECLS
struct _SpiceDisplayPrivate {
gint channel_id;
+ gint monitor_id;
/* options */
bool keyboard_grab_enable;
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 4d0f9be..7f7f17a 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -101,7 +101,8 @@ enum {
PROP_AUTO_CLIPBOARD,
PROP_SCALING,
PROP_DISABLE_INPUTS,
- PROP_ZOOM_LEVEL
+ PROP_ZOOM_LEVEL,
+ PROP_MONITOR_ID
};
/* Signals */
@@ -151,6 +152,9 @@ static void spice_display_get_property(GObject *object,
case PROP_CHANNEL_ID:
g_value_set_int(value, d->channel_id);
break;
+ case PROP_MONITOR_ID:
+ g_value_set_int(value, d->monitor_id);
+ break;
case PROP_KEYBOARD_GRAB:
g_value_set_boolean(value, d->keyboard_grab_enable);
break;
@@ -242,6 +246,9 @@ static void spice_display_set_property(GObject *object,
case PROP_CHANNEL_ID:
d->channel_id = g_value_get_int(value);
break;
+ case PROP_MONITOR_ID:
+ d->monitor_id = g_value_get_int(value);
+ break;
case PROP_KEYBOARD_GRAB:
d->keyboard_grab_enable = g_value_get_boolean(value);
update_keyboard_grab(display);
@@ -1492,6 +1499,25 @@ static void spice_display_class_init(SpiceDisplayClass *klass)
G_PARAM_STATIC_STRINGS));
/**
+ * SpiceDisplay:monitor-id:
+ *
+ * Select monitor from #SpiceDisplay to show.
+ * The value -1 means the whole display is shown.
+ * By default, the monitor 0 is selected.
+ *
+ * Since: 0.13
+ **/
+ g_object_class_install_property
+ (gobject_class, PROP_MONITOR_ID,
+ g_param_spec_int("monitor-id",
+ "Monitor ID",
+ "Select monitor ID",
+ -1, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* SpiceDisplay::mouse-grab:
* @display: the #SpiceDisplay that emitted the signal
* @status: 1 if grabbed, 0 otherwise.
@@ -1969,7 +1995,7 @@ static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer dat
/**
* spice_display_new:
* @session: a #SpiceSession
- * @id: the display channel ID to associate with #SpiceDisplay
+ * @channel_id: the display channel ID to associate with #SpiceDisplay
*
* Returns: a new #SpiceDisplay widget.
**/
@@ -1980,6 +2006,24 @@ SpiceDisplay *spice_display_new(SpiceSession *session, int id)
}
/**
+ * spice_display_new_with_monitor:
+ * @session: a #SpiceSession
+ * @channel_id: the display channel ID to associate with #SpiceDisplay
+ * @monitor_id: the monitor id within the display channel
+ *
+ * Since: 0.13
+ * Returns: a new #SpiceDisplay widget.
+ **/
+SpiceDisplay* spice_display_new_with_monitor(SpiceSession *session, gint channel_id, gint monitor_id)
+{
+ return g_object_new(SPICE_TYPE_DISPLAY,
+ "session", session,
+ "channel-id", channel_id,
+ "monitor-id", monitor_id,
+ NULL);
+}
+
+/**
* spice_display_mouse_ungrab:
* @display:
*
diff --git a/gtk/spice-widget.h b/gtk/spice-widget.h
index 3f6a785..d239ed2 100644
--- a/gtk/spice-widget.h
+++ b/gtk/spice-widget.h
@@ -70,7 +70,9 @@ typedef enum
GType spice_display_get_type(void);
-SpiceDisplay* spice_display_new(SpiceSession *session, int id);
+SpiceDisplay* spice_display_new(SpiceSession *session, int channel_id);
+SpiceDisplay* spice_display_new_with_monitor(SpiceSession *session, gint channel_id, gint monitor_id);
+
void spice_display_mouse_ungrab(SpiceDisplay *display);
void spice_display_set_grab_keys(SpiceDisplay *display, SpiceGrabSequence *seq);
SpiceGrabSequence *spice_display_get_grab_keys(SpiceDisplay *display);
commit 230125032d848878ff2ee85d2829b9b62133a8c4
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 15:36:40 2012 +0200
Claim SPICE_DISPLAY_CAP_MONITORS_CONFIG
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 5ae6e34..675ea14 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -654,6 +654,7 @@ static HDC create_compatible_dc(void)
static void spice_display_channel_reset_capabilities(SpiceChannel *channel)
{
spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_SIZED_STREAM);
+ spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_MONITORS_CONFIG);
}
static void spice_display_channel_init(SpiceDisplayChannel *channel)
commit 9381bc293cd0a7e344aa16f410034dc3099ed954
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 15:35:46 2012 +0200
Add spice_display_get_primary()
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index f82df4c..5ae6e34 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -352,6 +352,43 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
rop3_init();
}
+/**
+ * spice_display_get_primary:
+ * @channel:
+ * @surface_id:
+ * @primary:
+ *
+ * Retrieve primary display surface @surface_id.
+ *
+ * Returns: %TRUE if the primary surface was found and its details
+ * collected in @primary.
+ */
+gboolean spice_display_get_primary(SpiceChannel *channel, guint32 surface_id,
+ SpiceDisplayPrimary *primary)
+{
+ g_return_val_if_fail(SPICE_IS_DISPLAY_CHANNEL(channel), FALSE);
+ g_return_val_if_fail(primary != NULL, FALSE);
+
+ SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
+ display_surface *surface = find_surface(c, surface_id);
+
+ if (surface == NULL)
+ return FALSE;
+
+ g_return_val_if_fail(surface->primary, FALSE);
+
+ primary->format = surface->format;
+ primary->width = surface->width;
+ primary->height = surface->height;
+ primary->stride = surface->stride;
+ primary->shmid = surface->shmid;
+ primary->data = surface->data;
+ primary->marked = c->mark;
+ SPICE_DEBUG("get primary %p", primary->data);
+
+ return TRUE;
+}
+
/* signal trampoline---------------------------------------------------------- */
struct SPICE_DISPLAY_PRIMARY_CREATE {
diff --git a/gtk/channel-display.h b/gtk/channel-display.h
index a31aa54..88e60d9 100644
--- a/gtk/channel-display.h
+++ b/gtk/channel-display.h
@@ -43,6 +43,17 @@ struct _SpiceDisplayMonitorConfig {
guint height;
};
+typedef struct _SpiceDisplayPrimary SpiceDisplayPrimary;
+struct _SpiceDisplayPrimary {
+ enum SpiceSurfaceFmt format;
+ gint width;
+ gint height;
+ gint stride;
+ gint shmid;
+ guint8 *data;
+ gboolean marked;
+};
+
/**
* SpiceDisplayChannel:
*
@@ -80,10 +91,11 @@ struct _SpiceDisplayChannelClass {
gboolean mark);
/*< private >*/
- /* Do not add fields to this struct */
};
GType spice_display_channel_get_type(void);
+gboolean spice_display_get_primary(SpiceChannel *channel, guint32 surface_id,
+ SpiceDisplayPrimary *primary);
G_END_DECLS
diff --git a/gtk/map-file b/gtk/map-file
index 32ead37..c58bab5 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -17,6 +17,7 @@ spice_channel_type_to_string;
spice_client_error_quark;
spice_cursor_channel_get_type;
spice_display_channel_get_type;
+spice_display_get_primary;
spice_display_copy_to_guest;
spice_display_get_grab_keys;
spice_display_get_pixbuf;
commit d2e6f651da995ac09b6ba0c03e8b8a347a9a3e51
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 12:54:27 2012 +0200
display: add readonly monitors property
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 952628c..f82df4c 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -27,6 +27,7 @@
#include <sys/ipc.h>
#endif
+#include "glib-compat.h"
#include "spice-client.h"
#include "spice-common.h"
@@ -70,6 +71,7 @@ struct _SpiceDisplayChannelPrivate {
int nstreams;
gboolean mark;
guint mark_false_event_id;
+ GArray *monitors;
#ifdef WIN32
HDC dc;
#endif
@@ -82,6 +84,7 @@ enum {
PROP_0,
PROP_WIDTH,
PROP_HEIGHT,
+ PROP_MONITORS
};
enum {
@@ -123,6 +126,9 @@ static void spice_display_channel_dispose(GObject *object)
static void spice_display_channel_finalize(GObject *object)
{
+ SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(object)->priv;
+
+ g_clear_pointer(&c->monitors, g_array_unref);
clear_surfaces(SPICE_CHANNEL(object), FALSE);
clear_streams(SPICE_CHANNEL(object));
@@ -142,6 +148,8 @@ static void spice_display_channel_constructed(GObject *object)
g_return_if_fail(c->images != NULL);
g_return_if_fail(c->palettes != NULL);
+ c->monitors = g_array_new(FALSE, TRUE, sizeof(SpiceDisplayMonitorConfig));
+
if (G_OBJECT_CLASS(spice_display_channel_parent_class)->constructed)
G_OBJECT_CLASS(spice_display_channel_parent_class)->constructed(object);
}
@@ -165,6 +173,10 @@ static void spice_display_get_property(GObject *object,
g_value_set_uint(value, surface ? surface->height : 0);
break;
}
+ case PROP_MONITORS: {
+ g_value_set_boxed(value, c->monitors);
+ break;
+ }
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
@@ -228,6 +240,22 @@ static void spice_display_channel_class_init(SpiceDisplayChannelClass *klass)
G_PARAM_STATIC_STRINGS));
/**
+ * SpiceDisplayChannel:monitors:
+ *
+ * Current monitors configuration.
+ *
+ * Since: 0.13
+ */
+ g_object_class_install_property
+ (gobject_class, PROP_MONITORS,
+ g_param_spec_boxed("monitors",
+ "Display monitors",
+ "The monitors configuration",
+ G_TYPE_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* SpiceDisplayChannel::display-primary-create:
* @display: the #SpiceDisplayChannel that emitted the signal
* @format: %SPICE_SURFACE_FMT_32_xRGB or %SPICE_SURFACE_FMT_16_555;
diff --git a/gtk/channel-display.h b/gtk/channel-display.h
index b8d7679..a31aa54 100644
--- a/gtk/channel-display.h
+++ b/gtk/channel-display.h
@@ -33,6 +33,16 @@ typedef struct _SpiceDisplayChannel SpiceDisplayChannel;
typedef struct _SpiceDisplayChannelClass SpiceDisplayChannelClass;
typedef struct _SpiceDisplayChannelPrivate SpiceDisplayChannelPrivate;
+typedef struct _SpiceDisplayMonitorConfig SpiceDisplayMonitorConfig;
+struct _SpiceDisplayMonitorConfig {
+ guint id;
+ guint surface_id;
+ guint x;
+ guint y;
+ guint width;
+ guint height;
+};
+
/**
* SpiceDisplayChannel:
*
commit d809b2ce54fb61081bc6d3d1f67068093f5532ec
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 12:51:02 2012 +0200
glib-compat: add g_clear_pointer
A helpful macro from glib 2.34
diff --git a/gtk/glib-compat.h b/gtk/glib-compat.h
index bd8f3de..aa750b2 100644
--- a/gtk/glib-compat.h
+++ b/gtk/glib-compat.h
@@ -106,4 +106,24 @@ GType spice_main_context_get_type (void) G_GNUC_CONST;
# define G_SIGNAL_DEPRECATED (1 << 9)
#endif
+#ifndef g_clear_pointer
+#define g_clear_pointer(pp, destroy) \
+ G_STMT_START { \
+ G_STATIC_ASSERT (sizeof *(pp) == sizeof (gpointer)); \
+ /* Only one access, please */ \
+ gpointer *_pp = (gpointer *) (pp); \
+ gpointer _p; \
+ /* This assignment is needed to avoid a gcc warning */ \
+ GDestroyNotify _destroy = (GDestroyNotify) (destroy); \
+ \
+ (void) (0 ? (gpointer) *(pp) : 0); \
+ do \
+ _p = g_atomic_pointer_get (_pp); \
+ while G_UNLIKELY (!g_atomic_pointer_compare_and_exchange (_pp, _p, NULL)); \
+ \
+ if (_p) \
+ _destroy (_p); \
+ } G_STMT_END
+#endif
+
#endif /* GLIB_COMPAT_H */
commit 6bea13927e6c1833e2c12b604c51bb265cede305
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Sat Jun 16 12:48:40 2012 +0200
Change surface_id to a guint32
That's the correct type used by the protocol.
diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h
index d327c28..ff0f484 100644
--- a/gtk/channel-display-priv.h
+++ b/gtk/channel-display-priv.h
@@ -38,7 +38,7 @@ G_BEGIN_DECLS
typedef struct display_surface {
RingItem link;
- int surface_id;
+ guint32 surface_id;
bool primary;
enum SpiceSurfaceFmt format;
int width, height, stride, size;
diff --git a/gtk/channel-display.c b/gtk/channel-display.c
index 801d867..952628c 100644
--- a/gtk/channel-display.c
+++ b/gtk/channel-display.c
@@ -100,7 +100,7 @@ static void spice_display_channel_up(SpiceChannel *channel);
static void clear_surfaces(SpiceChannel *channel, gboolean keep_primary);
static void clear_streams(SpiceChannel *channel);
-static display_surface *find_surface(SpiceDisplayChannelPrivate *c, int surface_id);
+static display_surface *find_surface(SpiceDisplayChannelPrivate *c, guint32 surface_id);
static gboolean display_stream_render(display_stream *st);
static void spice_display_channel_reset(SpiceChannel *channel, gboolean migrating);
static void spice_display_channel_reset_capabilities(SpiceChannel *channel);
@@ -710,7 +710,7 @@ static void destroy_canvas(display_surface *surface)
surface->canvas = NULL;
}
-static display_surface *find_surface(SpiceDisplayChannelPrivate *c, int surface_id)
+static display_surface *find_surface(SpiceDisplayChannelPrivate *c, guint32 surface_id)
{
display_surface *surface;
RingItem *item;
commit ac9e55b64ff00803530557d2cad15287076a51b1
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 14:13:01 2012 +0200
widget: add main channel before other channels
Make sure that the d->main channel member can be referenced when
adding further channels, when we perform their setup.
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 237d832..4d0f9be 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -441,7 +441,14 @@ spice_display_constructor(GType gtype,
G_CALLBACK(channel_destroy), display);
list = spice_session_get_channels(d->session);
for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
- channel_new(d->session, it->data, (gpointer*)display);
+ if (SPICE_IS_MAIN_CHANNEL(it->data)) {
+ channel_new(d->session, it->data, (gpointer*)display);
+ break;
+ }
+ }
+ for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
+ if (!SPICE_IS_MAIN_CHANNEL(it->data))
+ channel_new(d->session, it->data, (gpointer*)display);
}
g_list_free(list);
commit 53127e30561ed4d66c772a57428b352a3b92127b
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Fri Jun 15 16:34:54 2012 +0200
Add missing agent cap description
diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 70861e6..1d83bfa 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -149,6 +149,7 @@ static const char *agent_caps[] = {
[ VD_AGENT_CAP_CLIPBOARD ] = "clipboard (old)",
[ VD_AGENT_CAP_DISPLAY_CONFIG ] = "display config",
[ VD_AGENT_CAP_CLIPBOARD_BY_DEMAND ] = "clipboard",
+ [ VD_AGENT_CAP_CLIPBOARD_SELECTION ] = "clipboard selection",
};
#define NAME(_a, _i) ((_i) < SPICE_N_ELEMENTS(_a) ? (_a[(_i)] ?: "?") : "?")
commit e3bb7b1cfd162fcb8943e9d582dab43eeec6ce41
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Tue Jun 12 19:24:47 2012 +0200
display: learn to restrict display to an area
Each spice widget can now restrict the area of the primary
surface they show and interact with by setting the private
area member.
A nice clean-up would be to seperate an area object that
would deal with clipping, input translation and color
conversion, but the resulting code would be similar anyway
diff --git a/gtk/spice-widget-cairo.c b/gtk/spice-widget-cairo.c
index 2f1ef75..1144308 100644
--- a/gtk/spice-widget-cairo.c
+++ b/gtk/spice-widget-cairo.c
@@ -37,16 +37,22 @@ int spicex_image_create(SpiceDisplay *display)
{
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ g_return_val_if_fail(d->ximage == NULL, 1);
+
if (d->format == SPICE_SURFACE_FMT_16_555 ||
d->format == SPICE_SURFACE_FMT_16_565) {
d->convert = TRUE;
- d->data = g_malloc0(d->height * d->stride); /* pixels are 32 bits */
+ d->data = g_malloc0(d->area.width * d->area.height * 4);
+
+ d->ximage = cairo_image_surface_create_for_data
+ (d->data, CAIRO_FORMAT_RGB24, d->area.width, d->area.height, d->area.width * 4);
+
} else {
d->convert = FALSE;
- }
- d->ximage = cairo_image_surface_create_for_data
- (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+ d->ximage = cairo_image_surface_create_for_data
+ (d->data, CAIRO_FORMAT_RGB24, d->width, d->height, d->stride);
+ }
return 0;
}
@@ -64,13 +70,13 @@ void spicex_image_destroy(SpiceDisplay *display)
g_free(d->data);
d->data = NULL;
}
+ d->convert = FALSE;
}
G_GNUC_INTERNAL
void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
{
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
- int fbw = d->width, fbh = d->height;
int ww, wh;
gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(display)), &ww, &wh);
@@ -86,8 +92,8 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
behaviour of drawing the rectangle from right to left
to cut out the whole */
if (d->ximage)
- cairo_rectangle(cr, d->mx + fbw, d->my,
- -1 * fbw, fbh);
+ cairo_rectangle(cr, d->mx + d->area.width, d->my,
+ -d->area.width, d->area.height);
cairo_fill(cr);
}
@@ -97,9 +103,16 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
double sx, sy;
spice_display_get_scaling(display, &sx, &sy);
cairo_scale(cr, sx, sy);
+ if (!d->convert)
+ cairo_translate(cr, -d->area.x, -d->area.y);
cairo_set_source_surface(cr, d->ximage, 0, 0);
} else {
- cairo_set_source_surface(cr, d->ximage, d->mx, d->my);
+ cairo_translate(cr, d->mx, d->my);
+ cairo_rectangle(cr, 0, 0, d->area.width, d->area.height);
+ cairo_clip(cr);
+ if (!d->convert)
+ cairo_translate(cr, -d->area.x, -d->area.y);
+ cairo_set_source_surface(cr, d->ximage, 0, 0);
}
cairo_paint(cr);
@@ -108,8 +121,8 @@ void spicex_draw_event(SpiceDisplay *display, cairo_t *cr)
GdkPixbuf *image = d->mouse_pixbuf;
if (image != NULL) {
gdk_cairo_set_source_pixbuf(cr, image,
- d->mx + d->mouse_guest_x - d->mouse_hotspot.x,
- d->my + d->mouse_guest_y - d->mouse_hotspot.y);
+ d->mouse_guest_x - d->mouse_hotspot.x,
+ d->mouse_guest_y - d->mouse_hotspot.y);
cairo_paint(cr);
}
}
@@ -149,8 +162,8 @@ void spicex_image_invalidate(SpiceDisplay *display,
double sx, sy;
/* Scale the exposed region */
- sx = (double)ww / (double)d->width;
- sy = (double)wh / (double)d->height;
+ sx = (double)ww / (double)d->area.width;
+ sy = (double)wh / (double)d->area.height;
*x *= sx;
*y *= sy;
diff --git a/gtk/spice-widget-priv.h b/gtk/spice-widget-priv.h
index 898d86c..a94db68 100644
--- a/gtk/spice-widget-priv.h
+++ b/gtk/spice-widget-priv.h
@@ -57,6 +57,8 @@ struct _SpiceDisplayPrivate {
gpointer data_origin; /* the original display image data */
gpointer data; /* converted if necessary to 32 bits */
+ GdkRectangle area;
+ /* window border */
gint ww, wh, mx, my;
bool convert;
diff --git a/gtk/spice-widget-x11.c b/gtk/spice-widget-x11.c
index be2b6e7..7fc99ff 100644
--- a/gtk/spice-widget-x11.c
+++ b/gtk/spice-widget-x11.c
@@ -222,30 +222,30 @@ void spicex_expose_event(SpiceDisplay *display, GdkEventExpose *expose)
if (expose->area.x >= d->mx &&
expose->area.y >= d->my &&
- expose->area.x + expose->area.width <= d->mx + d->width &&
- expose->area.y + expose->area.height <= d->my + d->height) {
+ expose->area.x + expose->area.width <= d->mx + d->area.width &&
+ expose->area.y + expose->area.height <= d->my + d->area.height) {
/* area is completely inside the guest screen -- blit it */
if (d->have_mitshm && d->shminfo) {
XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
d->gc, d->ximage,
- expose->area.x - d->mx, expose->area.y - d->my,
- expose->area.x, expose->area.y,
+ d->area.x + expose->area.x - d->mx, d->area.y + expose->area.y - d->my,
+ expose->area.x, expose->area.y,
expose->area.width, expose->area.height,
true);
} else {
XPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
d->gc, d->ximage,
- expose->area.x - d->mx, expose->area.y - d->my,
- expose->area.x, expose->area.y,
+ d->area.x + expose->area.x - d->mx, d->area.y + expose->area.y - d->my,
+ expose->area.x expose->area.y,
expose->area.width, expose->area.height);
}
} else {
/* complete window update */
- if (d->ww > d->width || d->wh > d->height) {
+ if (d->ww > d->area.width || d->wh > d->area.height) {
int x1 = d->mx;
- int x2 = d->mx + d->width;
+ int x2 = d->mx + d->area.width;
int y1 = d->my;
- int y2 = d->my + d->height;
+ int y2 = d->my + d->area.height;
XFillRectangle(d->dpy, gdk_x11_drawable_get_xid(window),
d->gc, 0, 0, x1, d->wh);
XFillRectangle(d->dpy, gdk_x11_drawable_get_xid(window),
@@ -258,12 +258,12 @@ void spicex_expose_event(SpiceDisplay *display, GdkEventExpose *expose)
if (d->have_mitshm && d->shminfo) {
XShmPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
d->gc, d->ximage,
- 0, 0, d->mx, d->my, d->width, d->height,
+ d->area.x, d->area.y, d->mx, d->my, d->area.width, d->area.height,
true);
} else {
XPutImage(d->dpy, gdk_x11_drawable_get_xid(window),
d->gc, d->ximage,
- 0, 0, d->mx, d->my, d->width, d->height);
+ d->area.x, d->area.y, d->mx, d->my, d->area.width, d->area.height);
}
}
}
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index 095224c..237d832 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -201,8 +201,8 @@ static void update_size_request(SpiceDisplay *display)
reqwidth = 640;
reqheight = 480;
} else {
- reqwidth = d->width;
- reqheight = d->height;
+ reqwidth = d->area.width;
+ reqheight = d->area.height;
}
gtk_widget_set_size_request(GTK_WIDGET(display), reqwidth, reqheight);
@@ -789,15 +789,16 @@ static void recalc_geometry(GtkWidget *widget)
d->mx = 0;
d->my = 0;
if (!spicex_is_scaled(display)) {
- if (d->ww > d->width)
- d->mx = (d->ww - d->width) / 2;
- if (d->wh > d->height)
- d->my = (d->wh - d->height) / 2;
+ if (d->ww > d->area.width)
+ d->mx = (d->ww - d->area.width) / 2;
+ if (d->wh > d->area.height)
+ d->my = (d->wh - d->area.height) / 2;
} else
zoom = (gdouble)d->zoom_level / 100;
- SPICE_DEBUG("monitors: id %d, guest %dx%d, window %dx%d, zoom %g, offset +%d+%d",
- d->channel_id, d->width, d->height, d->ww, d->wh, zoom, d->mx, d->my);
+ SPICE_DEBUG("monitors: id %d, guest +%d+%d:%dx%d, window %dx%d, zoom %g, offset +%d+%d",
+ d->channel_id, d->area.x, d->area.y, d->area.width, d->area.height,
+ d->ww, d->wh, zoom, d->mx, d->my);
if (d->resize_guest_enable)
spice_main_set_display(d->main, d->channel_id,
@@ -820,44 +821,36 @@ static void recalc_geometry(GtkWidget *widget)
#define CONVERT_0555_TO_8888(s) (CONVERT_0555_TO_0888(s) | 0xff000000)
-static gboolean do_color_convert(SpiceDisplay *display,
- gint x, gint y, gint w, gint h)
+static gboolean do_color_convert(SpiceDisplay *display, GdkRectangle *r)
{
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
- int i, j, maxy, maxx, miny, minx;
guint32 *dest = d->data;
guint16 *src = d->data_origin;
+ gint x, y;
- if (!d->convert)
- return true;
-
+ g_return_val_if_fail(r != NULL, false);
g_return_val_if_fail(d->format == SPICE_SURFACE_FMT_16_555 ||
d->format == SPICE_SURFACE_FMT_16_565, false);
- miny = MAX(y, 0);
- minx = MAX(x, 0);
- maxy = MIN(y + h, d->height);
- maxx = MIN(x + w, d->width);
-
- dest += (d->stride / 4) * miny;
- src += (d->stride / 2) * miny;
+ src += (d->stride / 2) * r->y + r->x;
+ dest += d->area.width * (r->y - d->area.y) + (r->x - d->area.x);
if (d->format == SPICE_SURFACE_FMT_16_555) {
- for (j = miny; j < maxy; j++) {
- for (i = minx; i < maxx; i++) {
- dest[i] = CONVERT_0555_TO_0888(src[i]);
+ for (y = 0; y < r->height; y++) {
+ for (x = 0; x < r->width; x++) {
+ dest[x] = CONVERT_0555_TO_0888(src[x]);
}
- dest += d->stride / 4;
+ dest += d->area.width;
src += d->stride / 2;
}
} else if (d->format == SPICE_SURFACE_FMT_16_565) {
- for (j = miny; j < maxy; j++) {
- for (i = minx; i < maxx; i++) {
- dest[i] = CONVERT_0565_TO_0888(src[i]);
+ for (y = 0; y < r->height; y++) {
+ for (x = 0; x < r->width; x++) {
+ dest[x] = CONVERT_0565_TO_0888(src[x]);
}
- dest += d->stride / 4;
+ dest += d->area.width;
src += d->stride / 2;
}
}
@@ -875,10 +868,7 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
if (d->mark == 0 || d->data == NULL)
return false;
-
- if (!d->ximage) {
- spicex_image_create(display);
- }
+ g_return_val_if_fail(d->ximage != NULL, false);
spicex_draw_event(display, cr);
update_mouse_pointer(display);
@@ -894,10 +884,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *expose)
if (d->mark == 0 || d->data == NULL)
return false;
-
- if (!d->ximage) {
- spicex_image_create(display);
- }
+ g_return_val_if_fail(d->ximage != NULL, false);
spicex_expose_event(display, expose);
update_mouse_pointer(display);
@@ -1181,10 +1168,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
return true;
gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh);
- if (spicex_is_scaled(display) && (d->width != ww || d->height != wh)) {
+ if (spicex_is_scaled(display) && (d->area.width != ww || d->area.height != wh)) {
double sx, sy;
- sx = (double)d->width / (double)ww;
- sy = (double)d->height / (double)wh;
+ sx = (double)d->area.width / (double)ww;
+ sy = (double)d->area.height / (double)wh;
/* Scaling the desktop, so scale the mouse coords by the same.
* Ratio for the rounding used:
@@ -1208,10 +1195,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
switch (d->mouse_mode) {
case SPICE_MOUSE_MODE_CLIENT:
- if (motion->x >= 0 && motion->x < d->width &&
- motion->y >= 0 && motion->y < d->height) {
+ if (motion->x >= 0 && motion->x < d->area.width &&
+ motion->y >= 0 && motion->y < d->area.height) {
spice_inputs_position(d->inputs,
- motion->x, motion->y,
+ motion->x + d->area.x, motion->y + d->area.y,
d->channel_id,
button_mask_gdk_to_spice(motion->state));
}
@@ -1285,7 +1272,7 @@ static gboolean button_event(GtkWidget *widget, GdkEventButton *button)
gtk_widget_get_pointer (widget, &x, &y);
x -= d->mx;
y -= d->my;
- if (!(x >= 0 && x < d->width && y >= 0 && y < d->height))
+ if (!(x >= 0 && x < d->area.width && y >= 0 && y < d->area.height))
return true;
}
@@ -1577,6 +1564,40 @@ static void update_mouse_mode(SpiceChannel *channel, gpointer data)
cursor_invalidate(display);
}
+static void update_area(SpiceDisplay *display,
+ gint x, gint y, gint width, gint height)
+{
+ SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ GdkRectangle primary = {
+ .x = 0,
+ .y = 0,
+ .width = d->width,
+ .height = d->height
+ };
+ GdkRectangle area = {
+ .x = x,
+ .y = y,
+ .width = width,
+ .height = height
+ };
+
+ SPICE_DEBUG("update area, primary: %dx%d, area: +%d+%d %dx%d", d->width, d->height, area.x, area.y, area.width, area.height);
+
+ if (!gdk_rectangle_intersect(&primary, &area, &area)) {
+ SPICE_DEBUG("The monitor area is not intersecting primary surface");
+ memset(&d->area, '\0', sizeof(d->area));
+ /* FIXME mark false? */
+ return;
+ }
+
+ spicex_image_destroy(display);
+ d->area = area;
+ spicex_image_create(display);
+ update_size_request(display);
+
+ gtk_widget_queue_draw(GTK_WIDGET(display));
+}
+
static void primary_create(SpiceChannel *channel, gint format,
gint width, gint height, gint stride,
gint shmid, gpointer imgdata, gpointer data)
@@ -1586,14 +1607,13 @@ static void primary_create(SpiceChannel *channel, gint format,
d->format = format;
d->stride = stride;
- d->shmid = shmid;
+ d->shmid = shmid;
+ d->width = width;
+ d->height = height;
d->data_origin = d->data = imgdata;
- if (d->width != width || d->height != height) {
- d->width = width;
- d->height = height;
- update_size_request(display);
- }
+ /* by default, display whole surface */
+ update_area(display, 0, 0, width, height);
}
static void primary_destroy(SpiceChannel *channel, gpointer data)
@@ -1602,26 +1622,40 @@ static void primary_destroy(SpiceChannel *channel, gpointer data)
SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
spicex_image_destroy(display);
- d->format = 0;
d->width = 0;
d->height = 0;
d->stride = 0;
d->shmid = 0;
- d->data = 0;
- d->data_origin = 0;
+ d->data = NULL;
+ d->data_origin = NULL;
}
static void invalidate(SpiceChannel *channel,
gint x, gint y, gint w, gint h, gpointer data)
{
SpiceDisplay *display = data;
+ SpiceDisplayPrivate *d = SPICE_DISPLAY_GET_PRIVATE(display);
+ GdkRectangle rect = {
+ .x = x,
+ .y = y,
+ .width = w,
+ .height = h
+ };
if (!gtk_widget_get_window(GTK_WIDGET(display)))
return;
- if (!do_color_convert(display, x, y, w, h))
+ if (!gdk_rectangle_intersect(&rect, &d->area, &rect))
return;
+ if (d->convert)
+ do_color_convert(display, &rect);
+
+ x = rect.x - d->area.x;
+ y = rect.y - d->area.y;
+ w = rect.width;
+ h = rect.height;
+
spicex_image_invalidate(display, &x, &y, &w, &h);
gtk_widget_queue_draw_area(GTK_WIDGET(display),
x, y, w, h);
@@ -1706,7 +1740,7 @@ G_GNUC_INTERNAL
void spice_display_get_scaling(SpiceDisplay *display, double *sx, double *sy)
{
SpiceDisplayPrivate *d = display->priv;
- int fbw = d->width, fbh = d->height;
+ int fbw = d->area.width, fbh = d->area.height;
int ww, wh;
if (!spicex_is_scaled(display) || !gtk_widget_get_window(GTK_WIDGET(display))) {
@@ -1732,8 +1766,8 @@ static void cursor_invalidate(SpiceDisplay *display)
spice_display_get_scaling(display, &sx, &sy);
gtk_widget_queue_draw_area(GTK_WIDGET(display),
- (d->mouse_guest_x + d->mx - d->mouse_hotspot.x) * sx,
- (d->mouse_guest_y + d->my - d->mouse_hotspot.y) * sy,
+ (d->mouse_guest_x + d->mx - d->mouse_hotspot.x - d->area.x) * sx,
+ (d->mouse_guest_y + d->my - d->mouse_hotspot.y - d->area.y) * sy,
gdk_pixbuf_get_width(d->mouse_pixbuf) * sx,
gdk_pixbuf_get_height(d->mouse_pixbuf) * sy);
}
@@ -2000,16 +2034,14 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
g_return_val_if_fail(d != NULL, NULL);
/* TODO: ensure d->data has been exposed? */
+ g_return_val_if_fail(d->data != NULL, NULL);
- data = g_malloc(d->width * d->height * 3);
+ data = g_malloc(d->area.width * d->area.height * 3);
src = d->data;
dest = data;
- if (src == NULL || dest == NULL)
- return NULL;
-
- for (y = 0; y < d->height; ++y) {
- for (x = 0; x < d->width; ++x) {
+ for (y = d->area.y; y < d->area.height; ++y) {
+ for (x = d->area.x; x < d->area.width; ++x) {
dest[0] = src[x * 4 + 2];
dest[1] = src[x * 4 + 1];
dest[2] = src[x * 4 + 0];
@@ -2019,7 +2051,7 @@ GdkPixbuf *spice_display_get_pixbuf(SpiceDisplay *display)
}
pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, false,
- 8, d->width, d->height, d->width * 3,
+ 8, d->area.width, d->area.height, d->area.width * 3,
(GdkPixbufDestroyNotify)g_free, NULL);
return pixbuf;
}
commit 59b7c8a0053f4facb43b16313906353237a5e3db
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Wed Jun 20 15:14:38 2012 +0200
widget: simplify redraw call
diff --git a/gtk/spice-widget.c b/gtk/spice-widget.c
index e892591..095224c 100644
--- a/gtk/spice-widget.c
+++ b/gtk/spice-widget.c
@@ -1637,8 +1637,7 @@ static void mark(SpiceChannel *channel, gint mark, gpointer data)
d->mark = mark;
spice_main_set_display_enabled(d->main, d->channel_id, d->mark != 0);
if (mark != 0 && gtk_widget_get_window(GTK_WIDGET(display)))
- gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(display)),
- NULL, FALSE);
+ gtk_widget_queue_draw(GTK_WIDGET(display));
}
static void cursor_set(SpiceCursorChannel *channel,
commit 175f80a876b29541c2341b48d1c57796e4186b9b
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date: Mon Jul 16 17:46:37 2012 +0200
Update spice-common
diff --git a/spice-common b/spice-common
index 5f44094..735e431 160000
--- a/spice-common
+++ b/spice-common
@@ -1 +1 @@
-Subproject commit 5f4409494066b5f59df58d6207fdbb0441aa9e90
+Subproject commit 735e43167195267e70559101a1b35196b59b84ca
More information about the Spice-commits
mailing list