Bug 223492. Support the freedesktop startup notification API for Linux/Unix. r=caillon/mats,sr=tor

This commit is contained in:
roc+%cs.cmu.edu 2007-02-09 01:33:26 +00:00
parent f10fee8ca0
commit f5c47d9d22
16 changed files with 502 additions and 43 deletions

View File

@ -229,6 +229,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@

View File

@ -126,6 +126,7 @@ GNOMEVFS_VERSION=2.0
GNOMEUI_VERSION=2.2.0
GCONF_VERSION=1.2.1
LIBGNOME_VERSION=2.0
STARTUP_NOTIFICATION_VERSION=0.8
MSMANIFEST_TOOL=
@ -4773,6 +4774,41 @@ 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)

View File

@ -56,7 +56,8 @@ REQUIRES = \
string \
appcomps \
toolkitcomps \
appcomps \
appshell \
layout \
xulapp \
widget \
gfx \

View File

@ -57,6 +57,8 @@
#include "nsIServiceManager.h"
#include "nsIWeakReference.h"
#include "nsIWidget.h"
#include "nsIAppShellService.h"
#include "nsAppShellCID.h"
#include "nsCOMPtr.h"
#include "nsString.h"
@ -64,6 +66,10 @@
#include "prenv.h"
#include "nsCRT.h"
#ifdef MOZ_WIDGET_GTK2
#include "nsGTKToolkit.h"
#endif
#ifdef MOZ_XUL_APP
#include "nsICommandLineRunner.h"
#include "nsXULAppAPI.h"
@ -154,19 +160,45 @@ nsGTKRemoteService::StartupHandler(const void* aKey,
return PL_DHASH_NEXT;
}
NS_IMETHODIMP
nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow)
static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow)
{
// get the native window for this instance
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(window, nsnull);
nsCOMPtr<nsIBaseWindow> baseWindow
(do_QueryInterface(window->GetDocShell()));
NS_ENSURE_TRUE(baseWindow, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(baseWindow, nsnull);
nsCOMPtr<nsIWidget> mainWidget;
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
return mainWidget;
}
#ifdef MOZ_WIDGET_GTK2
static nsGTKToolkit* GetGTKToolkit()
{
nsCOMPtr<nsIAppShellService> svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
if (!svc)
return nsnull;
nsCOMPtr<nsIDOMWindowInternal> 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
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
@ -258,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;
@ -281,8 +313,60 @@ nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow)
}
#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;
@ -312,6 +396,12 @@ nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow)
#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))
@ -320,6 +410,8 @@ nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow)
if (aWindow)
cmdline->SetWindowContext(aWindow);
SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
rv = cmdline->Run();
if (NS_ERROR_ABORT == rv)
return "500 command not parseable";
@ -331,7 +423,8 @@ nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow)
}
const char*
nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow)
nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
PRUint32 aTimestamp)
{
nsresult rv;
@ -362,6 +455,8 @@ nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow)
if (NS_FAILED(rv))
return "509 internal error";
nsCAutoString desktopStartupID;
char **argv = (char**) malloc(sizeof(char*) * argc);
if (!argv) return "509 internal error";
@ -370,6 +465,12 @@ nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow)
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
@ -384,7 +485,10 @@ nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow)
if (aWindow)
cmdline->SetWindowContext(aWindow);
SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
rv = cmdline->Run();
if (NS_ERROR_ABORT == rv)
return "500 command not parseable";
@ -484,7 +588,7 @@ nsGTKRemoteService::HandlePropertyChange(GtkWidget *aWidget,
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),
@ -529,7 +633,7 @@ nsGTKRemoteService::HandlePropertyChange(GtkWidget *aWidget,
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),

View File

@ -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,

View File

@ -207,6 +207,10 @@ ifdef MOZ_ENABLE_XFT
EXTRA_DSO_LDOPTS += $(FT2_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

View File

@ -68,6 +68,7 @@ REQUIRES = \
profile \
string \
uriloader \
layout \
widget \
windowwatcher \
xpcom \

View File

@ -98,6 +98,12 @@
#include "nsIWindowWatcher.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
#include "nsPIDOMWindow.h"
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
#include "nsIDocShell.h"
#include "nsAppShellCID.h"
#ifdef XP_WIN
#include "nsIWinAppHelper.h"
#endif
@ -265,6 +271,9 @@ static char **gRestartArgv;
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
#include <gtk/gtk.h>
#endif //MOZ_WIDGET_GTK || MOZ_WIDGET_GTK2
#if defined(MOZ_WIDGET_GTK2)
#include "nsGTKToolkit.h"
#endif
#if defined(MOZ_WIDGET_QT)
#include <qapplication.h>
@ -1122,7 +1131,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;
@ -1163,7 +1172,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",
@ -1180,7 +1189,7 @@ HandleRemoteArgument(const char* remote)
}
static PRBool
RemoteCommandLine()
RemoteCommandLine(const char* aDesktopStartupID)
{
nsresult rv;
ArgResult ar;
@ -1212,7 +1221,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)
@ -2114,6 +2123,52 @@ public:
#ifdef MOZ_WIDGET_GTK2
#include "prlink.h"
typedef void (*_g_set_application_name_fn)(const gchar *application_name);
typedef void (*_gtk_window_set_auto_startup_notification_fn)(gboolean setting);
static PRFuncPtr FindFunction(const char* aName)
{
PRLibrary *lib = nsnull;
PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(aName, &lib);
// Since the library was already loaded, we can safely unload it here.
if (lib) {
PR_UnloadLibrary(lib);
}
return result;
}
static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow)
{
// get the native window for this instance
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(window, nsnull);
nsCOMPtr<nsIBaseWindow> baseWindow
(do_QueryInterface(window->GetDocShell()));
NS_ENSURE_TRUE(baseWindow, nsnull);
nsCOMPtr<nsIWidget> mainWidget;
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
return mainWidget;
}
static nsGTKToolkit* GetGTKToolkit()
{
nsCOMPtr<nsIAppShellService> svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
if (!svc)
return nsnull;
nsCOMPtr<nsIDOMWindowInternal> 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
/**
@ -2359,6 +2414,16 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
return 0;
}
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) || defined(MOZ_ENABLE_XREMOTE)
// Stash DESKTOP_STARTUP_ID in malloc'ed memory because gtk_init will clear it.
#define HAVE_DESKTOP_STARTUP_ID
const char* desktopStartupIDEnv = PR_GetEnv("DESKTOP_STARTUP_ID");
nsCAutoString desktopStartupID;
if (desktopStartupIDEnv) {
desktopStartupID.Assign(desktopStartupIDEnv);
}
#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
@ -2375,14 +2440,15 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
#if defined(MOZ_WIDGET_GTK2)
// g_set_application_name () is only defined in glib2.2 and higher.
PRLibrary *glib2 = nsnull;
_g_set_application_name_fn _g_set_application_name =
(_g_set_application_name_fn)PR_FindFunctionSymbolAndLibrary("g_set_application_name", &glib2);
(_g_set_application_name_fn)FindFunction("g_set_application_name");
if (_g_set_application_name) {
_g_set_application_name(gAppData->name);
}
if (glib2) {
PR_UnloadLibrary(glib2);
_gtk_window_set_auto_startup_notification_fn _gtk_window_set_auto_startup_notification =
(_gtk_window_set_auto_startup_notification_fn)FindFunction("gtk_window_set_auto_startup_notification");
if (_gtk_window_set_auto_startup_notification) {
_gtk_window_set_auto_startup_notification(PR_FALSE);
}
#endif
@ -2451,13 +2517,15 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n");
return 1;
}
const char* desktopStartupIDPtr =
desktopStartupID.IsEmpty() ? nsnull : desktopStartupID.get();
if (ar) {
return HandleRemoteArgument(xremotearg);
return HandleRemoteArgument(xremotearg, desktopStartupIDPtr);
}
if (!PR_GetEnv("MOZ_NO_REMOTE")) {
// Try to remote the entire command line. If this fails, start up normally.
if (RemoteCommandLine())
if (RemoteCommandLine(desktopStartupIDPtr))
return 0;
}
#endif
@ -2677,6 +2745,13 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
NS_TIMELINE_LEAVE("appStartup->CreateHiddenWindow");
NS_ENSURE_SUCCESS(rv, 1);
#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2)
nsRefPtr<nsGTKToolkit> toolkit = GetGTKToolkit();
if (toolkit && !desktopStartupID.IsEmpty()) {
toolkit->SetDesktopStartupID(desktopStartupID);
}
#endif
// Extension Compatibility Checking and Startup
if (gAppData->flags & NS_XRE_ENABLE_EXTENSION_MANAGER) {
nsCOMPtr<nsIExtensionManager> em(do_GetService("@mozilla.org/extensions/manager;1"));
@ -2833,6 +2908,21 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
}
#endif
#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_TOOLKIT_GTK2)
nsGTKToolkit* toolkit = GetGTKToolkit();
if (toolkit) {
nsCAutoString currentDesktopStartupID;
toolkit->GetDesktopStartupID(&currentDesktopStartupID);
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;
}

View File

@ -127,6 +127,7 @@ EXTRA_DSO_LDOPTS += \
$(MOZ_COMPONENT_LIBS) \
-lgkgfx \
-lgtkxtbin \
$(MOZ_STARTUP_NOTIFICATION_LIBS) \
$(XLDFLAGS) \
$(XLIBS) \
$(MOZ_GTK2_LIBS)
@ -140,6 +141,7 @@ endif
EXPORTS = \
nsIGdkPixbufImage.h \
nsGTKToolkit.h \
nsIImageToPixbuf.h \
mozdrawingarea.h \
mozcontainer.h \
@ -153,8 +155,8 @@ endif
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

View File

@ -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 <gtk/gtk.h>
/**
* 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

View File

@ -38,7 +38,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nscore.h" // needed for 'nsnull'
#include "nsToolkit.h"
#include "nsGTKToolkit.h"
#include "nsWidgetAtoms.h"
//
@ -52,9 +52,10 @@ static PRUintn gToolkitTLSIndex = 0;
// constructor
//
//-------------------------------------------------------------------------
nsToolkit::nsToolkit()
nsGTKToolkit::nsGTKToolkit()
{
mSharedGC = nsnull;
mFocusTimestamp = 0;
}
//-------------------------------------------------------------------------
@ -62,7 +63,7 @@ nsToolkit::nsToolkit()
// destructor
//
//-------------------------------------------------------------------------
nsToolkit::~nsToolkit()
nsGTKToolkit::~nsGTKToolkit()
{
if (mSharedGC) {
gdk_gc_unref(mSharedGC);
@ -78,9 +79,9 @@ nsToolkit::~nsToolkit()
//
//-------------------------------------------------------------------------
NS_IMPL_ISUPPORTS1(nsToolkit, nsIToolkit)
NS_IMPL_ISUPPORTS1(nsGTKToolkit, nsIToolkit)
void nsToolkit::CreateSharedGC(void)
void nsGTKToolkit::CreateSharedGC(void)
{
GdkPixmap *pixmap;
@ -92,7 +93,7 @@ void nsToolkit::CreateSharedGC(void)
gdk_pixmap_unref(pixmap);
}
GdkGC *nsToolkit::GetSharedGC(void)
GdkGC *nsGTKToolkit::GetSharedGC(void)
{
return gdk_gc_ref(mSharedGC);
}
@ -101,7 +102,7 @@ GdkGC *nsToolkit::GetSharedGC(void)
//
//
//-------------------------------------------------------------------------
NS_IMETHODIMP nsToolkit::Init(PRThread *aThread)
NS_IMETHODIMP nsGTKToolkit::Init(PRThread *aThread)
{
CreateSharedGC();
@ -138,7 +139,7 @@ NS_METHOD NS_GetCurrentToolkit(nsIToolkit* *aResult)
// Create a new toolkit for this thread...
//
if (!toolkit) {
toolkit = new nsToolkit();
toolkit = new nsGTKToolkit();
if (!toolkit) {
rv = NS_ERROR_OUT_OF_MEMORY;

View File

@ -41,7 +41,7 @@
#include "prlink.h"
#include "nsWindow.h"
#include "nsToolkit.h"
#include "nsGTKToolkit.h"
#include "nsIDeviceContext.h"
#include "nsIRenderingContext.h"
#include "nsIRegion.h"
@ -60,6 +60,11 @@
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION
#define SN_API_NOT_YET_FROZEN
#include <startup-notification-1.0/libsn/sn.h>
#endif
#include "gtk2xtbin.h"
#include "nsIPrefService.h"
@ -697,6 +702,75 @@ nsWindow::Enable(PRBool aState)
return NS_ERROR_NOT_IMPLEMENTED;
}
typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp);
// This will become obsolete when new GTK APIs are widely supported,
// as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375
static void
SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow)
{
nsCOMPtr<nsIToolkit> 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()) {
// We don't have the data we need. Fall back to an
// approximation ... using the timestamp of the remote command
// being received as a guess for the timestamp of the user event
// that triggered it.
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", &gtkLibrary);
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)
{
@ -1207,7 +1281,7 @@ nsWindow::GetNativeData(PRUint32 aDataType)
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;
}
@ -3183,13 +3257,18 @@ 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
mNeedsShow = PR_FALSE;
if (mIsTopLevel) {
// Set up usertime/startupID metadata for the created window.
if (mWindowType != eWindowType_invisible) {
SetUserTimeAndStartupIDForActivatedWindow(mShell);
}
moz_drawingarea_set_visibility(mDrawingarea, aAction);
gtk_widget_show(GTK_WIDGET(mContainer));
gtk_widget_show(mShell);

View File

@ -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 *aProgram, const char *aUsername,
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 char *aProgram, const char *aUsername,
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 aWindow, const char *aCommand,
("(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 aWindow, const char *aCommand,
/* 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 aWindow, PRInt32 argc, char **argv,
// [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[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 += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
}
argvlen += len;
}
PRInt32* buffer = (PRInt32*) malloc(argvlen + argc + 1 +
sizeof(PRInt32) * (argc + 1));
@ -708,6 +738,10 @@ XRemoteClient::DoSendCommandLine(Window aWindow, PRInt32 argc, char **argv,
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

View File

@ -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,

View File

@ -99,7 +99,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

View File

@ -76,6 +76,10 @@ public:
* @param aCommand This is the command that is passed to the server.
* Please see the additional information located at:
* http://www.mozilla.org/unix/remote.html
*
* @param aDesktopStartupID the contents of the DESKTOP_STARTUP_ID environment
* variable defined by the Startup Notification specification
* http://standards.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt
*
* @param aResponse If there is a response, it will be here. This
* includes error messages. The string is allocated using stdlib
@ -85,11 +89,16 @@ public:
*/
virtual nsresult SendCommand(const char *aProgram, const char *aUsername,
const char *aProfile, const char *aCommand,
const char* aDesktopStartupID,
char **aResponse, PRBool *aSucceeded) = 0;
/**
* Send a complete command line to a running instance.
*
* @param aDesktopStartupID the contents of the DESKTOP_STARTUP_ID environment
* variable defined by the Startup Notification specification
* http://standards.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt
*
* @see sendCommand
* @param argc The number of command-line arguments.
*
@ -97,6 +106,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;
};