From: Robert O'Callahan Subject: implement startup-notification References: https://bugzilla.novell.com/show_bug.cgi?id=115417 https://bugzilla.mozilla.org/show_bug.cgi?id=223492 Index: config/autoconf.mk.in =================================================================== --- config/autoconf.mk.in.orig +++ config/autoconf.mk.in @@ -223,6 +223,10 @@ MOZ_ENABLE_GNOMEUI = @MOZ_ENABLE_GNOMEUI MOZ_GNOMEUI_CFLAGS = @MOZ_GNOMEUI_CFLAGS@ MOZ_GNOMEUI_LIBS = @MOZ_GNOMEUI_LIBS@ +MOZ_ENABLE_STARTUP_NOTIFICATION = @MOZ_ENABLE_STARTUP_NOTIFICATION@ +MOZ_STARTUP_NOTIFICATION_CFLAGS = @MOZ_STARTUP_NOTIFICATION_CFLAGS@ +MOZ_STARTUP_NOTIFICATION_LIBS = @MOZ_STARTUP_NOTIFICATION_LIBS@ + MOZ_GNOMEVFS_CFLAGS = @MOZ_GNOMEVFS_CFLAGS@ MOZ_GNOMEVFS_LIBS = @MOZ_GNOMEVFS_LIBS@ Index: configure.in =================================================================== --- configure.in.orig +++ configure.in @@ -125,6 +125,7 @@ GNOMEVFS_VERSION=2.0 GNOMEUI_VERSION=2.2.0 GCONF_VERSION=1.2.1 LIBGNOME_VERSION=2.0 +STARTUP_NOTIFICATION_VERSION=0.8 dnl Set various checks dnl ======================================================== @@ -4088,6 +4089,42 @@ fi # COMPILE_ENVIRONMENT AC_SUBST(MOZ_DEFAULT_TOOLKIT) +dnl ======================================================== +dnl = startup-notification support module +dnl ======================================================== + +if test "$MOZ_ENABLE_GTK2" +then + MOZ_ENABLE_STARTUP_NOTIFICATION= + + MOZ_ARG_ENABLE_BOOL(startup-notification, + [ --enable-startup-notification Enable startup-notification support (default: disabled) ], + MOZ_ENABLE_STARTUP_NOTIFICATION=force, + MOZ_ENABLE_STARTUP_NOTIFICATION=) + + if test "$MOZ_ENABLE_STARTUP_NOTIFICATION" + then + PKG_CHECK_MODULES(MOZ_STARTUP_NOTIFICATION, + libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_VERSION, + [MOZ_ENABLE_STARTUP_NOTIFICATION=1], [ + if test "$MOZ_ENABLE_STARTUP_NOTIFICATION" = "force" + then + AC_MSG_ERROR([* * * Could not find startup-notification >= $STARTUP_NOTIFICATION_VERSION]) + fi + MOZ_ENABLE_STARTUP_NOTIFICATION= + ]) + fi + + if test "$MOZ_ENABLE_STARTUP_NOTIFICATION"; then + AC_DEFINE(MOZ_ENABLE_STARTUP_NOTIFICATION) + fi + + TK_LIBS="$TK_LIBS $MOZ_STARTUP_NOTIFICATION_LIBS" +fi +AC_SUBST(MOZ_ENABLE_STARTUP_NOTIFICATION) +AC_SUBST(MOZ_STARTUP_NOTIFICATION_CFLAGS) +AC_SUBST(MOZ_STARTUP_NOTIFICATION_LIBS) + AC_SUBST(GTK_CONFIG) AC_SUBST(TK_CFLAGS) AC_SUBST(TK_LIBS) Index: toolkit/components/remote/Makefile.in =================================================================== --- toolkit/components/remote/Makefile.in.orig +++ toolkit/components/remote/Makefile.in @@ -56,8 +56,9 @@ REQUIRES = \ string \ appcomps \ toolkitcomps \ - appcomps \ + appshell \ xulapp \ + layout \ widget \ gfx \ dom \ Index: toolkit/components/remote/nsGTKRemoteService.cpp =================================================================== --- toolkit/components/remote/nsGTKRemoteService.cpp.orig +++ toolkit/components/remote/nsGTKRemoteService.cpp @@ -50,7 +50,7 @@ #include "nsIBaseWindow.h" #include "nsIDocShell.h" -#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" #include "nsIGenericFactory.h" #include "nsILocalFile.h" #include "nsIObserverService.h" @@ -58,6 +58,8 @@ #include "nsIServiceManager.h" #include "nsIWeakReference.h" #include "nsIWidget.h" +#include "nsIAppShellService.h" +#include "nsAppShellCID.h" #include "nsCOMPtr.h" #include "nsString.h" @@ -72,6 +74,10 @@ #include "nsISuiteRemoteService.h" #endif +#ifdef MOZ_WIDGET_GTK2 +#include "nsGTKToolkit.h" +#endif + #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION" #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK" #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND" @@ -155,20 +161,44 @@ nsGTKRemoteService::StartupHandler(const return PL_DHASH_NEXT; } -NS_IMETHODIMP -nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow) +static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) { // get the native window for this instance nsCOMPtr scriptObject (do_QueryInterface(aWindow)); - NS_ENSURE_TRUE(scriptObject, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(scriptObject, nsnull); nsCOMPtr baseWindow (do_QueryInterface(scriptObject->GetDocShell())); - NS_ENSURE_TRUE(baseWindow, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(baseWindow, nsnull); nsCOMPtr mainWidget; baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; +} + +static nsGTKToolkit* GetGTKToolkit() +{ + nsCOMPtr svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); + if (!svc) + return nsnull; + nsCOMPtr window; + svc->GetHiddenDOMWindow(getter_AddRefs(window)); + if (!window) + return nsnull; + nsIWidget* widget = GetMainWidget(window); + if (!widget) + return nsnull; + nsIToolkit* toolkit = widget->GetToolkit(); + if (!toolkit) + return nsnull; + return NS_STATIC_CAST(nsGTKToolkit*, toolkit); +} + +NS_IMETHODIMP +nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow) +{ + nsIWidget* mainWidget = GetMainWidget(aWindow); NS_ENSURE_TRUE(mainWidget, NS_ERROR_FAILURE); // walk up the widget tree and find the toplevel window in the @@ -260,7 +290,7 @@ nsGTKRemoteService::EnsureAtoms(void) #ifndef MOZ_XUL_APP const char* -nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow, PRUint32 aTimestamp) { nsresult rv; @@ -283,8 +313,60 @@ nsGTKRemoteService::HandleCommand(char* } #else //MOZ_XUL_APP + +// Set desktop startup ID to the passed ID, if there is one, so that any created +// windows get created with the right window manager metadata, and any windows +// that get new tabs and are activated also get the right WM metadata. +// If there is no desktop startup ID, then use the X event's timestamp +// for _NET_ACTIVE_WINDOW when the window gets focused or shown. +static void +SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID, + PRUint32 aTimestamp) { +#ifdef MOZ_WIDGET_GTK2 + nsGTKToolkit* toolkit = GetGTKToolkit(); + if (!toolkit) + return; + if (!aDesktopStartupID.IsEmpty()) { + toolkit->SetDesktopStartupID(aDesktopStartupID); + } else { + toolkit->SetFocusTimestamp(aTimestamp); + } +#endif +} + +static PRBool +FindExtensionParameterInCommand(const char* aParameterName, + const nsACString& aCommand, + char aSeparator, + nsACString* aValue) +{ + nsCAutoString searchFor; + searchFor.Append(aSeparator); + searchFor.Append(aParameterName); + searchFor.Append('='); + + nsACString::const_iterator start, end; + aCommand.BeginReading(start); + aCommand.EndReading(end); + if (!FindInReadable(searchFor, start, end)) + return PR_FALSE; + + nsACString::const_iterator charStart, charEnd; + charStart = end; + aCommand.EndReading(charEnd); + nsACString::const_iterator idStart = charStart, idEnd; + if (FindCharInReadable(aSeparator, charStart, charEnd)) { + idEnd = charStart; + } else { + idEnd = charEnd; + } + *aValue = nsDependentCSubstring(idStart, idEnd); + return PR_TRUE; +} + const char* -nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow, + PRUint32 aTimestamp) { nsresult rv; @@ -314,6 +396,12 @@ nsGTKRemoteService::HandleCommand(char* #endif if (!command.EqualsLiteral("ping")) { + nsCAutoString desktopStartupID; + nsDependentCString cmd(aCommand); + FindExtensionParameterInCommand("DESKTOP_STARTUP_ID", + cmd, '\n', + &desktopStartupID); + char* argv[3] = {"dummyappname", "-remote", aCommand}; rv = cmdline->Init(3, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT); if (NS_FAILED(rv)) @@ -322,6 +410,8 @@ nsGTKRemoteService::HandleCommand(char* if (aWindow) cmdline->SetWindowContext(aWindow); + SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); + rv = cmdline->Run(); if (NS_ERROR_ABORT == rv) return "500 command not parseable"; @@ -333,7 +423,8 @@ nsGTKRemoteService::HandleCommand(char* } const char* -nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow, + PRUint32 aTimestamp) { nsresult rv; @@ -364,6 +455,8 @@ nsGTKRemoteService::HandleCommandLine(ch if (NS_FAILED(rv)) return "509 internal error"; + nsCAutoString desktopStartupID; + char **argv = (char**) malloc(sizeof(char*) * argc); if (!argv) return "509 internal error"; @@ -372,6 +465,12 @@ nsGTKRemoteService::HandleCommandLine(ch for (int i = 0; i < argc; ++i) { argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]); + if (i == 0) { + nsDependentCString cmd(argv[0]); + FindExtensionParameterInCommand("DESKTOP_STARTUP_ID", + cmd, ' ', + &desktopStartupID); + } #ifdef DEBUG_bsmedberg printf(" argv[%i]:\t%s\n", i, argv[i]); #endif @@ -386,7 +485,10 @@ nsGTKRemoteService::HandleCommandLine(ch if (aWindow) cmdline->SetWindowContext(aWindow); + SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); + rv = cmdline->Run(); + if (NS_ERROR_ABORT == rv) return "500 command not parseable"; @@ -486,7 +588,7 @@ nsGTKRemoteService::HandlePropertyChange return FALSE; // cool, we got the property data. - const char *response = HandleCommand(data, window); + const char *response = HandleCommand(data, window, pevent->time); // put the property onto the window as the response XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window), @@ -531,7 +633,7 @@ nsGTKRemoteService::HandlePropertyChange return FALSE; // cool, we got the property data. - const char *response = HandleCommandLine(data, window); + const char *response = HandleCommandLine(data, window, pevent->time); // put the property onto the window as the response XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window), Index: toolkit/components/remote/nsGTKRemoteService.h =================================================================== --- toolkit/components/remote/nsGTKRemoteService.h.orig +++ toolkit/components/remote/nsGTKRemoteService.h @@ -80,10 +80,12 @@ private: nsIWeakReference* aData, void* aClosure); - static const char* HandleCommand(char* aCommand, nsIDOMWindow* aWindow); + static const char* HandleCommand(char* aCommand, nsIDOMWindow* aWindow, + PRUint32 aTimestamp); #ifdef MOZ_XUL_APP - static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow); + static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow, + PRUint32 aTimestamp); #endif static gboolean HandlePropertyChange(GtkWidget *widget, Index: toolkit/library/Makefile.in =================================================================== --- toolkit/library/Makefile.in.orig +++ toolkit/library/Makefile.in @@ -350,6 +350,10 @@ ifneq (,$(filter gtk gtk2 qt xlib,$(MOZ_ EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS) $(MOZ_GTK_LDFLAGS) $(MOZ_XFT_LIBS) $(MOZ_GTK2_LIBS) $(XT_LIBS) endif +ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +EXTRA_DSO_LDOPTS += $(MOZ_STARTUP_NOTIFICATION_LIBS) +endif + ifdef MOZ_ENABLE_XPRINT EXTRA_DSO_LDOPTS += $(MOZ_XPRINT_LDFLAGS) endif Index: toolkit/xre/Makefile.in =================================================================== --- toolkit/xre/Makefile.in.orig +++ toolkit/xre/Makefile.in @@ -69,6 +69,7 @@ REQUIRES = \ shellservice \ string \ uriloader \ + layout \ widget \ windowwatcher \ xpcom \ Index: toolkit/xre/nsAppRunner.cpp =================================================================== --- toolkit/xre/nsAppRunner.cpp.orig +++ toolkit/xre/nsAppRunner.cpp @@ -96,6 +96,7 @@ #include "nsIWindowWatcher.h" #include "nsIXULAppInfo.h" #include "nsIXULRuntime.h" +#include "nsIScriptGlobalObject.h" #ifdef XP_WIN #include "nsIWinAppHelper.h" #endif @@ -115,6 +116,12 @@ #include "nsXULAppAPI.h" #include "nsXREDirProvider.h" #include "nsToolkitCompsCID.h" +#include "nsPIDOMWindow.h" +#include "nsIDOMWindowInternal.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIDocShell.h" +#include "nsAppShellCID.h" #include "nsINIParser.h" @@ -262,6 +269,9 @@ static char **gRestartArgv; #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) #include #endif //MOZ_WIDGET_GTK || MOZ_WIDGET_GTK2 +#if defined(MOZ_WIDGET_GTK2) +#include "nsGTKToolkit.h" +#endif #if defined(MOZ_WIDGET_QT) #include @@ -1105,7 +1115,7 @@ DumpVersion() // use int here instead of a PR type since it will be returned // from main - just to keep types consistent static int -HandleRemoteArgument(const char* remote) +HandleRemoteArgument(const char* remote, const char* aDesktopStartupID) { nsresult rv; ArgResult ar; @@ -1146,7 +1156,7 @@ HandleRemoteArgument(const char* remote) nsXPIDLCString response; PRBool success = PR_FALSE; rv = client.SendCommand(program.get(), username, profile, remote, - getter_Copies(response), &success); + aDesktopStartupID, getter_Copies(response), &success); // did the command fail? if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n", @@ -1163,7 +1173,7 @@ HandleRemoteArgument(const char* remote) } static PRBool -RemoteCommandLine() +RemoteCommandLine(const char* aDesktopStartupID) { nsresult rv; ArgResult ar; @@ -1195,7 +1205,7 @@ RemoteCommandLine() nsXPIDLCString response; PRBool success = PR_FALSE; rv = client.SendCommandLine(program.get(), username, nsnull, - gArgc, gArgv, + gArgc, gArgv, aDesktopStartupID, getter_Copies(response), &success); // did the command fail? if (NS_FAILED(rv) || !success) @@ -2080,6 +2090,41 @@ public: #ifdef MOZ_WIDGET_GTK2 #include "prlink.h" typedef void (*_g_set_application_name_fn)(const gchar *application_name); + +static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) +{ + // get the native window for this instance + nsCOMPtr scriptObject + (do_QueryInterface(aWindow)); + NS_ENSURE_TRUE(scriptObject, nsnull); + + nsCOMPtr baseWindow + (do_QueryInterface(scriptObject->GetDocShell())); + NS_ENSURE_TRUE(baseWindow, nsnull); + + nsCOMPtr mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; +} + +static nsGTKToolkit* GetGTKToolkit() +{ + nsCOMPtr svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); + if (!svc) + return nsnull; + nsCOMPtr window; + svc->GetHiddenDOMWindow(getter_AddRefs(window)); + if (!window) + return nsnull; + nsIWidget* widget = GetMainWidget(window); + if (!widget) + return nsnull; + nsIToolkit* toolkit = widget->GetToolkit(); + if (!toolkit) + return nsnull; + return NS_STATIC_CAST(nsGTKToolkit*, toolkit); +} + #endif int @@ -2248,6 +2293,19 @@ XRE_main(int argc, char* argv[], const n return 0; } +#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) || defined(MOZ_ENABLE_XREMOTE) + // Stash DESKTOP_STARTUP_ID becaus gtk_init will clear it. +#define HAVE_DESKTOP_STARTUP_ID + char* desktopStartupID = PR_GetEnv("DESKTOP_STARTUP_ID"); + if (desktopStartupID) { + if (desktopStartupID[0] == 0) { + desktopStartupID = nsnull; + } else { + desktopStartupID = strdup(desktopStartupID); + } + } +#endif + #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) // setup for private colormap. Ideally we'd like to do this // in nsAppShell::Create, but we need to get in before gtk @@ -2273,6 +2331,7 @@ XRE_main(int argc, char* argv[], const n if (glib2) { PR_UnloadLibrary(glib2); } + gtk_window_set_auto_startup_notification(PR_FALSE); #endif gtk_widget_set_default_visual(gdk_rgb_get_visual()); @@ -2337,12 +2396,12 @@ XRE_main(int argc, char* argv[], const n return 1; } if (ar) { - return HandleRemoteArgument(xremotearg); + return HandleRemoteArgument(xremotearg, desktopStartupID); } if (!PR_GetEnv("MOZ_NO_REMOTE")) { // Try to remote the entire command line. If this fails, start up normally. - if (RemoteCommandLine()) + if (RemoteCommandLine(desktopStartupID)) return 0; } #endif @@ -2586,6 +2645,13 @@ XRE_main(int argc, char* argv[], const n NS_TIMELINE_LEAVE("appStartup->CreateHiddenWindow"); NS_ENSURE_SUCCESS(rv, 1); +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) + nsRefPtr toolkit = GetGTKToolkit(); + if (toolkit && desktopStartupID) { + toolkit->SetDesktopStartupID(nsDependentCString(desktopStartupID)); + } +#endif + // Extension Compatibility Checking and Startup if (gAppData->flags & NS_XRE_ENABLE_EXTENSION_MANAGER) { nsCOMPtr em(do_GetService("@mozilla.org/extensions/manager;1")); @@ -2766,9 +2832,28 @@ XRE_main(int argc, char* argv[], const n } #endif +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_TOOLKIT_GTK2) + nsGTKToolkit* toolkit = GetGTKToolkit(); + if (toolkit) { + nsCAutoString currentDesktopStartupID; + toolkit->GetDesktopStartupID(¤tDesktopStartupID); + if (!currentDesktopStartupID.IsEmpty()) { + nsCAutoString desktopStartupEnv; + desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); + desktopStartupEnv.Append(currentDesktopStartupID); + // Leak it with extreme prejudice! + PR_SetEnv(ToNewCString(desktopStartupEnv)); + } + } +#endif + rv = LaunchChild(nativeApp, appInitiatedRestart, upgraded ? -1 : 0); return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; } +#ifdef HAVE_DESKTOP_STARTUP_ID + free(desktopStartupID); +#endif + return NS_FAILED(rv) ? 1 : 0; } Index: widget/src/gtk2/Makefile.in =================================================================== --- widget/src/gtk2/Makefile.in.orig +++ widget/src/gtk2/Makefile.in @@ -97,6 +97,7 @@ EXTRA_DSO_LDOPTS += \ $(MOZ_COMPONENT_LIBS) \ -lgkgfx \ -lgtkxtbin \ + $(MOZ_STARTUP_NOTIFICATION_LIBS) \ $(XLDFLAGS) \ $(XLIBS) \ $(MOZ_GTK2_LIBS) @@ -107,14 +108,15 @@ endif EXPORTS = \ nsIGdkPixbufImage.h \ + nsGTKToolkit.h \ mozdrawingarea.h \ mozcontainer.h \ $(NULL) include $(topsrcdir)/config/rules.mk -CFLAGS += $(MOZ_GTK2_CFLAGS) -CXXFLAGS += $(MOZ_GTK2_CFLAGS) +CFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) +CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) DEFINES += -DUSE_XIM Index: widget/src/gtk2/nsGTKToolkit.h =================================================================== --- /dev/null +++ widget/src/gtk2/nsGTKToolkit.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GTKTOOLKIT_H +#define GTKTOOLKIT_H + +#include "nsIToolkit.h" +#include "nsString.h" +#include + +/** + * Wrapper around the thread running the message pump. + * The toolkit abstraction is necessary because the message pump must + * execute within the same thread that created the widget under Win32. + */ + +class nsGTKToolkit : public nsIToolkit +{ +public: + nsGTKToolkit(); + virtual ~nsGTKToolkit(); + + NS_DECL_ISUPPORTS + + NS_IMETHOD Init(PRThread *aThread); + + void CreateSharedGC(void); + GdkGC *GetSharedGC(void); + + /** + * Get/set our value of DESKTOP_STARTUP_ID. When non-empty, this is applied + * to the next toplevel window to be shown or focused (and then immediately + * cleared). + */ + void SetDesktopStartupID(const nsACString& aID) { mDesktopStartupID = aID; } + void GetDesktopStartupID(nsACString* aID) { *aID = mDesktopStartupID; } + + /** + * Get/set the timestamp value to be used, if non-zero, to focus the + * next top-level window to be shown or focused (upon which it is cleared). + */ + void SetFocusTimestamp(PRUint32 aTimestamp) { mFocusTimestamp = aTimestamp; } + PRUint32 GetFocusTimestamp() { return mFocusTimestamp; } + +private: + GdkGC *mSharedGC; + nsCString mDesktopStartupID; + PRUint32 mFocusTimestamp; +}; + +#endif // GTKTOOLKIT_H Index: widget/src/gtk2/nsToolkit.cpp =================================================================== --- widget/src/gtk2/nsToolkit.cpp.orig +++ widget/src/gtk2/nsToolkit.cpp @@ -38,7 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nscore.h" // needed for 'nsnull' -#include "nsToolkit.h" +#include "nsGTKToolkit.h" // // Static thread local storage index of the Toolkit @@ -51,9 +51,10 @@ static PRUintn gToolkitTLSIndex = 0; // constructor // //------------------------------------------------------------------------- -nsToolkit::nsToolkit() +nsGTKToolkit::nsGTKToolkit() { mSharedGC = nsnull; + mFocusTimestamp = 0; } //------------------------------------------------------------------------- @@ -61,7 +62,7 @@ nsToolkit::nsToolkit() // destructor // //------------------------------------------------------------------------- -nsToolkit::~nsToolkit() +nsGTKToolkit::~nsGTKToolkit() { if (mSharedGC) { gdk_gc_unref(mSharedGC); @@ -77,9 +78,9 @@ nsToolkit::~nsToolkit() // //------------------------------------------------------------------------- -NS_IMPL_ISUPPORTS1(nsToolkit, nsIToolkit) +NS_IMPL_ISUPPORTS1(nsGTKToolkit, nsIToolkit) -void nsToolkit::CreateSharedGC(void) +void nsGTKToolkit::CreateSharedGC(void) { GdkPixmap *pixmap; @@ -91,7 +92,7 @@ void nsToolkit::CreateSharedGC(void) gdk_pixmap_unref(pixmap); } -GdkGC *nsToolkit::GetSharedGC(void) +GdkGC *nsGTKToolkit::GetSharedGC(void) { return gdk_gc_ref(mSharedGC); } @@ -100,7 +101,7 @@ GdkGC *nsToolkit::GetSharedGC(void) // // //------------------------------------------------------------------------- -NS_IMETHODIMP nsToolkit::Init(PRThread *aThread) +NS_IMETHODIMP nsGTKToolkit::Init(PRThread *aThread) { CreateSharedGC(); @@ -135,7 +136,7 @@ NS_METHOD NS_GetCurrentToolkit(nsIToolki // Create a new toolkit for this thread... // if (!toolkit) { - toolkit = new nsToolkit(); + toolkit = new nsGTKToolkit(); if (!toolkit) { rv = NS_ERROR_OUT_OF_MEMORY; Index: widget/src/gtk2/nsWindow.cpp =================================================================== --- widget/src/gtk2/nsWindow.cpp.orig +++ widget/src/gtk2/nsWindow.cpp @@ -40,7 +40,7 @@ #include "prlink.h" #include "nsWindow.h" -#include "nsToolkit.h" +#include "nsGTKToolkit.h" #include "nsIRenderingContext.h" #include "nsIRegion.h" #include "nsIRollupListener.h" @@ -58,6 +58,11 @@ #include #include +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +#define SN_API_NOT_YET_FROZEN +#include +#endif + #include "gtk2xtbin.h" #include "nsIPrefService.h" @@ -660,6 +665,70 @@ nsWindow::Enable(PRBool aState) return NS_ERROR_NOT_IMPLEMENTED; } +typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp); + +static void +SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) +{ + nsCOMPtr toolkit; + NS_GetCurrentToolkit(getter_AddRefs(toolkit)); + if (!toolkit) + return; + + nsGTKToolkit* GTKToolkit = NS_STATIC_CAST(nsGTKToolkit*, + NS_STATIC_CAST(nsIToolkit*, toolkit)); + nsCAutoString desktopStartupID; + GTKToolkit->GetDesktopStartupID(&desktopStartupID); + if (desktopStartupID.IsEmpty()) { + PRUint32 timestamp = GTKToolkit->GetFocusTimestamp(); + if (timestamp) { + gdk_window_focus(aWindow->window, timestamp); + GTKToolkit->SetFocusTimestamp(0); + } + return; + } + +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION + GdkDrawable* drawable = GDK_DRAWABLE(aWindow->window); + GtkWindow* win = GTK_WINDOW(aWindow); + if (!win) { + NS_WARNING("Passed in widget was not a GdkWindow!"); + return; + } + GdkScreen* screen = gtk_window_get_screen(win); + SnDisplay* snd = + sn_display_new(gdk_x11_drawable_get_xdisplay(drawable), nsnull, nsnull); + if (!snd) + return; + SnLauncheeContext* ctx = + sn_launchee_context_new(snd, gdk_screen_get_number(screen), + desktopStartupID.get()); + if (!ctx) { + sn_display_unref(snd); + return; + } + + if (sn_launchee_context_get_id_has_timestamp(ctx)) { + PRLibrary* gtkLibrary; + SetUserTimeFunc setUserTimeFunc = (SetUserTimeFunc) + PR_FindFunctionSymbolAndLibrary("gdk_x11_window_set_user_time", >kLibrary); + if (setUserTimeFunc) { + setUserTimeFunc(aWindow->window, sn_launchee_context_get_timestamp(ctx)); + PR_UnloadLibrary(gtkLibrary); + } + } + + sn_launchee_context_setup_window(ctx, gdk_x11_drawable_get_xid(drawable)); + + sn_launchee_context_complete(ctx); + + sn_launchee_context_unref(ctx); + sn_display_unref(snd); +#endif + + GTKToolkit->SetDesktopStartupID(EmptyCString()); +} + NS_IMETHODIMP nsWindow::SetFocus(PRBool aRaise) { @@ -680,6 +749,10 @@ nsWindow::SetFocus(PRBool aRaise) // set properly. GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget); + if (toplevelWidget && aRaise) { + SetUserTimeAndStartupIDForActivatedWindow(toplevelWidget); + } + if (gRaiseWindows && aRaise && toplevelWidget && !GTK_WIDGET_HAS_FOCUS(owningWidget) && !GTK_WIDGET_HAS_FOCUS(toplevelWidget)) { @@ -1167,7 +1240,7 @@ nsWindow::GetNativeData(PRUint32 aDataTy case NS_NATIVE_GRAPHIC: { NS_ASSERTION(nsnull != mToolkit, "NULL toolkit, unable to get a GC"); - return (void *)NS_STATIC_CAST(nsToolkit *, mToolkit)->GetSharedGC(); + return (void *)NS_STATIC_CAST(nsGTKToolkit *, mToolkit)->GetSharedGC(); break; } @@ -2802,7 +2875,7 @@ nsWindow::NativeShow (PRBool aAction) // is shown. // XXX that may or may not be true for GTK+ 2.x if (mTransparencyBitmap) { - ApplyTransparencyBitmap(); + ApplyTransparencyBitmap(); } // unset our flag now that our window has been shown @@ -2812,6 +2885,11 @@ nsWindow::NativeShow (PRBool aAction) moz_drawingarea_set_visibility(mDrawingarea, aAction); gtk_widget_show(GTK_WIDGET(mContainer)); gtk_widget_show(mShell); + + // Set up usertime/startupID metadata for the created window. + if (mWindowType != eWindowType_invisible) { + SetUserTimeAndStartupIDForActivatedWindow(mShell); + } } else if (mContainer) { moz_drawingarea_set_visibility(mDrawingarea, TRUE); Index: widget/src/xremoteclient/XRemoteClient.cpp =================================================================== --- widget/src/xremoteclient/XRemoteClient.cpp.orig +++ widget/src/xremoteclient/XRemoteClient.cpp @@ -173,6 +173,7 @@ XRemoteClient::Shutdown (void) nsresult XRemoteClient::SendCommand (const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aWindowFound) { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand")); @@ -198,7 +199,7 @@ XRemoteClient::SendCommand (const char * if (NS_SUCCEEDED(rv)) { // send our command - rv = DoSendCommand(w, aCommand, aResponse, &destroyed); + rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, &destroyed); // if the window was destroyed, don't bother trying to free the // lock. @@ -217,6 +218,7 @@ nsresult XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, const char *aProfile, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aWindowFound) { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine")); @@ -242,7 +244,7 @@ XRemoteClient::SendCommandLine (const ch if (NS_SUCCEEDED(rv)) { // send our command - rv = DoSendCommandLine(w, argc, argv, aResponse, &destroyed); + rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, &destroyed); // if the window was destroyed, don't bother trying to free the // lock. @@ -643,6 +645,7 @@ XRemoteClient::FreeLock(Window aWindow) nsresult XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed) { *aDestroyed = PR_FALSE; @@ -651,9 +654,28 @@ XRemoteClient::DoSendCommand(Window aWin ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n", aCommand, (unsigned int) aWindow)); + // We add the DESKTOP_STARTUP_ID setting as an extra line of + // the command string. Firefox ignores all lines but the first. + static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID="; + + PRInt32 len = strlen(aCommand); + if (aDesktopStartupID) { + len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); + } + char* buffer = (char*)malloc(len + 1); + if (!buffer) + return NS_ERROR_OUT_OF_MEMORY; + + strcpy(buffer, aCommand); + if (aDesktopStartupID) { + strcat(buffer, desktopStartupPrefix); + strcat(buffer, aDesktopStartupID); + } + XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8, - PropModeReplace, (unsigned char *)aCommand, - strlen(aCommand)); + PropModeReplace, (unsigned char *)buffer, len); + + free(buffer); if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom)) return NS_ERROR_FAILURE; @@ -663,7 +685,7 @@ XRemoteClient::DoSendCommand(Window aWin /* like strcpy, but return the char after the final null */ static char* -estrcpy(char* s, char* d) +estrcpy(const char* s, char* d) { while (*s) *d++ = *s++; @@ -674,6 +696,7 @@ estrcpy(char* s, char* d) nsresult XRemoteClient::DoSendCommandLine(Window aWindow, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed) { int i; @@ -690,9 +713,16 @@ XRemoteClient::DoSendCommandLine(Window // [argc][offsetargv0][offsetargv1...]\0\0argv[1]...\0 // (offset is from the beginning of the buffer) + static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID="; + PRInt32 argvlen = strlen(cwdbuf); - for (i = 0; i < argc; ++i) - argvlen += strlen(argv[i]); + for (i = 0; i < argc; ++i) { + PRInt32 len = strlen(argv[i]); + if (i == 0 && aDesktopStartupID) { + len += strlen(desktopStartupPrefix) + strlen(aDesktopStartupID); + } + argvlen += len; + } PRInt32* buffer = (PRInt32*) malloc(argvlen + argc + 1 + sizeof(PRInt32) * (argc + 1)); @@ -708,6 +738,10 @@ XRemoteClient::DoSendCommandLine(Window for (int i = 0; i < argc; ++i) { buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer)); bufend = estrcpy(argv[i], bufend); + if (i == 0 && aDesktopStartupID) { + bufend = estrcpy(desktopStartupPrefix, bufend - 1); + bufend = estrcpy(aDesktopStartupID, bufend - 1); + } } #ifdef DEBUG_bsmedberg Index: widget/src/xremoteclient/XRemoteClient.h =================================================================== --- widget/src/xremoteclient/XRemoteClient.h.orig +++ widget/src/xremoteclient/XRemoteClient.h @@ -48,10 +48,12 @@ public: virtual nsresult Init(); virtual nsresult SendCommand(const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded); virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername, const char *aProfile, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded); void Shutdown(); @@ -67,10 +69,12 @@ private: PRBool aSupportsCommandLine); nsresult DoSendCommand (Window aWindow, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed); nsresult DoSendCommandLine(Window aWindow, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed); PRBool WaitForResponse (Window aWindow, char **aResponse, Index: widget/src/xremoteclient/mozilla-xremote-client.cpp =================================================================== --- widget/src/xremoteclient/mozilla-xremote-client.cpp.orig +++ widget/src/xremoteclient/mozilla-xremote-client.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef MOZ_WIDGET_PHOTON #include "PhRemoteClient.h" #else @@ -99,7 +100,7 @@ int main(int argc, char **argv) // send the command - it doesn't get any easier than this PRBool success = PR_FALSE; char *error = 0; - rv = client.SendCommand(browser, username, profile, command, + rv = client.SendCommand(browser, username, profile, command, nsnull, &error, &success); // failed to send command Index: widget/src/xremoteclient/nsRemoteClient.h =================================================================== --- widget/src/xremoteclient/nsRemoteClient.h.orig +++ widget/src/xremoteclient/nsRemoteClient.h @@ -85,6 +85,7 @@ public: */ virtual nsresult SendCommand(const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded) = 0; /** @@ -97,6 +98,7 @@ public: virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername, const char *aProfile, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded) = 0; };