1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Unix Native App Support.
17 * The Initial Developer of the Original Code is
18 * Mozilla Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2007
20 * the Initial Developer. All Rights Reserved.
23 * Michael Wu <flamingice@sourmilk.net> (original author)
24 * Michael Ventnor <m.ventnor@gmail.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsNativeAppSupportBase.h"
43 #include "nsISupportsPrimitives.h"
44 #include "nsIObserverService.h"
45 #include "nsIAppStartup.h"
46 #include "nsServiceManagerUtils.h"
48 #include "nsXREDirProvider.h"
49 #include "nsReadableUtils.h"
52 #include "nsDirectoryServiceDefs.h"
53 #include "nsICommandLineRunner.h"
54 #include "nsIWindowMediator.h"
55 #include "nsIDOMWindowInternal.h"
56 #include "nsPIDOMWindow.h"
57 #include "nsIDocShell.h"
58 #include "nsIBaseWindow.h"
59 #include "nsIWidget.h"
60 #include "nsIWritablePropertyBag2.h"
61 #include "nsIPrefService.h"
62 #include "mozilla/Services.h"
66 #include <glib-object.h>
71 #include <X11/Xatom.h>
74 #ifdef MOZ_PLATFORM_MAEMO
75 struct DBusMessage
; /* libosso.h references internals of dbus */
77 #include <dbus/dbus.h>
78 #include <dbus/dbus-protocol.h>
81 // These come from <mce/dbus-names.h> (maemo sdk 5+)
82 #define MCE_SERVICE "com.nokia.mce"
83 #define MCE_REQUEST_IF "com.nokia.mce.request"
84 #define MCE_REQUEST_PATH "/com/nokia/mce/request"
85 #define MCE_SIGNAL_IF "com.nokia.mce.signal"
86 #define MCE_DEVICE_ORIENTATION_SIG "sig_device_orientation_ind"
87 #define MCE_MATCH_RULE "type='signal',interface='" MCE_SIGNAL_IF "',member='" MCE_DEVICE_ORIENTATION_SIG "'"
90 #define MIN_GTK_MAJOR_VERSION 2
91 #define MIN_GTK_MINOR_VERSION 10
92 #define UNSUPPORTED_GTK_MSG "We're sorry, this application requires a version of the GTK+ library that is not installed on your computer.\n\n\
93 You have GTK+ %d.%d.\nThis application requires GTK+ %d.%d or newer.\n\n\
94 Please upgrade your GTK+ library if you wish to use this application."
96 typedef struct _GnomeProgram GnomeProgram
;
97 typedef struct _GnomeModuleInfo GnomeModuleInfo
;
98 typedef struct _GnomeClient GnomeClient
;
108 GNOME_INTERACT_ERRORS
,
110 } GnomeInteractStyle
;
117 typedef GnomeProgram
* (*_gnome_program_init_fn
)(const char *, const char *,
118 const GnomeModuleInfo
*, int,
119 char **, const char *, ...);
120 typedef GnomeProgram
* (*_gnome_program_get_fn
)(void);
121 typedef const GnomeModuleInfo
* (*_libgnomeui_module_info_get_fn
)();
122 typedef GnomeClient
* (*_gnome_master_client_fn
)(void);
123 typedef void (*_gnome_client_set_restart_command_fn
)(GnomeClient
*, gint
, gchar
*[]);
125 static _gnome_client_set_restart_command_fn gnome_client_set_restart_command
;
127 gboolean
save_yourself_cb(GnomeClient
*client
, gint phase
,
128 GnomeSaveStyle style
, gboolean shutdown
,
129 GnomeInteractStyle interact
, gboolean fast
,
132 nsCOMPtr
<nsIObserverService
> obsServ
=
133 mozilla::services::GetObserverService();
135 nsCOMPtr
<nsISupportsPRBool
> didSaveSession
=
136 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
);
138 if (!obsServ
|| !didSaveSession
)
141 // Notify observers to save the session state
142 didSaveSession
->SetData(PR_FALSE
);
143 obsServ
->NotifyObservers(didSaveSession
, "session-save", nsnull
);
146 didSaveSession
->GetData(&status
);
148 // If there was no session saved and the save_yourself request is
149 // caused by upcoming shutdown we like to prepare for it
150 if (!status
&& shutdown
) {
151 nsCOMPtr
<nsISupportsPRBool
> cancelQuit
=
152 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
);
154 cancelQuit
->SetData(PR_FALSE
);
155 obsServ
->NotifyObservers(cancelQuit
, "quit-application-requested", nsnull
);
158 cancelQuit
->GetData(&abortQuit
);
164 void die_cb(GnomeClient
*client
, gpointer user_data
)
166 nsCOMPtr
<nsIAppStartup
> appService
=
167 do_GetService("@mozilla.org/toolkit/app-startup;1");
170 appService
->Quit(nsIAppStartup::eForceQuit
);
173 class nsNativeAppSupportUnix
: public nsNativeAppSupportBase
176 NS_IMETHOD
Start(PRBool
* aRetVal
);
177 NS_IMETHOD
Stop(PRBool
*aResult
);
181 #ifdef MOZ_PLATFORM_MAEMO
182 osso_context_t
*m_osso_context
;
183 /* A note about why we need to have m_hw_state:
184 the osso hardware callback does not tell us what changed, just
185 that something has changed. We need to keep track of state so
186 that we can determine what has changed.
188 osso_hw_state_t m_hw_state
;
192 #ifdef MOZ_PLATFORM_MAEMO
194 GetMostRecentWindow(const PRUnichar
* aType
, nsIDOMWindowInternal
** aWindow
)
196 nsCOMPtr
<nsIWindowMediator
> wm
= do_GetService("@mozilla.org/appshell/window-mediator;1");
198 return wm
->GetMostRecentWindow(aType
, aWindow
);
199 return NS_ERROR_FAILURE
;
203 WidgetForDOMWindow(nsISupports
*aWindow
)
205 nsCOMPtr
<nsPIDOMWindow
> domWindow(do_QueryInterface(aWindow
));
209 nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(domWindow
->GetDocShell());
213 nsCOMPtr
<nsIWidget
> widget
;
214 baseWindow
->GetMainWidget(getter_AddRefs(widget
));
218 return (GtkWidget
*)(widget
->GetNativeData(NS_NATIVE_SHELLWIDGET
));
222 OssoSetWindowOrientation(PRBool aPortrait
)
224 // If we locked the screen, ignore any orientation changes
225 PRBool lockScreen
= PR_FALSE
;
226 nsCOMPtr
<nsIPrefBranch
> prefs
= do_GetService(NS_PREFSERVICE_CONTRACTID
);
228 prefs
->GetBoolPref("toolkit.screen.lock", &lockScreen
);
233 // Tell Hildon desktop to force our window to be either portrait or landscape,
234 // depending on the current rotation
235 // NOTE: We only update the most recent top-level window so this is only
236 // suitable for apps with only one window.
237 nsCOMPtr
<nsIDOMWindowInternal
> window
;
238 GetMostRecentWindow(EmptyString().get(), getter_AddRefs(window
));
239 GtkWidget
* widget
= WidgetForDOMWindow(window
);
240 if (widget
&& widget
->window
) {
241 GdkWindow
*gdk
= widget
->window
;
242 GdkAtom request
= gdk_atom_intern("_HILDON_PORTRAIT_MODE_REQUEST", FALSE
);
245 gulong portrait_set
= 1;
246 gdk_property_change(gdk
, request
, gdk_x11_xatom_to_atom(XA_CARDINAL
),
247 32, GDK_PROP_MODE_REPLACE
, (const guchar
*) &portrait_set
, 1);
250 gdk_property_delete(gdk
, request
);
254 // Update the system info property
255 nsCOMPtr
<nsIWritablePropertyBag2
> info
= do_GetService("@mozilla.org/system-info;1");
257 info
->SetPropertyAsAString(NS_LITERAL_STRING("screen-orientation"),
258 aPortrait
? NS_LITERAL_STRING("portrait") : NS_LITERAL_STRING("landscape"));
262 static PRBool
OssoIsScreenOn(osso_context_t
* ctx
)
266 PRBool result
= PR_FALSE
;
268 rv
= osso_rpc_run_system(ctx
, MCE_SERVICE
, MCE_REQUEST_PATH
, MCE_REQUEST_IF
,
269 "get_display_status", &ret
, DBUS_TYPE_INVALID
);
271 if (strcmp(ret
.value
.s
, "on") == 0)
274 osso_rpc_free_val(&ret
);
279 static void OssoRequestAccelerometer(osso_context_t
*ctx
, PRBool aEnabled
)
284 rv
= osso_rpc_run_system(ctx
,
286 MCE_REQUEST_PATH
, MCE_REQUEST_IF
,
287 aEnabled
? "req_accelerometer_enable" : "req_accelerometer_disable",
288 aEnabled
? &ret
: NULL
,
291 // Orientation might changed while the accelerometer was off, so let's update
292 // the window's orientation
293 if (rv
== OSSO_OK
&& aEnabled
) {
294 OssoSetWindowOrientation(strcmp(ret
.value
.s
, "portrait") == 0);
295 osso_rpc_free_val(&ret
);
299 static void OssoDisplayCallback(osso_display_state_t state
, gpointer data
)
301 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
305 osso_context_t
* context
= (osso_context_t
*) data
;
307 if (state
== OSSO_DISPLAY_ON
) {
308 os
->NotifyObservers(nsnull
, "system-display-on", nsnull
);
309 OssoRequestAccelerometer(context
, PR_TRUE
);
311 os
->NotifyObservers(nsnull
, "system-display-dimmed-or-off", nsnull
);
312 OssoRequestAccelerometer(context
, PR_FALSE
);
316 static void OssoHardwareCallback(osso_hw_state_t
*state
, gpointer data
)
318 NS_ASSERTION(state
, "osso_hw_state_t must not be null.");
319 NS_ASSERTION(data
, "data must not be null.");
321 osso_hw_state_t
* ourState
= (osso_hw_state_t
*) data
;
323 if (state
->shutdown_ind
) {
324 nsCOMPtr
<nsIAppStartup
> appService
= do_GetService("@mozilla.org/toolkit/app-startup;1");
326 appService
->Quit(nsIAppStartup::eForceQuit
);
330 if (state
->memory_low_ind
&& !ourState
->memory_low_ind
) {
331 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
333 os
->NotifyObservers(nsnull
, "memory-pressure", NS_LITERAL_STRING("low-memory").get());
336 if (state
->system_inactivity_ind
!= ourState
->system_inactivity_ind
) {
337 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
341 if (state
->system_inactivity_ind
)
342 os
->NotifyObservers(nsnull
, "system-idle", nsnull
);
344 os
->NotifyObservers(nsnull
, "system-active", nsnull
);
347 memcpy(ourState
, state
, sizeof(osso_hw_state_t
));
351 OssoDbusCallback(const gchar
*interface
, const gchar
*method
,
352 GArray
*arguments
, gpointer data
, osso_rpc_t
*retval
)
354 retval
->type
= DBUS_TYPE_INVALID
;
356 // The "top_application" method just wants us to focus the top-most window.
357 if (!strcmp("top_application", method
)) {
358 nsCOMPtr
<nsIDOMWindowInternal
> window
;
359 GetMostRecentWindow(NS_LITERAL_STRING("").get(), getter_AddRefs(window
));
366 if (!strcmp("quit", method
)) {
367 nsCOMPtr
<nsIAppStartup
> appService
= do_GetService("@mozilla.org/toolkit/app-startup;1");
369 appService
->Quit(nsIAppStartup::eForceQuit
);
374 // Other methods can have arguments, which we convert and send to commandline
376 nsCOMPtr
<nsICommandLineRunner
> cmdLine
377 (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
379 nsCOMPtr
<nsIFile
> workingDir
;
380 NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR
,
381 getter_AddRefs(workingDir
));
386 // Not all DBus methods pass arguments
387 if (arguments
&& arguments
->len
> 0) {
388 // Create argument list with a dummy argv[0]
389 argc
= arguments
->len
+ 1;
390 argv
= (char**)calloc(1, argc
* sizeof(*argv
));
392 // Start at 1 to skip the dummy argv[0]
393 for (int i
= 1; i
< argc
; i
++) {
394 osso_rpc_t
* entry
= (osso_rpc_t
*)&g_array_index(arguments
, osso_rpc_t
, i
- 1);
395 if (entry
->type
!= DBUS_TYPE_STRING
)
398 argv
[i
] = strdup(entry
->value
.s
);
402 cmdLine
->Init(argc
, argv
, workingDir
, nsICommandLine::STATE_REMOTE_AUTO
);
404 // Cleanup argument list
415 static DBusHandlerResult
416 OssoModeControlCallback(DBusConnection
*con
, DBusMessage
*msg
, gpointer data
)
418 if (dbus_message_is_signal(msg
, MCE_SIGNAL_IF
, MCE_DEVICE_ORIENTATION_SIG
)) {
419 DBusMessageIter iter
;
420 if (dbus_message_iter_init(msg
, &iter
)) {
421 const gchar
*mode
= NULL
;
422 dbus_message_iter_get_basic(&iter
, &mode
);
424 OssoSetWindowOrientation(strcmp(mode
, "portrait") == 0);
427 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
433 nsNativeAppSupportUnix::Start(PRBool
*aRetVal
)
435 NS_ASSERTION(gAppData
, "gAppData must not be null.");
437 if (gtk_major_version
< MIN_GTK_MAJOR_VERSION
||
438 (gtk_major_version
== MIN_GTK_MAJOR_VERSION
&& gtk_minor_version
< MIN_GTK_MINOR_VERSION
)) {
439 GtkWidget
* versionErrDialog
= gtk_message_dialog_new(NULL
,
440 GtkDialogFlags(GTK_DIALOG_MODAL
|
441 GTK_DIALOG_DESTROY_WITH_PARENT
),
447 MIN_GTK_MAJOR_VERSION
,
448 MIN_GTK_MINOR_VERSION
);
449 gtk_dialog_run(GTK_DIALOG(versionErrDialog
));
450 gtk_widget_destroy(versionErrDialog
);
454 #ifdef MOZ_PLATFORM_MAEMO
455 /* zero state out. */
456 memset(&m_hw_state
, 0, sizeof(osso_hw_state_t
));
458 /* Initialize maemo application
460 The initalization name will be of the form "Vendor.Name".
461 If a Vendor isn't given, then we will just use "Name".
463 Note that this value must match your X-Osso-Service name
464 defined in your desktop file. If it doesn't, the OSSO
465 system will happily kill your process.
467 nsCAutoString applicationName
;
468 if (gAppData
->vendor
) {
469 applicationName
.Append(gAppData
->vendor
);
470 applicationName
.Append(".");
472 applicationName
.Append(gAppData
->name
);
473 ToLowerCase(applicationName
);
475 m_osso_context
= osso_initialize(applicationName
.get(),
476 gAppData
->version
? gAppData
->version
: "1.0",
480 /* Check that initilialization was ok */
481 if (m_osso_context
== nsnull
) {
482 return NS_ERROR_FAILURE
;
485 osso_hw_set_event_cb(m_osso_context
, nsnull
, OssoHardwareCallback
, &m_hw_state
);
486 osso_hw_set_display_event_cb(m_osso_context
, OssoDisplayCallback
, m_osso_context
);
487 osso_rpc_set_default_cb_f(m_osso_context
, OssoDbusCallback
, nsnull
);
489 // Setup an MCE callback to monitor orientation
490 DBusConnection
*connnection
= (DBusConnection
*)osso_get_sys_dbus_connection(m_osso_context
);
491 dbus_bus_add_match(connnection
, MCE_MATCH_RULE
, nsnull
);
492 dbus_connection_add_filter(connnection
, OssoModeControlCallback
, nsnull
, nsnull
);
499 PRLibrary
*gnomeuiLib
= PR_LoadLibrary("libgnomeui-2.so.0");
503 PRLibrary
*gnomeLib
= PR_LoadLibrary("libgnome-2.so.0");
505 PR_UnloadLibrary(gnomeuiLib
);
509 _gnome_program_init_fn gnome_program_init
=
510 (_gnome_program_init_fn
)PR_FindFunctionSymbol(gnomeLib
, "gnome_program_init");
511 _gnome_program_get_fn gnome_program_get
=
512 (_gnome_program_get_fn
)PR_FindFunctionSymbol(gnomeLib
, "gnome_program_get");
513 _libgnomeui_module_info_get_fn libgnomeui_module_info_get
= (_libgnomeui_module_info_get_fn
)PR_FindFunctionSymbol(gnomeuiLib
, "libgnomeui_module_info_get");
514 if (!gnome_program_init
|| !gnome_program_get
|| !libgnomeui_module_info_get
) {
515 PR_UnloadLibrary(gnomeuiLib
);
516 PR_UnloadLibrary(gnomeLib
);
523 // We will load gail, atk-bridge by ourself later
524 // We can't run atk-bridge init here, because gail get the control
525 // Set GNOME_ACCESSIBILITY to 0 can avoid this
526 static const char *accEnv
= "GNOME_ACCESSIBILITY";
527 const char *accOldValue
= getenv(accEnv
);
528 setenv(accEnv
, "0", 1);
532 if (!gnome_program_get()) {
533 gnome_program_init("Gecko", "1.0", libgnomeui_module_info_get(), gArgc
, gArgv
, NULL
);
539 setenv(accEnv
, accOldValue
, 1);
545 // Careful! These libraries cannot be unloaded after this point because
546 // gnome_program_init causes atexit handlers to be registered. Strange
547 // crashes will occur if these libraries are unloaded.
550 gnome_client_set_restart_command
= (_gnome_client_set_restart_command_fn
)
551 PR_FindFunctionSymbol(gnomeuiLib
, "gnome_client_set_restart_command");
553 _gnome_master_client_fn gnome_master_client
= (_gnome_master_client_fn
)
554 PR_FindFunctionSymbol(gnomeuiLib
, "gnome_master_client");
556 GnomeClient
*client
= gnome_master_client();
557 g_signal_connect(client
, "save-yourself", G_CALLBACK(save_yourself_cb
), NULL
);
558 g_signal_connect(client
, "die", G_CALLBACK(die_cb
), NULL
);
560 // Set the correct/requested restart command in any case.
562 // Is there a request to suppress default binary launcher?
564 char* argv1
= getenv("MOZ_APP_LAUNCHER");
567 // Tell the desktop the command for restarting us so that we can be part of XSMP session restore
568 NS_ASSERTION(gDirServiceProvider
, "gDirServiceProvider is NULL! This shouldn't happen!");
569 nsCOMPtr
<nsIFile
> executablePath
;
573 rv
= gDirServiceProvider
->GetFile(XRE_EXECUTABLE_FILE
, &dummy
, getter_AddRefs(executablePath
));
575 if (NS_SUCCEEDED(rv
)) {
576 // Strip off the -bin suffix to get the shell script we should run; this is what Breakpad does
577 nsCAutoString leafName
;
578 rv
= executablePath
->GetNativeLeafName(leafName
);
579 if (NS_SUCCEEDED(rv
) && StringEndsWith(leafName
, NS_LITERAL_CSTRING("-bin"))) {
580 leafName
.SetLength(leafName
.Length() - strlen("-bin"));
581 executablePath
->SetNativeLeafName(leafName
);
584 executablePath
->GetNativePath(path
);
585 argv1
= (char*)(path
.get());
590 gnome_client_set_restart_command(client
, 1, &argv1
);
598 nsNativeAppSupportUnix::Stop(PRBool
*aResult
)
600 NS_ENSURE_ARG(aResult
);
603 #ifdef MOZ_PLATFORM_MAEMO
604 if (m_osso_context
) {
605 // Disable the accelerometer when closing
606 OssoRequestAccelerometer(m_osso_context
, PR_FALSE
);
608 // Remove the MCE callback filter
609 DBusConnection
*connnection
= (DBusConnection
*)osso_get_sys_dbus_connection(m_osso_context
);
610 dbus_connection_remove_filter(connnection
, OssoModeControlCallback
, nsnull
);
612 osso_hw_unset_event_cb(m_osso_context
, nsnull
);
613 osso_rpc_unset_default_cb_f(m_osso_context
, OssoDbusCallback
, nsnull
);
614 osso_deinitialize(m_osso_context
);
615 m_osso_context
= nsnull
;
622 nsNativeAppSupportUnix::Enable()
624 #ifdef MOZ_PLATFORM_MAEMO
625 // Enable the accelerometer for orientation support
626 if (OssoIsScreenOn(m_osso_context
))
627 OssoRequestAccelerometer(m_osso_context
, PR_TRUE
);
633 NS_CreateNativeAppSupport(nsINativeAppSupport
**aResult
)
635 nsNativeAppSupportBase
* native
= new nsNativeAppSupportUnix();
637 return NS_ERROR_OUT_OF_MEMORY
;