vinagre r325 - in trunk: . src



Author: jwendell
Date: Sat May 10 02:41:21 2008
New Revision: 325
URL: http://svn.gnome.org/viewvc/vinagre?rev=325&view=rev

Log:
2008-05-09  Jonh Wendell <jwendell gnome org>

	* lots of files: Closes #519145 â Implement unique instance.


Added:
   trunk/src/bacon-message-connection.c
   trunk/src/bacon-message-connection.h
   trunk/src/vinagre-app.c
   trunk/src/vinagre-app.h
   trunk/src/vinagre-bacon.c
   trunk/src/vinagre-bacon.h
Modified:
   trunk/AUTHORS
   trunk/ChangeLog
   trunk/src/Makefile.am
   trunk/src/vinagre-commands.c
   trunk/src/vinagre-commands.h
   trunk/src/vinagre-main.c
   trunk/src/vinagre-tab.c
   trunk/src/vinagre-tab.h
   trunk/src/vinagre-ui.h
   trunk/src/vinagre-utils.c
   trunk/src/vinagre-utils.h
   trunk/src/vinagre-window-private.h
   trunk/src/vinagre-window.c
   trunk/src/vinagre-window.h

Modified: trunk/AUTHORS
==============================================================================
--- trunk/AUTHORS	(original)
+++ trunk/AUTHORS	Sat May 10 02:41:21 2008
@@ -1,5 +1,7 @@
 Jonh Wendell <wendell bani com br>
 
+Some parts of vinagre were based on gedit, so, thanks to gedit crowd.
+
 And many thanks to the gtk-vnc development team:
 - Anthony Liguori
 - Daniel P. Berrange

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Sat May 10 02:41:21 2008
@@ -29,6 +29,9 @@
 	gossip-cell-renderer-expander.c gossip-cell-renderer-expander.h \
 	vinagre-mdns.h vinagre-mdns.c \
 	vinagre-prefs.h vinagre-prefs.c \
+	bacon-message-connection.h bacon-message-connection.c \
+	vinagre-app.h vinagre-app.c \
+	vinagre-bacon.h vinagre-bacon.c \
 	$(NULL)
 
 vinagre_LDADD = \

Added: trunk/src/bacon-message-connection.c
==============================================================================
--- (empty file)
+++ trunk/src/bacon-message-connection.c	Sat May 10 02:41:21 2008
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2003 Bastien Nocera <hadess hadess net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#include "bacon-message-connection.h"
+
+#ifndef UNIX_PATH_MAX
+#define UNIX_PATH_MAX 108
+#endif
+
+struct BaconMessageConnection {
+	/* A server accepts connections */
+	gboolean is_server;
+
+	/* The socket path itself */
+	char *path;
+
+	/* File descriptor of the socket */
+	int fd;
+	/* Channel to watch */
+	GIOChannel *chan;
+	/* Event id returned by g_io_add_watch() */
+	int conn_id;
+
+	/* Connections accepted by this connection */
+	GSList *accepted_connections;
+
+	/* callback */
+	void (*func) (const char *message, gpointer user_data);
+	gpointer data;
+};
+
+static gboolean
+test_is_socket (const char *path)
+{
+	struct stat s;
+
+	if (stat (path, &s) == -1)
+		return FALSE;
+
+	if (S_ISSOCK (s.st_mode))
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean
+is_owned_by_user_and_socket (const char *path)
+{
+	struct stat s;
+
+	if (stat (path, &s) == -1)
+		return FALSE;
+
+	if (s.st_uid != geteuid ())
+		return FALSE;
+
+	if ((s.st_mode & S_IFSOCK) != S_IFSOCK)
+		return FALSE;
+	
+	return TRUE;
+}
+
+static gboolean server_cb (GIOChannel *source,
+			   GIOCondition condition, gpointer data);
+
+static gboolean
+setup_connection (BaconMessageConnection *conn)
+{
+	g_return_val_if_fail (conn->chan == NULL, FALSE);
+
+	conn->chan = g_io_channel_unix_new (conn->fd);
+	if (!conn->chan) {
+		return FALSE;
+	}
+	g_io_channel_set_line_term (conn->chan, "\n", 1);
+	conn->conn_id = g_io_add_watch (conn->chan, G_IO_IN, server_cb, conn);
+
+	return TRUE;
+}
+
+static void
+accept_new_connection (BaconMessageConnection *server_conn)
+{
+	BaconMessageConnection *conn;
+	int alen;
+
+	g_return_if_fail (server_conn->is_server);
+
+	conn = g_new0 (BaconMessageConnection, 1);
+	conn->is_server = FALSE;
+	conn->func = server_conn->func;
+	conn->data = server_conn->data;
+
+	conn->fd = accept (server_conn->fd, NULL, (guint *)&alen);
+
+	server_conn->accepted_connections =
+		g_slist_prepend (server_conn->accepted_connections, conn);
+
+	setup_connection (conn);
+}
+
+static gboolean
+server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+	BaconMessageConnection *conn = (BaconMessageConnection *)data;
+	char *message, *subs, buf;
+	int cd, rc, offset;
+	gboolean finished;
+
+	offset = 0;
+	if (conn->is_server && conn->fd == g_io_channel_unix_get_fd (source)) {
+		accept_new_connection (conn);
+		return TRUE;
+	}
+	message = g_malloc (1);
+	cd = conn->fd;
+	rc = read (cd, &buf, 1);
+	while (rc > 0 && buf != '\n')
+	{
+		message = g_realloc (message, rc + offset + 1);
+		message[offset] = buf;
+		offset = offset + rc;
+		rc = read (cd, &buf, 1);
+	}
+	if (rc <= 0) {
+		g_io_channel_shutdown (conn->chan, FALSE, NULL);
+		g_io_channel_unref (conn->chan);
+		conn->chan = NULL;
+		close (conn->fd);
+		conn->fd = -1;
+		g_free (message);
+		conn->conn_id = 0;
+
+		return FALSE;
+	}
+	message[offset] = '\0';
+
+	subs = message;
+	finished = FALSE;
+
+	while (finished == FALSE && *subs != '\0')
+	{
+		if (conn->func != NULL)
+			(*conn->func) (subs, conn->data);
+
+		subs += strlen (subs) + 1;
+		if (subs - message >= offset)
+			finished = TRUE;
+	}
+
+	g_free (message);
+
+	return TRUE;
+}
+
+static char *
+find_file_with_pattern (const char *dir, const char *pattern)
+{
+	GDir *filedir;
+	char *found_filename;
+	const char *filename;
+	GPatternSpec *pat;
+
+	filedir = g_dir_open (dir, 0, NULL);
+	if (filedir == NULL)
+		return NULL;
+
+	pat = g_pattern_spec_new (pattern);
+	if (pat == NULL)
+	{
+		g_dir_close (filedir);
+		return NULL;
+	}
+
+	found_filename = NULL;
+
+	while ((filename = g_dir_read_name (filedir)))
+	{
+		if (g_pattern_match_string (pat, filename))
+		{
+			char *tmp = g_build_filename (dir, filename, NULL);
+			if (is_owned_by_user_and_socket (tmp))
+				found_filename = g_strdup (filename);
+			g_free (tmp);
+		}
+
+		if (found_filename != NULL)
+			break;
+	}
+
+	g_pattern_spec_free (pat);
+	g_dir_close (filedir);
+
+	return found_filename;
+}
+
+static char *
+socket_filename (const char *prefix)
+{
+	char *pattern, *newfile, *path, *filename;
+	const char *tmpdir;
+
+	pattern = g_strdup_printf ("%s.%s.*", prefix, g_get_user_name ());
+	tmpdir = g_get_tmp_dir ();
+	filename = find_file_with_pattern (tmpdir, pattern);
+	if (filename == NULL)
+	{
+		newfile = g_strdup_printf ("%s.%s.%u", prefix,
+				g_get_user_name (), g_random_int ());
+		path = g_build_filename (tmpdir, newfile, NULL);
+		g_free (newfile);
+	} else {
+		path = g_build_filename (tmpdir, filename, NULL);
+		g_free (filename);
+	}
+
+	g_free (pattern);
+	return path;
+}
+
+static gboolean
+try_server (BaconMessageConnection *conn)
+{
+	struct sockaddr_un uaddr;
+
+	uaddr.sun_family = AF_UNIX;
+	strncpy (uaddr.sun_path, conn->path,
+			MIN (strlen(conn->path)+1, UNIX_PATH_MAX));
+	conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
+	if (bind (conn->fd, (struct sockaddr *) &uaddr, sizeof (uaddr)) == -1)
+	{
+		conn->fd = -1;
+		return FALSE;
+	}
+	listen (conn->fd, 5);
+
+	if (!setup_connection (conn))
+		return FALSE;
+	return TRUE;
+}
+
+static gboolean
+try_client (BaconMessageConnection *conn)
+{
+	struct sockaddr_un uaddr;
+
+	uaddr.sun_family = AF_UNIX;
+	strncpy (uaddr.sun_path, conn->path,
+			MIN(strlen(conn->path)+1, UNIX_PATH_MAX));
+	conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
+	if (connect (conn->fd, (struct sockaddr *) &uaddr,
+				sizeof (uaddr)) == -1)
+	{
+		conn->fd = -1;
+		return FALSE;
+	}
+
+	return setup_connection (conn);
+}
+
+BaconMessageConnection *
+bacon_message_connection_new (const char *prefix)
+{
+	BaconMessageConnection *conn;
+
+	g_return_val_if_fail (prefix != NULL, NULL);
+
+	conn = g_new0 (BaconMessageConnection, 1);
+	conn->path = socket_filename (prefix);
+
+	if (test_is_socket (conn->path) == FALSE)
+	{
+		if (!try_server (conn))
+		{
+			bacon_message_connection_free (conn);
+			return NULL;
+		}
+
+		conn->is_server = TRUE;
+		return conn;
+	}
+
+	if (try_client (conn) == FALSE)
+	{
+		unlink (conn->path);
+		try_server (conn);
+		if (conn->fd == -1)
+		{
+			bacon_message_connection_free (conn);
+			return NULL;
+		}
+
+		conn->is_server = TRUE;
+		return conn;
+	}
+
+	conn->is_server = FALSE;
+	return conn;
+}
+
+void
+bacon_message_connection_free (BaconMessageConnection *conn)
+{
+	GSList *child_conn;
+
+	g_return_if_fail (conn != NULL);
+	/* Only servers can accept other connections */
+	g_return_if_fail (conn->is_server != FALSE ||
+			  conn->accepted_connections == NULL);
+
+	child_conn = conn->accepted_connections;
+	while (child_conn != NULL) {
+		bacon_message_connection_free (child_conn->data);
+		child_conn = g_slist_next (child_conn);
+	}
+	g_slist_free (conn->accepted_connections);
+
+	if (conn->conn_id) {
+		g_source_remove (conn->conn_id);
+		conn->conn_id = 0;
+	}
+	if (conn->chan) {
+		g_io_channel_shutdown (conn->chan, FALSE, NULL);
+		g_io_channel_unref (conn->chan);
+	}
+
+	if (conn->is_server != FALSE) {
+		unlink (conn->path);
+	}
+	if (conn->fd != -1) {
+		close (conn->fd);
+	}
+
+	g_free (conn->path);
+	g_free (conn);
+}
+
+void
+bacon_message_connection_set_callback (BaconMessageConnection *conn,
+				       BaconMessageReceivedFunc func,
+				       gpointer user_data)
+{
+	g_return_if_fail (conn != NULL);
+
+	conn->func = func;
+	conn->data = user_data;
+}
+
+void
+bacon_message_connection_send (BaconMessageConnection *conn,
+			       const char *message)
+{
+	g_return_if_fail (conn != NULL);
+	g_return_if_fail (message != NULL);
+
+	g_io_channel_write_chars (conn->chan, message, strlen (message),
+				  NULL, NULL);
+	g_io_channel_write_chars (conn->chan, "\n", 1, NULL, NULL);
+	g_io_channel_flush (conn->chan, NULL);
+}
+
+gboolean
+bacon_message_connection_get_is_server (BaconMessageConnection *conn)
+{
+	g_return_val_if_fail (conn != NULL, FALSE);
+
+	return conn->is_server;
+}
+

Added: trunk/src/bacon-message-connection.h
==============================================================================
--- (empty file)
+++ trunk/src/bacon-message-connection.h	Sat May 10 02:41:21 2008
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003 Bastien Nocera <hadess hadess net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef BACON_MESSAGE_CONNECTION_H
+#define BACON_MESSAGE_CONNECTION_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef void (*BaconMessageReceivedFunc) (const char *message,
+					  gpointer user_data);
+
+typedef struct BaconMessageConnection BaconMessageConnection;
+
+BaconMessageConnection *bacon_message_connection_new	(const char *prefix);
+void bacon_message_connection_free			(BaconMessageConnection *conn);
+void bacon_message_connection_set_callback		(BaconMessageConnection *conn,
+							 BaconMessageReceivedFunc func,
+							 gpointer user_data);
+void bacon_message_connection_send			(BaconMessageConnection *conn,
+							 const char *message);
+gboolean bacon_message_connection_get_is_server		(BaconMessageConnection *conn);
+
+G_END_DECLS
+
+#endif /* BACON_MESSAGE_CONNECTION_H */

Added: trunk/src/vinagre-app.c
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-app.c	Sat May 10 02:41:21 2008
@@ -0,0 +1,274 @@
+/*
+ * vinagre-app.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008 - Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "vinagre-app.h"
+#include "vinagre-utils.h"
+
+#define VINAGRE_APP_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), VINAGRE_TYPE_APP, VinagreAppPrivate))
+
+
+struct _VinagreAppPrivate
+{
+  GList *windows;
+  VinagreWindow *active_window;
+};
+
+G_DEFINE_TYPE(VinagreApp, vinagre_app, G_TYPE_OBJECT)
+
+static void
+vinagre_app_finalize (GObject *object)
+{
+  VinagreApp *app = VINAGRE_APP (object); 
+
+  g_list_free (app->priv->windows);
+
+  G_OBJECT_CLASS (vinagre_app_parent_class)->finalize (object);
+}
+
+static void 
+vinagre_app_class_init (VinagreAppClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = vinagre_app_finalize;
+
+  g_type_class_add_private (object_class, sizeof(VinagreAppPrivate));
+}
+
+static void
+vinagre_app_init (VinagreApp *app)
+{
+  app->priv = VINAGRE_APP_GET_PRIVATE (app);
+  app->priv->windows = NULL;
+  app->priv->active_window = NULL;
+}
+
+static void
+app_weak_notify (gpointer data,
+		 GObject *where_the_app_was)
+{
+  gtk_main_quit ();
+}
+
+VinagreApp *
+vinagre_app_get_default (void)
+{
+  static VinagreApp *app = NULL;
+
+  if (G_LIKELY (app))
+    return app;
+
+  app = VINAGRE_APP (g_object_new (VINAGRE_TYPE_APP, NULL));	
+
+  g_object_add_weak_pointer (G_OBJECT (app),
+			     (gpointer) &app);
+  g_object_weak_ref (G_OBJECT (app),
+		     app_weak_notify,
+		     NULL);
+
+  return app;
+}
+
+static gboolean
+window_focus_in_event (VinagreWindow *window, 
+		       GdkEventFocus *event, 
+		       VinagreApp    *app)
+{
+  /* updates active_view and active_child when a new toplevel receives focus */
+  g_return_val_if_fail (VINAGRE_IS_WINDOW (window), FALSE);
+
+  app->priv->active_window = window;
+
+  return FALSE;
+}
+
+static void
+window_destroy (VinagreWindow *window, 
+		VinagreApp    *app)
+{
+  app->priv->windows = g_list_remove (app->priv->windows,
+				      window);
+
+  if (window == app->priv->active_window)
+    app->priv->active_window = app->priv->windows != NULL ?
+			       app->priv->windows->data : NULL;
+
+  if (app->priv->windows == NULL)
+    g_object_unref (app);
+}
+
+VinagreWindow *
+vinagre_app_create_window (VinagreApp *app, GdkScreen *screen)
+{
+  VinagreWindow *window;
+
+  /*
+   * We need to be careful here, there is a race condition:
+   * when another vinagre is launched it checks active_window,
+   * so we must do our best to ensure that active_window
+   * is never NULL when at least a window exists.
+   */
+  if (app->priv->windows == NULL)
+    app->priv->active_window = window = vinagre_window_new ();
+  else
+    window = vinagre_window_new ();
+
+  app->priv->windows = g_list_prepend (app->priv->windows,
+				       window);
+
+  g_signal_connect (window, 
+		    "focus_in_event",
+		    G_CALLBACK (window_focus_in_event), 
+		    app);
+  g_signal_connect (window, 
+		    "destroy",
+		    G_CALLBACK (window_destroy),
+		    app);
+
+  if (screen != NULL)
+    gtk_window_set_screen (GTK_WINDOW (window), screen);
+
+  return window;
+}
+
+const GList *
+vinagre_app_get_windows (VinagreApp *app)
+{
+  g_return_val_if_fail (VINAGRE_IS_APP (app), NULL);
+
+  return app->priv->windows;
+}
+
+VinagreWindow *
+vinagre_app_get_active_window (VinagreApp *app)
+{
+  g_return_val_if_fail (VINAGRE_IS_APP (app), NULL);
+
+  /* make sure our active window is always realized:
+   * this is needed on startup if we launch two vinagre fast
+   * enough that the second instance comes up before the
+   * first one shows its window.
+   */
+
+  if (!GTK_WIDGET_REALIZED (GTK_WIDGET (app->priv->active_window)))
+    gtk_widget_realize (GTK_WIDGET (app->priv->active_window));
+
+  return app->priv->active_window;
+}
+
+static gboolean
+is_in_viewport (VinagreWindow *window,
+		GdkScreen     *screen,
+		gint          workspace,
+		gint          viewport_x,
+		gint          viewport_y)
+{
+  GdkScreen *s;
+  GdkDisplay *display;
+  const gchar *cur_name;
+  const gchar *name;
+  gint cur_n;
+  gint n;
+  gint ws;
+  gint sc_width, sc_height;
+  gint x, y, width, height;
+  gint vp_x, vp_y;
+
+  /* Check for screen and display match */
+  display = gdk_screen_get_display (screen);
+  cur_name = gdk_display_get_name (display);
+  cur_n = gdk_screen_get_number (screen);
+
+  s = gtk_window_get_screen (GTK_WINDOW (window));
+  display = gdk_screen_get_display (s);
+  name = gdk_display_get_name (display);
+  n = gdk_screen_get_number (s);
+
+  if (strcmp (cur_name, name) != 0 || cur_n != n)
+    return FALSE;
+
+  /* Check for workspace match */
+  ws = vinagre_utils_get_window_workspace (GTK_WINDOW (window));
+  if (ws != workspace && ws != VINAGRE_ALL_WORKSPACES)
+    return FALSE;
+
+  /* Check for viewport match */
+  gdk_window_get_position (GTK_WIDGET (window)->window, &x, &y);
+  gdk_drawable_get_size (GTK_WIDGET (window)->window, &width, &height);
+  vinagre_utils_get_current_viewport (screen, &vp_x, &vp_y);
+  x += vp_x;
+  y += vp_y;
+
+  sc_width = gdk_screen_get_width (screen);
+  sc_height = gdk_screen_get_height (screen);
+
+  return x + width * .25 >= viewport_x &&
+	 x + width * .75 < viewport_x + sc_width &&
+	 y  >= viewport_y &&
+	 y + height < viewport_y + sc_height;
+}
+
+VinagreWindow *
+vinagre_app_get_window_in_viewport (VinagreApp *app,
+				    GdkScreen  *screen,
+				    gint        workspace,
+				    gint        viewport_x,
+				    gint        viewport_y)
+{
+  VinagreWindow *window;
+  GList *l;
+
+  g_return_val_if_fail (VINAGRE_IS_APP (app), NULL);
+
+  /* first try if the active window */
+  window = app->priv->active_window;
+
+  g_return_val_if_fail (VINAGRE_IS_WINDOW (window), NULL);
+
+  if (is_in_viewport (window, screen, workspace, viewport_x, viewport_y))
+    return window;
+
+  /* otherwise try to see if there is a window on this workspace */
+  for (l = app->priv->windows; l != NULL; l = l->next)
+    {
+      window = l->data;
+
+      if (is_in_viewport (window, screen, workspace, viewport_x, viewport_y))
+	return window;
+    }
+
+  /* no window on this workspace... create a new one */
+  return vinagre_app_create_window (app, screen);
+}
+
+GList *
+vinagre_app_get_connections (VinagreApp *app)
+{
+  return NULL;
+}
+
+/* vim: ts=8 */

Added: trunk/src/vinagre-app.h
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-app.h	Sat May 10 02:41:21 2008
@@ -0,0 +1,74 @@
+/*
+ * vinagre-app.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008 - Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_APP_H__
+#define __VINAGRE_APP_H__
+
+#include <gtk/gtk.h>
+
+#include "vinagre-window.h"
+
+G_BEGIN_DECLS
+
+#define VINAGRE_TYPE_APP              (vinagre_app_get_type())
+#define VINAGRE_APP(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), VINAGRE_TYPE_APP, VinagreApp))
+#define VINAGRE_APP_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), VINAGRE_TYPE_APP, VinagreAppClass))
+#define VINAGRE_IS_APP(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), VINAGRE_TYPE_APP))
+#define VINAGRE_IS_APP_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), VINAGRE_TYPE_APP))
+#define VINAGRE_APP_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), VINAGRE_TYPE_APP, VinagreAppClass))
+
+typedef struct _VinagreAppPrivate VinagreAppPrivate;
+typedef struct _VinagreApp VinagreApp;
+typedef struct _VinagreAppClass VinagreAppClass;
+
+struct _VinagreApp 
+{
+  GObject object;
+  VinagreAppPrivate *priv;
+};
+
+
+struct _VinagreAppClass 
+{
+  GObjectClass parent_class;
+};
+
+
+GType		vinagre_app_get_type		(void) G_GNUC_CONST;
+
+VinagreApp 	*vinagre_app_get_default	(void);
+
+VinagreWindow	*vinagre_app_create_window	(VinagreApp  *app,
+						 GdkScreen *screen);
+
+const GList	*vinagre_app_get_windows	(VinagreApp *app);
+VinagreWindow	*vinagre_app_get_active_window	(VinagreApp *app);
+GList		*vinagre_app_get_connections	(VinagreApp *app);
+
+VinagreWindow	*vinagre_app_get_window_in_viewport (VinagreApp *app,
+						     GdkScreen  *screen,
+						     gint        workspace,
+						     gint        viewport_x,
+						     gint        viewport_y);
+
+G_END_DECLS
+
+#endif  /* __VINAGRE_APP_H__  */
+/* vim: ts=8 */

Added: trunk/src/vinagre-bacon.c
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bacon.c	Sat May 10 02:41:21 2008
@@ -0,0 +1,289 @@
+/*
+ * vinagre-bacon.c
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008 - Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include "bacon-message-connection.h"
+#include "vinagre-utils.h"
+#include "vinagre-connection.h"
+#include "vinagre-app.h"
+#include "vinagre-window.h"
+#include "vinagre-commands.h"
+
+static guint32 startup_timestamp = 0;
+
+static GdkDisplay *
+display_open_if_needed (const gchar *name)
+{
+  GSList *displays;
+  GSList *l;
+  GdkDisplay *display = NULL;
+
+  displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
+
+  for (l = displays; l != NULL; l = l->next)
+    {
+      if (strcmp (gdk_display_get_name ((GdkDisplay *) l->data), name) == 0)
+	{
+	  display = l->data;
+	  break;
+	}
+    }
+
+  g_slist_free (displays);
+
+  return display != NULL ? display : gdk_display_open (name);
+}
+
+/* serverside */
+void
+vinagre_bacon_message_received (const char *message,
+				gpointer    data)
+{
+  gchar **commands;
+  gchar **params;
+  gint workspace;
+  gint viewport_x;
+  gint viewport_y;
+  gchar *display_name;
+  gint screen_number;
+  gint i;
+  VinagreApp *app;
+  VinagreWindow *window;
+  GdkDisplay *display;
+  GdkScreen *screen;
+  gboolean new_window = FALSE;
+  GSList *servers = NULL;
+  GSList *l, *next;
+  VinagreConnection *conn;
+  gchar *error;
+
+  g_return_if_fail (message != NULL);
+
+  commands = g_strsplit (message, "\v", -1);
+
+  /* header */
+  params = g_strsplit (commands[0], "\t", 6);
+  startup_timestamp = atoi (params[0]);
+  display_name = params[1];
+  screen_number = atoi (params[2]);
+  workspace = atoi (params[3]);
+  viewport_x = atoi (params[4]);
+  viewport_y = atoi (params[5]);
+
+  display = display_open_if_needed (display_name);
+  if (display == NULL)
+    {
+      g_warning ("Could not open display %s\n", display_name);
+      g_strfreev (params);
+      goto out;
+    }
+
+  screen = gdk_display_get_screen (display, screen_number);
+  g_strfreev (params);
+
+  /* body */
+  for (i = 1; commands[i] != NULL; i++)
+    {
+      params = g_strsplit (commands[i], "\t", -1);
+
+      if (strcmp (params[0], "NEW-WINDOW") == 0)
+	  new_window = TRUE;
+
+      else if (strcmp (params[0], "OPEN-URIS") == 0)
+	{
+	  gint n_uris, j;
+	  gchar **uris;
+
+	  n_uris = atoi (params[1]);
+	  uris = g_strsplit (params[2], " ", n_uris);
+
+	  for (j = 0; j < n_uris; j++)
+	    {
+	      conn = vinagre_connection_new_from_string (uris[j], &error);
+	      if (conn)
+		servers = g_slist_prepend (servers, conn);
+	    }
+
+	  g_strfreev (uris);
+	}
+
+	else
+	  {
+	    g_warning ("Unexpected bacon command");
+	  }
+
+      g_strfreev (params);
+    }
+
+  /* execute the commands */
+  app = vinagre_app_get_default ();
+  if (new_window)
+    window = vinagre_app_create_window (app, screen);
+  else
+    /* get a window in the current workspace (if exists) and raise it */
+    window = vinagre_app_get_window_in_viewport (app,
+						 screen,
+						 workspace,
+						 viewport_x,
+						 viewport_y);
+
+  for (l = servers; l; l = next)
+    {
+      VinagreConnection *conn = l->data;
+      
+      next = l->next;
+      vinagre_cmd_direct_connect (conn, window);
+    }
+
+  /* set the proper interaction time on the window.
+   * Fall back to roundtripping to the X server when we
+   * don't have the timestamp, e.g. when launched from
+   * terminal. We also need to make sure that the window
+   * has been realized otherwise it will not work. lame.
+   */
+  if (!GTK_WIDGET_REALIZED (window))
+    gtk_widget_realize (GTK_WIDGET (window));
+
+  if (startup_timestamp <= 0)
+    startup_timestamp = gdk_x11_get_server_time (GTK_WIDGET (window)->window);
+
+  gdk_x11_window_set_user_time (GTK_WIDGET (window)->window,
+				startup_timestamp);
+  gtk_window_present (GTK_WINDOW (window));
+
+  out:
+  g_strfreev (commands);
+}
+
+/* clientside */
+static void
+vinagre_bacon_send_message (BaconMessageConnection *connection,
+			    GSList *servers,
+			    gboolean new_window)
+{
+  GdkScreen *screen;
+  GdkDisplay *display;
+  const gchar *display_name;
+  gint screen_number;
+  gint ws;
+  gint viewport_x;
+  gint viewport_y;
+  GString *command;
+
+  /* the messages have the following format:
+   * <---                                  header                                     ---> <----            body             ----->
+   * timestamp \t display_name \t screen_number \t workspace \t viewport_x \t viewport_y \v OP1 \t arg \t arg \v OP2 \t arg \t arg|...
+   *
+   * when the arg is a list of uri, they are separated by a space.
+   * So the delimiters are \v for the commands, \t for the tokens in
+   * a command and ' ' for the uris: note that such delimiters cannot
+   * be part of an uri, this way parsing is easier.
+   */
+
+  screen = gdk_screen_get_default ();
+  display = gdk_screen_get_display (screen);
+
+  display_name = gdk_display_get_name (display);
+  screen_number = gdk_screen_get_number (screen);
+
+  ws = vinagre_utils_get_current_workspace (screen);
+  vinagre_utils_get_current_viewport (screen, &viewport_x, &viewport_y);
+
+  command = g_string_new (NULL);
+
+  /* header */
+  g_string_append_printf (command,
+			   "%" G_GUINT32_FORMAT "\t%s\t%d\t%d\t%d\t%d",
+			   startup_timestamp,
+			   display_name,
+			   screen_number,
+			   ws,
+			   viewport_x,
+			   viewport_y);
+
+  /* NEW-WINDOW command */
+  if (new_window)
+    {
+      command = g_string_append_c (command, '\v');
+      command = g_string_append (command, "NEW-WINDOW");
+    }
+
+  /* OPEN_URIS command */
+  if (servers)
+    {
+      GSList *l;
+
+      command = g_string_append_c (command, '\v');
+      command = g_string_append (command, "OPEN-URIS");
+
+      g_string_append_printf (command, "\t%d\t", g_slist_length (servers));
+
+      for (l = servers; l != NULL; l = l->next)
+	{
+	  VinagreConnection *conn = l->data;
+	  g_string_append_printf (command, "%s:%d",
+					   vinagre_connection_get_host (conn),
+					   vinagre_connection_get_port (conn));
+	  if (l->next != NULL)
+	    command = g_string_append_c (command, ' ');
+	}
+    }
+
+  bacon_message_connection_send (connection,
+				 command->str);
+
+  g_string_free (command, TRUE);
+}
+
+void
+vinagre_bacon_start (GSList *servers, gboolean new_window)
+{
+  BaconMessageConnection *connection;
+
+  connection = bacon_message_connection_new ("vinagre");
+  if (!connection)
+    {
+      g_warning ("Cannot create the 'vinagre' connection.");
+      return;
+    }
+
+  if (!bacon_message_connection_get_is_server (connection)) 
+    {
+      vinagre_bacon_send_message (connection, servers, new_window);
+
+      gdk_notify_startup_complete ();
+
+      g_slist_foreach (servers, (GFunc) g_object_unref, NULL);
+      g_slist_free (servers);
+      bacon_message_connection_free (connection);
+
+      exit (0);
+    }
+  else 
+    {
+      bacon_message_connection_set_callback (connection,
+					     vinagre_bacon_message_received,
+					     NULL);
+    }
+}
+/* vim: ts=8 */

Added: trunk/src/vinagre-bacon.h
==============================================================================
--- (empty file)
+++ trunk/src/vinagre-bacon.h	Sat May 10 02:41:21 2008
@@ -0,0 +1,27 @@
+/*
+ * vinagre-bacon.h
+ * This file is part of vinagre
+ *
+ * Copyright (C) 2008 - Jonh Wendell <wendell bani com br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __VINAGRE_BACON_H__
+#define __VINAGRE_BACON_H__
+
+void	vinagre_bacon_start (GSList *servers, gboolean new_window);
+
+#endif  /* __VINAGRE_BACON_H__  */
+/* vim: ts=8 */

Modified: trunk/src/vinagre-commands.c
==============================================================================
--- trunk/src/vinagre-commands.c	(original)
+++ trunk/src/vinagre-commands.c	Sat May 10 02:41:21 2008
@@ -42,14 +42,22 @@
 vinagre_cmd_direct_connect (VinagreConnection *conn,
 			    VinagreWindow     *window)
 {
-  GtkWidget *tab;
+  VinagreTab *tab;
 
   g_return_if_fail (VINAGRE_IS_WINDOW (window));
 
-  tab = vinagre_tab_new (conn, window);
-  vinagre_notebook_add_tab (VINAGRE_NOTEBOOK (window->priv->notebook),
-			    VINAGRE_TAB (tab),
-			    -1);
+  tab = vinagre_window_conn_exists (window, conn);
+  if (tab)
+    {
+      vinagre_window_set_active_tab (window, tab);
+    }
+  else
+    {
+      tab = VINAGRE_TAB (vinagre_tab_new (conn, window));
+      vinagre_notebook_add_tab (VINAGRE_NOTEBOOK (window->priv->notebook),
+				VINAGRE_TAB (tab),
+				-1);
+    }
 }
 
 /* Machine Menu */
@@ -57,7 +65,7 @@
 vinagre_cmd_machine_connect (GtkAction     *action,
 			     VinagreWindow *window)
 {
-  GtkWidget *tab;
+  VinagreTab *tab;
   VinagreConnection *conn;
 
   g_return_if_fail (VINAGRE_IS_WINDOW (window));
@@ -66,10 +74,18 @@
   if (!conn)
     return;
 
-  tab = vinagre_tab_new (conn, window);
-  vinagre_notebook_add_tab (VINAGRE_NOTEBOOK (window->priv->notebook),
-			    VINAGRE_TAB (tab),
-			    -1);
+  tab = vinagre_window_conn_exists (window, conn);
+  if (tab)
+    {
+      vinagre_window_set_active_tab (window, tab);
+    }
+  else
+    {
+      tab = VINAGRE_TAB (vinagre_tab_new (conn, window));
+      vinagre_notebook_add_tab (VINAGRE_NOTEBOOK (window->priv->notebook),
+				VINAGRE_TAB (tab),
+				-1);
+    }
 }
 
 void
@@ -168,6 +184,13 @@
   vinagre_window_close_all_tabs (window);
 }
 
+void
+vinagre_cmd_machine_quit (GtkAction     *action,
+			  VinagreWindow *window)
+{
+  gtk_widget_destroy (GTK_WIDGET (window));
+}
+
 /* View Menu */
 void
 vinagre_cmd_view_show_toolbar	(GtkAction     *action,
@@ -264,17 +287,24 @@
 vinagre_cmd_open_bookmark (VinagreWindow     *window,
 			   VinagreConnection *conn)
 {
-  GtkWidget *tab;
+  VinagreTab *tab;
   VinagreConnection *new_conn;
 
   g_return_if_fail (VINAGRE_IS_WINDOW (window));
 
-  new_conn = vinagre_connection_clone (conn);
-  tab = vinagre_tab_new (new_conn, window);
-  vinagre_notebook_add_tab (VINAGRE_NOTEBOOK (window->priv->notebook),
-			    VINAGRE_TAB (tab),
-			    -1);
-  gtk_widget_show (tab);
+  tab = vinagre_window_conn_exists (window, conn);
+  if (tab)
+    {
+      vinagre_window_set_active_tab (window, tab);
+    }
+  else
+    {
+      new_conn = vinagre_connection_clone (conn);
+      tab = VINAGRE_TAB (vinagre_tab_new (new_conn, window));
+      vinagre_notebook_add_tab (VINAGRE_NOTEBOOK (window->priv->notebook),
+				VINAGRE_TAB (tab),
+				-1);
+    }
 }
 
 void

Modified: trunk/src/vinagre-commands.h
==============================================================================
--- trunk/src/vinagre-commands.h	(original)
+++ trunk/src/vinagre-commands.h	Sat May 10 02:41:21 2008
@@ -43,6 +43,8 @@
 
 void		vinagre_cmd_machine_close_all	(GtkAction     *action,
 						 VinagreWindow *window);
+void		vinagre_cmd_machine_quit	(GtkAction     *action,
+						 VinagreWindow *window);
 
 void		vinagre_cmd_view_show_toolbar	(GtkAction     *action,
 						 VinagreWindow *window);

Modified: trunk/src/vinagre-main.c
==============================================================================
--- trunk/src/vinagre-main.c	(original)
+++ trunk/src/vinagre-main.c	Sat May 10 02:41:21 2008
@@ -28,21 +28,27 @@
 #include "vinagre-commands.h"
 #include "vinagre-bookmarks.h"
 #include "vinagre-window.h"
+#include "vinagre-app.h"
 #include "vinagre-utils.h"
 #include "vinagre-prefs.h"
 #include "vinagre-mdns.h"
+#include "vinagre-bacon.h"
 #include <vncdisplay.h>
 
 /* command line */
 static gchar **files = NULL;
 static gchar **remaining_args = NULL;
 static GSList *servers = NULL;
+static gboolean new_window = FALSE;
 
 static const GOptionEntry options [] =
 {
   { "file", 'f', 0, G_OPTION_ARG_FILENAME_ARRAY, &files,
     N_("Opens a .vnc file"), N_("filename")},
 
+  { "new-window", 'n', 0, G_OPTION_ARG_NONE, &new_window,
+    N_("Create a new toplevel window in an existing instance of vinagre"), NULL },
+
   { 
     G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args,
     NULL, N_("[server:port]") },
@@ -104,7 +110,7 @@
 						"The following errors have occurred:",
 						g_slist_length (errors)),
 				      errors,
-				      GTK_WINDOW (window));
+				      window?GTK_WINDOW (window):NULL);
       g_slist_free (errors);
     }
 }
@@ -113,7 +119,8 @@
   GOptionContext    *context;
   GError            *error = NULL;
   GSList            *l, *next;
-  VinagreWindow     *main_window;
+  VinagreWindow     *window;
+  VinagreApp        *app;
 
   bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
@@ -135,22 +142,25 @@
     }
 
   g_set_application_name (_("Remote Desktop Viewer"));
+  vinagre_main_process_command_line (NULL);
+
+  vinagre_bacon_start (servers, new_window);
 
   if (!g_thread_supported ())
     g_thread_init (NULL);
 
-  main_window = vinagre_window_new ();
-  gtk_widget_show (GTK_WIDGET(main_window));
+  app = vinagre_app_get_default ();
+  window = vinagre_app_create_window (app, NULL);
+  gtk_widget_show (GTK_WIDGET(window));
 
   vinagre_utils_handle_debug ();
 
-  vinagre_main_process_command_line (main_window);
   for (l = servers; l; l = next)
     {
       VinagreConnection *conn = l->data;
       
       next = l->next;
-      vinagre_cmd_direct_connect (conn, main_window);
+      vinagre_cmd_direct_connect (conn, window);
     }
   g_slist_free (servers);
 

Modified: trunk/src/vinagre-tab.c
==============================================================================
--- trunk/src/vinagre-tab.c	(original)
+++ trunk/src/vinagre-tab.c	Sat May 10 02:41:21 2008
@@ -152,6 +152,7 @@
     {
       case PROP_CONN:
         tab->priv->conn = g_value_get_object (value);
+	g_object_set_data (G_OBJECT (tab->priv->conn), VINAGRE_TAB_KEY, tab);
         break;
       case PROP_WINDOW:
         tab->priv->window = g_value_get_object (value);
@@ -917,4 +918,16 @@
   return tab->priv->state;
 }
 
+VinagreTab *
+vinagre_tab_get_from_connection (VinagreConnection *conn)
+{
+  gpointer res;
+	
+  g_return_val_if_fail (VINAGRE_IS_CONNECTION (conn), NULL);
+	
+  res = g_object_get_data (G_OBJECT (conn), VINAGRE_TAB_KEY);
+	
+  return (res != NULL) ? VINAGRE_TAB (res) : NULL;
+}
+
 /* vim: ts=8 */

Modified: trunk/src/vinagre-tab.h
==============================================================================
--- trunk/src/vinagre-tab.h	(original)
+++ trunk/src/vinagre-tab.h	Sat May 10 02:41:21 2008
@@ -32,6 +32,7 @@
 #define VINAGRE_IS_TAB(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), VINAGRE_TYPE_TAB))
 #define VINAGRE_IS_TAB_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), VINAGRE_TYPE_TAB))
 #define VINAGRE_TAB_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), VINAGRE_TYPE_TAB, VinagreTabClass))
+#define VINAGRE_TAB_KEY               "VINAGRE_TAB_KEY"
 
 typedef struct _VinagreTabPrivate VinagreTabPrivate;
 typedef struct _VinagreTab        VinagreTab;
@@ -94,6 +95,8 @@
 gboolean	  vinagre_tab_get_readonly	(VinagreTab *tab);
 
 VinagreTabState   vinagre_tab_get_state		(VinagreTab *tab);
+VinagreTab	  *vinagre_tab_get_from_connection (VinagreConnection *conn);
+
 
 G_END_DECLS
 

Modified: trunk/src/vinagre-ui.h
==============================================================================
--- trunk/src/vinagre-ui.h	(original)
+++ trunk/src/vinagre-ui.h	Sat May 10 02:41:21 2008
@@ -42,7 +42,7 @@
   { "MachineOpen", GTK_STOCK_OPEN, NULL, "<control>O",
     N_("Open a .VNC file"), G_CALLBACK (vinagre_cmd_machine_open) },
   { "MachineQuit", GTK_STOCK_QUIT, NULL, "<control>Q",
-    N_("Quit the program"), G_CALLBACK (gtk_main_quit) },
+    N_("Quit the program"), G_CALLBACK (vinagre_cmd_machine_quit) },
   	
   /* Bookmarks menu */
   { "BookmarksOpen", GTK_STOCK_CONNECT, N_("_Open bookmark"), NULL,

Modified: trunk/src/vinagre-utils.c
==============================================================================
--- trunk/src/vinagre-utils.c	(original)
+++ trunk/src/vinagre-utils.c	Sat May 10 02:41:21 2008
@@ -28,6 +28,14 @@
 #include <config.h>
 #endif
 
+/* For the workspace/viewport stuff */
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif
+
 GtkWidget *
 vinagre_utils_create_small_close_button ()
 {
@@ -181,4 +189,164 @@
 
   initialized = TRUE;
 }
+
+/* the following two functions are courtesy of galeon */
+
+/**
+ * gedit_utils_get_current_workspace: Get the current workspace
+ *
+ * Get the currently visible workspace for the #GdkScreen.
+ *
+ * If the X11 window property isn't found, 0 (the first workspace)
+ * is returned.
+ */
+guint
+vinagre_utils_get_current_workspace (GdkScreen *screen)
+{
+#ifdef GDK_WINDOWING_X11
+	GdkWindow *root_win;
+	GdkDisplay *display;
+	Atom type;
+	gint format;
+	gulong nitems;
+	gulong bytes_after;
+	guint *current_desktop;
+	gint err, result;
+	guint ret = 0;
+
+	g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
+
+	root_win = gdk_screen_get_root_window (screen);
+	display = gdk_screen_get_display (screen);
+
+	gdk_error_trap_push ();
+	result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
+				     gdk_x11_get_xatom_by_name_for_display (display, "_NET_CURRENT_DESKTOP"),
+				     0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
+				     &bytes_after, (gpointer) &current_desktop);
+	err = gdk_error_trap_pop ();
+
+	if (err != Success || result != Success)
+		return ret;
+
+	if (type == XA_CARDINAL && format == 32 && nitems > 0)
+		ret = current_desktop[0];
+
+	XFree (current_desktop);
+	return ret;
+#else
+	/* FIXME: on mac etc proably there are native APIs
+	 * to get the current workspace etc */
+	return 0;
+#endif
+}
+
+/**
+ * gedit_utils_get_window_workspace: Get the workspace the window is on
+ *
+ * This function gets the workspace that the #GtkWindow is visible on,
+ * it returns GEDIT_ALL_WORKSPACES if the window is sticky, or if
+ * the window manager doesn support this function
+ */
+guint
+vinagre_utils_get_window_workspace (GtkWindow *gtkwindow)
+{
+#ifdef GDK_WINDOWING_X11
+	GdkWindow *window;
+	GdkDisplay *display;
+	Atom type;
+	gint format;
+	gulong nitems;
+	gulong bytes_after;
+	guint *workspace;
+	gint err, result;
+	guint ret = VINAGRE_ALL_WORKSPACES;
+
+	g_return_val_if_fail (GTK_IS_WINDOW (gtkwindow), 0);
+	g_return_val_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (gtkwindow)), 0);
+
+	window = GTK_WIDGET (gtkwindow)->window;
+	display = gdk_drawable_get_display (window);
+
+	gdk_error_trap_push ();
+	result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window),
+				     gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"),
+				     0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
+				     &bytes_after, (gpointer) &workspace);
+	err = gdk_error_trap_pop ();
+
+	if (err != Success || result != Success)
+		return ret;
+
+	if (type == XA_CARDINAL && format == 32 && nitems > 0)
+		ret = workspace[0];
+
+	XFree (workspace);
+	return ret;
+#else
+	/* FIXME: on mac etc proably there are native APIs
+	 * to get the current workspace etc */
+	return 0;
+#endif
+}
+
+/**
+ * gedit_utils_get_current_viewport: Get the current viewport origin
+ *
+ * Get the currently visible viewport origin for the #GdkScreen.
+ *
+ * If the X11 window property isn't found, (0, 0) is returned.
+ */
+void
+vinagre_utils_get_current_viewport (GdkScreen    *screen,
+				  gint         *x,
+				  gint         *y)
+{
+#ifdef GDK_WINDOWING_X11
+	GdkWindow *root_win;
+	GdkDisplay *display;
+	Atom type;
+	gint format;
+	gulong nitems;
+	gulong bytes_after;
+	gulong *coordinates;
+	gint err, result;
+
+	g_return_if_fail (GDK_IS_SCREEN (screen));
+	g_return_if_fail (x != NULL && y != NULL);
+
+	/* Default values for the viewport origin */
+	*x = 0;
+	*y = 0;
+
+	root_win = gdk_screen_get_root_window (screen);
+	display = gdk_screen_get_display (screen);
+
+	gdk_error_trap_push ();
+	result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (root_win),
+				     gdk_x11_get_xatom_by_name_for_display (display, "_NET_DESKTOP_VIEWPORT"),
+				     0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems,
+				     &bytes_after, (void*) &coordinates);
+	err = gdk_error_trap_pop ();
+
+	if (err != Success || result != Success)
+		return;
+
+	if (type != XA_CARDINAL || format != 32 || nitems < 2)
+	{
+		XFree (coordinates);
+		return;
+	}
+
+	*x = coordinates[0];
+	*y = coordinates[1];
+	XFree (coordinates);
+#else
+	/* FIXME: on mac etc proably there are native APIs
+	 * to get the current workspace etc */
+	*x = 0;
+	*y = 0;
+#endif
+}
+
 /* vim: ts=8 */

Modified: trunk/src/vinagre-utils.h
==============================================================================
--- trunk/src/vinagre-utils.h	(original)
+++ trunk/src/vinagre-utils.h	Sat May 10 02:41:21 2008
@@ -2,7 +2,7 @@
  * vinagre-utils.h
  * This file is part of vinagre
  *
- * Copyright (C) 2007 - Jonh Wendell <wendell bani com br>
+ * Copyright (C) 2007,2008 - Jonh Wendell <wendell bani com br>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,6 +24,13 @@
 #include <gtk/gtk.h>
 #include <glib.h>
 
+/* useful macro */
+#define GBOOLEAN_TO_POINTER(i) ((gpointer) ((i) ? 2 : 1))
+#define GPOINTER_TO_BOOLEAN(i) ((gboolean) ((((gint)(i)) == 2) ? TRUE : FALSE))
+#define IS_VALID_BOOLEAN(v) (((v == TRUE) || (v == FALSE)) ? TRUE : FALSE)
+
+enum { VINAGRE_ALL_WORKSPACES = 0xffffffff };
+
 GtkWidget	*vinagre_utils_create_small_close_button (void);
 
 void		vinagre_utils_show_error		(const gchar *message,
@@ -42,5 +49,12 @@
 							 gssize      length);
 
 void		vinagre_utils_handle_debug		(void);
+
+guint		vinagre_utils_get_current_workspace	(GdkScreen *screen);
+guint		vinagre_utils_get_window_workspace	(GtkWindow *gtkwindow);
+void		vinagre_utils_get_current_viewport	(GdkScreen    *screen,
+							 gint         *x,
+							 gint         *y);
+
 #endif  /* __VINAGRE_UTILS_H__  */
 /* vim: ts=8 */

Modified: trunk/src/vinagre-window-private.h
==============================================================================
--- trunk/src/vinagre-window-private.h	(original)
+++ trunk/src/vinagre-window-private.h	Sat May 10 02:41:21 2008
@@ -48,6 +48,7 @@
   GtkAction      *recent_action;
   guint           bookmarks_list_menu_ui_id;
   guint           recents_menu_ui_id;
+  guint           update_recents_menu_ui_id;
 
   GtkWidget       *toolbar;
   GtkWidget       *menubar;

Modified: trunk/src/vinagre-window.c
==============================================================================
--- trunk/src/vinagre-window.c	(original)
+++ trunk/src/vinagre-window.c	Sat May 10 02:41:21 2008
@@ -47,7 +47,7 @@
 G_DEFINE_TYPE(VinagreWindow, vinagre_window, GTK_TYPE_WINDOW)
 
 static void
-vinagre_window_finalize (GObject *object)
+vinagre_window_dispose (GObject *object)
 {
   VinagreWindow *window = VINAGRE_WINDOW (object);
 
@@ -63,6 +63,37 @@
       window->priv->manager = NULL;
     }
 
+  if (window->priv->signal_notebook != 0)
+    {
+      g_signal_handler_disconnect (window->priv->notebook,
+				   window->priv->signal_notebook);
+      window->priv->signal_notebook = 0;
+    }
+
+  if (window->priv->signal_clipboard != 0)
+    {
+      GtkClipboard  *cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);;
+
+      g_signal_handler_disconnect (cb,
+				   window->priv->signal_clipboard);
+      window->priv->signal_clipboard = 0;
+    }
+
+  if (window->priv->update_recents_menu_ui_id != 0)
+    {
+      GtkRecentManager *recent_manager = gtk_recent_manager_get_default ();
+
+      g_signal_handler_disconnect (recent_manager,
+				   window->priv->update_recents_menu_ui_id);
+      window->priv->update_recents_menu_ui_id = 0;
+    }
+
+  G_OBJECT_CLASS (vinagre_window_parent_class)->dispose (object);
+}
+
+static void
+vinagre_window_finalize (GObject *object)
+{
   G_OBJECT_CLASS (vinagre_window_parent_class)->finalize (object);
 }
 
@@ -71,20 +102,8 @@
 			     GdkEventAny *event)
 {
   VinagreWindow *window = VINAGRE_WINDOW (widget);
-  GtkClipboard  *cb;
-
-  cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
-
-  if (window->priv->signal_notebook > 0)
-    g_signal_handler_disconnect (window->priv->notebook,
-				 window->priv->signal_notebook);
-
-  if (window->priv->signal_clipboard > 0)
-    g_signal_handler_disconnect (cb,
-				 window->priv->signal_clipboard);
 
   vinagre_window_close_all_tabs (window);
-  gtk_main_quit ();
 
   if (GTK_WIDGET_CLASS (vinagre_window_parent_class)->delete_event)
     return GTK_WIDGET_CLASS (vinagre_window_parent_class)->delete_event (widget, event);
@@ -198,7 +217,8 @@
   GObjectClass *object_class    = G_OBJECT_CLASS (klass);
   GtkWidgetClass *widget_class  = GTK_WIDGET_CLASS (klass);
 
-  object_class->finalize     = vinagre_window_finalize;
+  object_class->finalize = vinagre_window_finalize;
+  object_class->dispose  = vinagre_window_dispose;
 
   widget_class->key_press_event    = vinagre_window_key_press_cb;
   widget_class->window_state_event = vinagre_window_state_event_cb;
@@ -291,7 +311,8 @@
     g_free (error);
 }
 
-static void update_recent_connections (VinagreWindow *window)
+static void
+update_recent_connections (VinagreWindow *window)
 {
   VinagreWindowPrivate *p = window->priv;
 
@@ -465,11 +486,11 @@
   update_recent_connections (window);
 
   recent_manager = gtk_recent_manager_get_default ();
-  g_signal_connect (recent_manager,
+  window->priv->update_recents_menu_ui_id = g_signal_connect (
+		    recent_manager,
 		    "changed",
 		    G_CALLBACK (recent_manager_changed),
 		    window);
-
 }
 
 void
@@ -1143,4 +1164,60 @@
   return window->priv->manager;
 }
 
+static void
+add_connection (VinagreTab *tab, GList **res)
+{
+  VinagreConnection *conn;
+	
+  conn = vinagre_tab_get_conn (tab);
+	
+  *res = g_list_prepend (*res, conn);
+}
+
+/* Returns a newly allocated list with all the connections in the window */
+GList *
+vinagre_window_get_connections (VinagreWindow *window)
+{
+  GList *res = NULL;
+
+  g_return_val_if_fail (VINAGRE_IS_WINDOW (window), NULL);
+	
+  gtk_container_foreach (GTK_CONTAINER (window->priv->notebook),
+			 (GtkCallback)add_connection,
+			  &res);
+			       
+  res = g_list_reverse (res);
+	
+  return res;
+}
+
+VinagreTab *
+vinagre_window_conn_exists (VinagreWindow *window, VinagreConnection *conn)
+{
+  VinagreConnection *c;
+  VinagreTab *tab = NULL;
+  GList *l;
+
+  g_return_val_if_fail (VINAGRE_IS_WINDOW (window), NULL);
+  g_return_val_if_fail (VINAGRE_IS_CONNECTION (conn), NULL);
+
+  l = vinagre_window_get_connections (window);
+
+  while (l != NULL)
+    {
+      c = VINAGRE_CONNECTION (l->data);
+
+      if (!strcmp (vinagre_connection_get_host (conn), vinagre_connection_get_host (c)) &&
+	  vinagre_connection_get_port (conn) == vinagre_connection_get_port (c))
+	{
+	  tab = g_object_get_data (G_OBJECT (c), VINAGRE_TAB_KEY);
+	  break;
+	}
+      l = l->next;
+    }
+  g_list_free (l);
+
+  return tab;
+}
+
 /* vim: ts=8 */

Modified: trunk/src/vinagre-window.h
==============================================================================
--- trunk/src/vinagre-window.h	(original)
+++ trunk/src/vinagre-window.h	Sat May 10 02:41:21 2008
@@ -51,9 +51,9 @@
   GtkWindowClass parent_class;
 };
 
-GType 		 vinagre_window_get_type 			(void) G_GNUC_CONST;
+GType 		 vinagre_window_get_type 		(void) G_GNUC_CONST;
 
-VinagreWindow   *vinagre_window_new			();
+VinagreWindow   *vinagre_window_new			(void);
 
 VinagreTab	*vinagre_window_create_tab		(VinagreWindow         *window,
 							 gboolean              jump_to);
@@ -90,6 +90,11 @@
 void		vinagre_window_set_title		(VinagreWindow *window);
 
 void		vinagre_window_update_machine_menu_sensitivity (VinagreWindow	*window);
+
+GList 		*vinagre_window_get_connections		(VinagreWindow *window);
+VinagreTab	*vinagre_window_conn_exists		(VinagreWindow *window,
+							 VinagreConnection *conn);
+
 G_END_DECLS
 
 #endif  /* __VINAGRE_WINDOW_H__  */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]