From c375f862a03099f29ea269c2e850f78ae0bbcca8 Mon Sep 17 00:00:00 2001 From: Christian Dywan Date: Fri, 22 Aug 2008 03:59:07 +0200 Subject: [PATCH] Implement single instance support with Unique --- configure.in | 5 ++ midori/Makefile.am | 2 + midori/main.c | 28 +++++-- midori/midori-app.c | 183 +++++++++++++++++++++++++++++++++++++++++++ midori/midori-app.h | 10 +++ midori/wscript_build | 2 +- wscript | 6 +- 7 files changed, 229 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index ed99b8ad..702d8812 100644 --- a/configure.in +++ b/configure.in @@ -38,6 +38,11 @@ if test x"$enable_debug" = x"yes"; then ]) fi +# Checks for UNIQUE +PKG_CHECK_MODULES(UNIQUE, unique-1.0 >= 0.9, have_unique=true, have_unique=false) +AC_SUBST(UNIQUE_CFLAGS) +AC_SUBST(UNIQUE_LIBS) + # Checks for GIO2 PKG_CHECK_MODULES(GIO, gio-2.0 >= 2.16, have_gio=true, have_gio=false) AC_SUBST(GIO_CFLAGS) diff --git a/midori/Makefile.am b/midori/Makefile.am index 99ed5170..73229a72 100644 --- a/midori/Makefile.am +++ b/midori/Makefile.am @@ -1,4 +1,5 @@ INCLUDES = \ + $(UNIQUE_CFLAGS) \ $(GIO_CFLAGS) \ $(GTK_CFLAGS) \ $(GTKSOURCEVIEW_CFLAGS) \ @@ -8,6 +9,7 @@ INCLUDES = \ AM_CFLAGS = -DMIDORI_LOCALEDIR=\""$(localedir)"\" LDADD = \ + $(UNIQUE_LIBS) \ $(GIO_LIBS) \ $(GTK_LIBS) \ $(GTKSOURCEVIEW_LIBS)\ diff --git a/midori/main.c b/midori/main.c index 863ccee6..a1651468 100644 --- a/midori/main.c +++ b/midori/main.c @@ -384,6 +384,8 @@ main (int argc, char** argv) { gboolean version; + MidoriApp* app; + gboolean result; GError* error; GOptionEntry entries[] = { @@ -446,6 +448,23 @@ main (int argc, return 1; } + app = midori_app_new (); + if (midori_app_instance_is_running (app)) + { + /* TODO: Open as many tabs as we have uris, seperated by pipes */ + if (argc > 1) + result = midori_app_instance_send_uris (app, argv+1); + else + result = midori_app_instance_send_activate (app); + + if (result) + return 0; + + /* FIXME: Do we want a graphical error message? */ + g_print (_("An instance of Midori is already running but not responding.\n")); + return 1; + } + /* Load configuration files */ GString* error_messages = g_string_new (NULL); gchar* config_path = g_build_filename (g_get_user_config_dir (), @@ -590,11 +609,10 @@ main (int argc, g_signal_connect_after (trash, "add-item", G_CALLBACK (midori_web_list_add_item_cb), NULL); - MidoriApp* app = g_object_new (MIDORI_TYPE_APP, - "settings", settings, - "trash", trash, - "search-engines", search_engines, - NULL); + g_object_set (app, "settings", settings, + "trash", trash, + "search-engines", search_engines, + NULL); MidoriBrowser* browser = g_object_new (MIDORI_TYPE_BROWSER, "settings", settings, diff --git a/midori/midori-app.c b/midori/midori-app.c index 58335a2f..660bbe4d 100644 --- a/midori/midori-app.c +++ b/midori/midori-app.c @@ -9,11 +9,20 @@ See the file COPYING for the full license text. */ +#if HAVE_CONFIG_H + #include +#endif + #include "midori-app.h" +#include #include #include +#ifdef HAVE_UNIQUE + #include +#endif + struct _MidoriApp { GObject parent_instance; @@ -25,6 +34,8 @@ struct _MidoriApp MidoriWebSettings* settings; MidoriWebList* trash; MidoriWebList* search_engines; + + gpointer instance; }; G_DEFINE_TYPE (MidoriApp, midori_app, G_TYPE_OBJECT) @@ -163,9 +174,63 @@ midori_app_constructor (GType type, type, n_construct_properties, construct_properties); } +#ifdef HAVE_UNIQUE +static UniqueResponse +midori_browser_message_received_cb (UniqueApp* instance, + UniqueCommand command, + UniqueMessageData* message, + guint time, + MidoriApp* app) +{ + UniqueResponse response; + gchar** uris; + + switch (command) + { + case UNIQUE_ACTIVATE: + g_print("activate\n"); + gtk_window_set_screen (GTK_WINDOW (app->browser), + unique_message_data_get_screen (message)); + gtk_window_present (GTK_WINDOW (app->browser)); + response = UNIQUE_RESPONSE_OK; + break; + case UNIQUE_OPEN: + g_print("open\n"); + uris = unique_message_data_get_uris (message); + if (!uris) + response = UNIQUE_RESPONSE_FAIL; + else + { + g_print("open uris\n"); + while (*uris) + { + midori_browser_add_uri (app->browser, *uris); + g_print ("uri: %s\n", *uris); + uris++; + } + /* g_strfreev (uris); */ + response = UNIQUE_RESPONSE_OK; + } + break; + default: + g_print("fail\n"); + response = UNIQUE_RESPONSE_FAIL; + break; + } + + return response; +} +#endif + static void midori_app_init (MidoriApp* app) { + #ifdef HAVE_UNIQUE + gchar* display_name; + gchar* instance_name; + guint i, n; + #endif + g_assert (!_midori_app_singleton); _midori_app_singleton = app; @@ -175,6 +240,22 @@ midori_app_init (MidoriApp* app) app->settings = midori_web_settings_new (); app->trash = midori_web_list_new (); app->search_engines = midori_web_list_new (); + + #ifdef HAVE_UNIQUE + display_name = g_strdup (gdk_display_get_name (gdk_display_get_default ())); + n = strlen (display_name); + for (i = 0; i < n; i++) + if (display_name[i] == ':' || display_name[i] == '.') + display_name[i] = '_'; + instance_name = g_strdup_printf ("de.twotoasts.midori_%s", display_name); + app->instance = unique_app_new (instance_name, NULL); + g_free (instance_name); + g_free (display_name); + g_signal_connect (app->instance, "message-received", + G_CALLBACK (midori_browser_message_received_cb), app); + #else + app->instance = NULL; + #endif } static void @@ -188,6 +269,9 @@ midori_app_finalize (GObject* object) g_object_unref (app->settings); g_object_unref (app->trash); + if (app->instance) + g_object_unref (app->instance); + G_OBJECT_CLASS (midori_app_parent_class)->finalize (object); } @@ -321,8 +405,99 @@ midori_app_new (void) return app; } +/** + * midori_app_instance_is_running: + * @app: a #MidoriApp + * + * Determines whether an instance of Midori is + * already running on the default display. + * + * If Midori was built without single instance support + * this function will always return %FALSE. + * + * Return value: %TRUE if an instance is already running + **/ +gboolean +midori_app_instance_is_running (MidoriApp* app) +{ + g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); + + #ifdef HAVE_UNIQUE + return unique_app_is_running (app->instance); + #else + return FALSE; + #endif +} + +/** + * midori_app_instance_send_activate: + * @app: a #MidoriApp + * + * Sends a message to an instance of Midori already + * running on the default display, asking to activate it. + * + * Practically the current browser will be focussed. + * + * Return value: %TRUE if the message was sent successfully + **/ +gboolean +midori_app_instance_send_activate (MidoriApp* app) +{ + #ifdef HAVE_UNIQUE + UniqueResponse response; + #endif + + g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); + g_return_val_if_fail (midori_app_instance_is_running (app), FALSE); + + #ifdef HAVE_UNIQUE + response = unique_app_send_message (app->instance, UNIQUE_ACTIVATE, NULL); + if (response == UNIQUE_RESPONSE_OK) + return TRUE; + #endif + return FALSE; +} + +/** + * midori_app_instance_send_uris: + * @app: a #MidoriApp + * @uris: a string vector of URIs + * + * Sends a message to an instance of Midori already + * running on the default display, asking to open @uris. + * + * The strings in @uris will each be opened in a new tab. + * + * Return value: %TRUE if the message was sent successfully + **/ +gboolean +midori_app_instance_send_uris (MidoriApp* app, + gchar** uris) +{ + #ifdef HAVE_UNIQUE + UniqueMessageData* message; + UniqueResponse response; + #endif + + g_return_val_if_fail (MIDORI_IS_APP (app), FALSE); + g_return_val_if_fail (midori_app_instance_is_running (app), FALSE); + g_return_val_if_fail (uris != NULL, FALSE); + + #ifdef HAVE_UNIQUE + message = unique_message_data_new (); + unique_message_data_set_uris (message, uris); + response = unique_app_send_message (app->instance, UNIQUE_OPEN, message); + unique_message_data_free (message); + if (response == UNIQUE_RESPONSE_OK) + return TRUE; + #endif + return FALSE; +} + /** * midori_app_add_browser: + * @app: a #MidoriApp + * @browser: a #MidoriBrowser * * Adds a #MidoriBrowser to the #MidoriApp singleton. * @@ -336,6 +511,9 @@ void midori_app_add_browser (MidoriApp* app, MidoriBrowser* browser) { + g_return_if_fail (MIDORI_IS_APP (app)); + g_return_if_fail (MIDORI_IS_BROWSER (browser)); + gtk_window_add_accel_group (GTK_WINDOW (browser), app->accel_group); g_object_connect (browser, "signal::focus-in-event", midori_browser_focus_in_event_cb, app, @@ -346,6 +524,11 @@ midori_app_add_browser (MidoriApp* app, NULL); app->browsers = g_list_prepend (app->browsers, browser); + + #ifdef HAVE_UNIQUE + if (app->instance) + unique_app_watch_window (app->instance, GTK_WINDOW (browser)); + #endif } /** diff --git a/midori/midori-app.h b/midori/midori-app.h index 6735a98c..637cb6ef 100644 --- a/midori/midori-app.h +++ b/midori/midori-app.h @@ -53,6 +53,16 @@ midori_app_get_type (void); MidoriApp* midori_app_new (void); +gboolean +midori_app_instance_is_running (MidoriApp* app); + +gboolean +midori_app_instance_send_activate (MidoriApp* app); + +gboolean +midori_app_instance_send_uris (MidoriApp* app, + gchar** uris); + void midori_app_add_browser (MidoriApp* app, MidoriBrowser* browser); diff --git a/midori/wscript_build b/midori/wscript_build index 0963128e..cc6435cf 100644 --- a/midori/wscript_build +++ b/midori/wscript_build @@ -5,5 +5,5 @@ obj = bld.create_obj ('cc', 'program') obj.target = 'midori' obj.includes = '.. ../katze' obj.find_sources_in_dirs ('.') -obj.uselib = 'GIO GTK GTKSOURCEVIEW WEBKIT LIBXML' +obj.uselib = 'UNIQUE GIO GTK GTKSOURCEVIEW WEBKIT LIBXML' obj.uselib_local = 'katze' diff --git a/wscript b/wscript index 52fa64f9..10964a59 100644 --- a/wscript +++ b/wscript @@ -35,6 +35,10 @@ def configure (conf): nls = 'no' conf.check_message_custom ('localization', 'support', nls) + conf.check_pkg ('unique-1.0', destvar='UNIQUE', vnum='0.9', mandatory=False) + single_instance = ['no','yes'][conf.env['HAVE_UNIQUE'] == 1] + conf.check_message_custom ('single instance', 'support', single_instance) + conf.check_pkg ('gio-2.0', destvar='GIO', vnum='2.16.0', mandatory=False) conf.check_pkg ('gtk+-2.0', destvar='GTK', vnum='2.6.0', mandatory=True) conf.check_pkg ('gtksourceview-2.0', destvar='GTKSOURCEVIEW', vnum='2.0', mandatory=False) @@ -51,7 +55,7 @@ def configure (conf): conf.define ('PACKAGE_VERSION', VERSION) conf.define ('PACKAGE_NAME', APPNAME) - conf.define ('PACKAGE_BUGREPORT', 'http://software.twotoasts.de/bugs') + conf.define ('PACKAGE_BUGREPORT', 'http://www.twotoasts.de/bugs') conf.define ('GETTEXT_PACKAGE', APPNAME) conf.write_config_header ('config.h')