diff options
author | Christian Persch <chpe@cvs.gnome.org> | 2006-11-15 17:21:16 +0000 |
---|---|---|
committer | Christian Persch <chpe@src.gnome.org> | 2006-11-15 17:21:16 +0000 |
commit | 08504d7f6857b23ea596e08b8afc8944fbf31850 (patch) | |
tree | 6180a5d8f5b21458e75fbab4b91eeffd8a1748e6 | |
parent | 9a634aa3ae8af039131a0e816ae25419e49a23fe (diff) | |
download | totem-08504d7f6857b23ea596e08b8afc8944fbf31850.tar.gz |
A browser-plugin/org_gnome_totem_PluginViewer.xml: A
2006-11-15 Christian Persch <chpe@cvs.gnome.org>
* Makefile.am:
* browser-plugin/Makefile.am:
* browser-plugin/README:
* browser-plugin/TODO:
A browser-plugin/org_gnome_totem_PluginViewer.xml:
A browser-plugin/totem-plugin-viewer-commands.h:
A browser-plugin/totem-plugin-viewer-options.h:
A browser-plugin/totem-plugin-viewer.c:
* browser-plugin/totemBasicPlugin.cpp:
* browser-plugin/totemBasicPlugin.h:
* browser-plugin/totemComplexPlugin.cpp:
* browser-plugin/totemComplexPlugin.h:
* browser-plugin/totemGMPPlugin.cpp:
* browser-plugin/totemGMPPlugin.h:
* browser-plugin/totemMullYPlugin.cpp:
* browser-plugin/totemMullYPlugin.h:
* browser-plugin/totemNarrowSpacePlugin.cpp:
* browser-plugin/totemNarrowSpacePlugin.h:
* browser-plugin/totemPlugin.cpp:
* browser-plugin/totemPlugin.h:
* browser-plugin/totemPluginGlue.cpp:
* configure.in:
* data/mozilla-viewer.glade:
* po/POTFILES.in:
* src/Makefile.am:
* src/backend/bvw-test.c: (on_redirect), (main):
* src/plparse/totem-pl-parser.c:
(totem_pl_parser_can_parse_from_data):
R src/totem-mozilla-interface.xml:
R src/totem-mozilla-options.h:
R src/totem-mozilla-viewer.c:
Make plugin / viewer interaction completely async. Bug #350297.
Move plugin viewer from src/ to browser-plugin/.
30 files changed, 3968 insertions, 1902 deletions
@@ -1,3 +1,40 @@ +2006-11-15 Christian Persch <chpe@cvs.gnome.org> + + * Makefile.am: + * browser-plugin/Makefile.am: + * browser-plugin/README: + * browser-plugin/TODO: + A browser-plugin/org_gnome_totem_PluginViewer.xml: + A browser-plugin/totem-plugin-viewer-commands.h: + A browser-plugin/totem-plugin-viewer-options.h: + A browser-plugin/totem-plugin-viewer.c: + * browser-plugin/totemBasicPlugin.cpp: + * browser-plugin/totemBasicPlugin.h: + * browser-plugin/totemComplexPlugin.cpp: + * browser-plugin/totemComplexPlugin.h: + * browser-plugin/totemGMPPlugin.cpp: + * browser-plugin/totemGMPPlugin.h: + * browser-plugin/totemMullYPlugin.cpp: + * browser-plugin/totemMullYPlugin.h: + * browser-plugin/totemNarrowSpacePlugin.cpp: + * browser-plugin/totemNarrowSpacePlugin.h: + * browser-plugin/totemPlugin.cpp: + * browser-plugin/totemPlugin.h: + * browser-plugin/totemPluginGlue.cpp: + * configure.in: + * data/mozilla-viewer.glade: + * po/POTFILES.in: + * src/Makefile.am: + * src/backend/bvw-test.c: (on_redirect), (main): + * src/plparse/totem-pl-parser.c: + (totem_pl_parser_can_parse_from_data): + R src/totem-mozilla-interface.xml: + R src/totem-mozilla-options.h: + R src/totem-mozilla-viewer.c: + + Make plugin / viewer interaction completely async. Bug #350297. + Move plugin viewer from src/ to browser-plugin/. + ============ Version 2.17.3 2006-11-15 Bastien Nocera <hadess@hadess.net> diff --git a/Makefile.am b/Makefile.am index 331ab17b1..ba1bef77b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,8 @@ -SUBDIRS = po data src browser-plugin help +SUBDIRS = po data src help -install-data-local: - @$(NORMAL_INSTALL) +if ENABLE_BROWSER_PLUGINS +SUBDIRS += browser-plugin +endif EXTRA_DIST = \ COPYING.LIB \ @@ -25,4 +26,4 @@ DISTCLEANFILES = intltool-extract intltool-merge intltool-update \ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = totem-plparser.pc -DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper
\ No newline at end of file +DISTCHECK_CONFIGURE_FLAGS = --disable-scrollkeeper diff --git a/browser-plugin/Makefile.am b/browser-plugin/Makefile.am index 5b2fd578d..1dd0a5dc4 100644 --- a/browser-plugin/Makefile.am +++ b/browser-plugin/Makefile.am @@ -3,6 +3,8 @@ SUBDIRS = idl plugindir = $(libdir)/mozilla/plugins plugin_LTLIBRARIES = +BUILT_SOURCES = + EXTRA_DIST = \ plugin.symbols \ TODO @@ -11,7 +13,65 @@ gecko_include_subdirs = \ . \ docshell \ dom \ - necko + necko \ + xpcom + +# Totem Plugin Viewer + +libexec_PROGRAMS = totem-plugin-viewer + +totem_plugin_viewer_SOURCES = \ + totem-plugin-viewer.c \ + totem-plugin-viewer-interface.h \ + totem-plugin-viewer-commands.h \ + totem-plugin-viewer-options.h + +totem_plugin_viewer_CPPFLAGS = \ + -D_REENTRANT \ + -DDBUS_API_SUBJECT_TO_CHANGE \ + -I$(top_srcdir) \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/plparse \ + -I$(top_srcdir)/src/backend \ + -I$(top_builddir)/src \ + -I$(top_builddir)/src/plparse \ + -I$(top_builddir)/src/backend \ + -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ + -DGCONF_PREFIX="\"/apps/totem\"" \ + -DDATADIR="\"$(datadir)\"" \ + -DLIBEXECDIR="\"$(libexecdir)\"" \ + -DBINDIR="\"$(bindir)\"" \ + -DSHAREDIR="\"$(pkgdatadir)\"" \ + -DLOGO_NAME="\"totem_logo.png\"" \ + $(AM_CPPFLAGS) + +totem_plugin_viewer_CFLAGS = \ + $(EXTRA_GNOME_CFLAGS) \ + $(WARN_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(NVTV_CFLAGS) \ + $(AM_CFLAGS) + +totem_plugin_viewer_LDADD = \ + $(top_builddir)/src/backend/libbaconvideowidget.la \ + $(top_builddir)/src/libbaconpropertiespage.la \ + $(top_builddir)/src/libtotem_player.la \ + $(top_builddir)/src/plparse/libtotem-plparser.la \ + $(EXTRA_GNOME_LIBS) \ + $(XVIDMODE_LIBS) \ + $(DBUS_LIBS) \ + $(NVTV_LIBS) \ + $(XTEST_LIBS) \ + $(X_LIBS) + +BUILT_SOURCES += \ + totem-plugin-viewer-interface.h + +totem-plugin-viewer-interface.h: org_gnome_totem_PluginViewer.xml + $(DBUS_BIND) --prefix=totem_embedded --mode=glib-server $< > $@ + +EXTRA_DIST += \ + org_gnome_totem_PluginViewer.xml # Totem Basic Plugin @@ -25,7 +85,8 @@ libtotem_basic_plugin_la_SOURCES = \ totemPlugin.cpp \ totemPlugin.h \ totemBasicPlugin.cpp \ - totemBasicPlugin.h + totemBasicPlugin.h \ + totem-plugin-viewer-commands.h libtotem_basic_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/src \ @@ -40,11 +101,11 @@ libtotem_basic_plugin_la_CPPFLAGS = \ -DTOTEM_PL_PARSER_MINI \ -D_REENTRANT \ -DDBUS_API_SUBJECT_TO_CHANGE \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -DGCONF_PREFIX=\""/apps/totem"\" \ - -DDATADIR=\""$(pkgdatadir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DBINDIR=\""$(bindir)"\" \ + -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ + -DGCONF_PREFIX="\"/apps/totem\"" \ + -DDATADIR="\"$(pkgdatadir)\"" \ + -DLIBEXECDIR="\"$(libexecdir)\"" \ + -DBINDIR="\"$(bindir)\"" \ $(AM_CPPFLAGS) libtotem_basic_plugin_la_CFLAGS = \ @@ -65,7 +126,8 @@ libtotem_basic_plugin_la_CXXFLAGS = \ libtotem_basic_plugin_la_LIBADD = \ $(top_builddir)/src/plparse/libtotem-plparser-mini.la \ $(DBUS_LIBS) \ - $(MOZILLA_LIBS) + $(MOZILLA_LIBS) \ + $(LIBXPCOMGLUE_S) libtotem_basic_plugin_la_LDFLAGS = \ -avoid-version \ @@ -87,7 +149,8 @@ libtotem_gmp_plugin_la_SOURCES = \ totemPlugin.cpp \ totemPlugin.h \ totemGMPPlugin.cpp \ - totemGMPPlugin.h + totemGMPPlugin.h \ + totem-plugin-viewer-commands.h libtotem_gmp_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/src \ @@ -102,11 +165,11 @@ libtotem_gmp_plugin_la_CPPFLAGS = \ -DTOTEM_PL_PARSER_MINI \ -D_REENTRANT \ -DDBUS_API_SUBJECT_TO_CHANGE \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -DGCONF_PREFIX=\""/apps/totem"\" \ - -DDATADIR=\""$(pkgdatadir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DBINDIR=\""$(bindir)"\" \ + -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ + -DGCONF_PREFIX="\"/apps/totem\"" \ + -DDATADIR="\"$(pkgdatadir)\"" \ + -DLIBEXECDIR="\"$(libexecdir)\"" \ + -DBINDIR="\"$(bindir)\"" \ $(AM_CPPFLAGS) libtotem_gmp_plugin_la_CFLAGS = \ @@ -127,7 +190,8 @@ libtotem_gmp_plugin_la_CXXFLAGS = \ libtotem_gmp_plugin_la_LIBADD = \ $(top_builddir)/src/plparse/libtotem-plparser-mini.la \ $(DBUS_LIBS) \ - $(MOZILLA_LIBS) + $(MOZILLA_LIBS) \ + $(LIBXPCOMGLUE_S) libtotem_gmp_plugin_la_LDFLAGS = \ -avoid-version \ @@ -149,7 +213,8 @@ libtotem_complex_plugin_la_SOURCES = \ totemPlugin.cpp \ totemPlugin.h \ totemComplexPlugin.cpp \ - totemComplexPlugin.h + totemComplexPlugin.h \ + totem-plugin-viewer-commands.h libtotem_complex_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/src \ @@ -164,11 +229,11 @@ libtotem_complex_plugin_la_CPPFLAGS = \ -DTOTEM_PL_PARSER_MINI \ -D_REENTRANT \ -DDBUS_API_SUBJECT_TO_CHANGE \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -DGCONF_PREFIX=\""/apps/totem"\" \ - -DDATADIR=\""$(pkgdatadir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DBINDIR=\""$(bindir)"\" \ + -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ + -DGCONF_PREFIX="\"/apps/totem\"" \ + -DDATADIR="\"$(pkgdatadir)\"" \ + -DLIBEXECDIR="\"$(libexecdir)\"" \ + -DBINDIR="\"$(bindir)\"" \ $(AM_CPPFLAGS) libtotem_complex_plugin_la_CFLAGS = \ @@ -189,7 +254,8 @@ libtotem_complex_plugin_la_CXXFLAGS = \ libtotem_complex_plugin_la_LIBADD = \ $(top_builddir)/src/plparse/libtotem-plparser-mini.la \ $(DBUS_LIBS) \ - $(MOZILLA_LIBS) + $(MOZILLA_LIBS) \ + $(LIBXPCOMGLUE_S) libtotem_complex_plugin_la_LDFLAGS = \ -avoid-version \ @@ -211,7 +277,8 @@ libtotem_narrowspace_plugin_la_SOURCES = \ totemPlugin.cpp \ totemPlugin.h \ totemNarrowSpacePlugin.cpp \ - totemNarrowSpacePlugin.h + totemNarrowSpacePlugin.h \ + totem-plugin-viewer-commands.h libtotem_narrowspace_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/src \ @@ -226,11 +293,11 @@ libtotem_narrowspace_plugin_la_CPPFLAGS = \ -DTOTEM_PL_PARSER_MINI \ -D_REENTRANT \ -DDBUS_API_SUBJECT_TO_CHANGE \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -DGCONF_PREFIX=\""/apps/totem"\" \ - -DDATADIR=\""$(pkgdatadir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DBINDIR=\""$(bindir)"\" \ + -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ + -DGCONF_PREFIX="\"/apps/totem\"" \ + -DDATADIR="\"$(pkgdatadir)\"" \ + -DLIBEXECDIR="\"$(libexecdir)\"" \ + -DBINDIR="\"$(bindir)\"" \ $(AM_CPPFLAGS) libtotem_narrowspace_plugin_la_CFLAGS = \ @@ -251,7 +318,8 @@ libtotem_narrowspace_plugin_la_CXXFLAGS = \ libtotem_narrowspace_plugin_la_LIBADD = \ $(top_builddir)/src/plparse/libtotem-plparser-mini.la \ $(DBUS_LIBS) \ - $(MOZILLA_LIBS) + $(MOZILLA_LIBS) \ + $(LIBXPCOMGLUE_S) libtotem_narrowspace_plugin_la_LDFLAGS = \ -avoid-version \ @@ -273,7 +341,8 @@ libtotem_mully_plugin_la_SOURCES = \ totemPlugin.cpp \ totemPlugin.h \ totemMullYPlugin.cpp \ - totemMullYPlugin.h + totemMullYPlugin.h \ + totem-plugin-viewer-commands.h libtotem_mully_plugin_la_CPPFLAGS = \ -I$(top_srcdir)/src \ @@ -288,11 +357,11 @@ libtotem_mully_plugin_la_CPPFLAGS = \ -DTOTEM_PL_PARSER_MINI \ -D_REENTRANT \ -DDBUS_API_SUBJECT_TO_CHANGE \ - -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ - -DGCONF_PREFIX=\""/apps/totem"\" \ - -DDATADIR=\""$(pkgdatadir)"\" \ - -DLIBEXECDIR=\""$(libexecdir)"\" \ - -DBINDIR=\""$(bindir)"\" \ + -DGNOMELOCALEDIR="\"$(datadir)/locale\"" \ + -DGCONF_PREFIX="\"/apps/totem\"" \ + -DDATADIR="\"$(pkgdatadir)\"" \ + -DLIBEXECDIR="\"$(libexecdir)\"" \ + -DBINDIR="\"$(bindir)\"" \ $(AM_CPPFLAGS) libtotem_mully_plugin_la_CFLAGS = \ @@ -313,7 +382,8 @@ libtotem_mully_plugin_la_CXXFLAGS = \ libtotem_mully_plugin_la_LIBADD = \ $(top_builddir)/src/plparse/libtotem-plparser-mini.la \ $(DBUS_LIBS) \ - $(MOZILLA_LIBS) + $(MOZILLA_LIBS) \ + $(LIBXPCOMGLUE_S) libtotem_mully_plugin_la_LDFLAGS = \ -avoid-version \ diff --git a/browser-plugin/README b/browser-plugin/README new file mode 100644 index 000000000..ba6394d46 --- /dev/null +++ b/browser-plugin/README @@ -0,0 +1,17 @@ +Notes on the plugin's internas +============================== + + +Special topics: Complex plugin +============================== + +The complex plugin supports multiple plugins working together, see +[http://service.real.com/help/library/guides/realone/ProductionGuide/HTML/realpgd.htm?page=htmfiles/embed.htm]. + +To implement this, we maintain a list of totem complex plugins. +The plugins are partitioned into classes by their 'console' name; and a +random plugin (well, the first one instantiated) is chosen as representant. +This one owns the plugin viewer and all viewer interaction of the linked plugins +are done trough it. When this plugin is destroyed, we search for a new +representant, transfer the viewer ownership to it and update the representant +pointer in all of the classes' plugins. diff --git a/browser-plugin/TODO b/browser-plugin/TODO index b5a899a6f..b604cbbba 100644 --- a/browser-plugin/TODO +++ b/browser-plugin/TODO @@ -2,6 +2,18 @@ Plugin: - get info about play state, URL etc back from viewer to the plugin - support all <embed> and <object> attributes that the legacy plugins support - there still seem to be some unnecessary symbols exported in the plugin .so's +- use peer-2-peer DBUS instead of session bus +- (maybe) use libdbus directly instead of dbus-glib +- unify playlist handling into the viewer (is it possible to write a gstreamer element + that handles the playlist internally?) +- make StreamAsFile use async calls; need to keep a link to the file around!! +- put back narrowspace URL extension parsing +- use nsCOMPtr<> once the linking has been figured out (libxpcomglue_s) +- invert DBUS-OpenStream/NPN_GetURL order: this is necessary so we can push the real stream URI + to the viewer (for use as base uri for redirects) + +Viewer: +- show totem logo image when waiting for click to start? GMP plugin: - write IDL files (where [_not_ MSDN !] to find documentation?) @@ -9,6 +21,8 @@ GMP plugin: Complex plugin: - write IDL file (nsIHXPlayer.idl available, but incompatible with totem's licence exception; is the exception needed for the *plugin* itself?) +- implement the various 'controls' types in the viewer +- in the viewer, cope with the 'main' window being removed? (maybe reparent to a hidden window) NarrowSpace plugin: - write IDL file (where to find documentation?) diff --git a/browser-plugin/org_gnome_totem_PluginViewer.xml b/browser-plugin/org_gnome_totem_PluginViewer.xml new file mode 100644 index 000000000..0bf4df057 --- /dev/null +++ b/browser-plugin/org_gnome_totem_PluginViewer.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/org/gnome/totem/PluginViewer"> + <interface name="org.gnome.totem.PluginViewer"> + <method name="DoCommand"> + <arg type="s" name="Command" direction="in" /> + </method> + <method name="SetWindow"> + <arg type="s" name="Controls" direction="in" /> + <arg type="u" name="XID" direction="in" /> + <arg type="i" name="Width" direction="in" /> + <arg type="i" name="Height" direction="in" /> + </method> + <method name="UnsetWindow"> + <arg type="u" name="XID" direction="in" /> + </method> + <method name="OpenStream"> + <arg type="s" name="URI" direction="in" /> + <arg type="s" name="BaseURI" direction="in" /> + </method> + <method name="CloseStream" /> + <method name="OpenURI"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="totem_embedded_open_uri" /> + <arg type="s" name="URI" direction="in" /> + <arg type="s" name="BaseURI" direction="in" /> + </method> + <method name="SetPlaylist"> + <arg type="s" name="Path" direction="in" /> + <arg type="s" name="URI" direction="in" /> + <arg type="s" name="BaseURI" direction="in" /> + </method> + <method name="SetLocalFile"> + <arg type="s" name="Path" direction="in" /> + <arg type="s" name="URI" direction="in" /> + <arg type="s" name="BaseURI" direction="in" /> + </method> + + <signal name="ButtonPress" /> + <signal name="StartStream" /> + <signal name="StopStream" /> + </interface> +</node> diff --git a/browser-plugin/totem-plugin-viewer-commands.h b/browser-plugin/totem-plugin-viewer-commands.h new file mode 100644 index 000000000..631d500ef --- /dev/null +++ b/browser-plugin/totem-plugin-viewer-commands.h @@ -0,0 +1,28 @@ +/* Totem Plugin Viewer commands + * + * Copyright © 2006 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __TOTEM_PLUGIN_VIEWER_COMMANDS__ +#define __TOTEM_PLUGIN_VIEWER_COMMANDS__ + +#define TOTEM_COMMAND_PLAY "Play" +#define TOTEM_COMMAND_PAUSE "Pause" +#define TOTEM_COMMAND_STOP "Stop" + +#endif /* !__TOTEM_PLUGIN_VIEWER_COMMANDS__ */ diff --git a/src/totem-mozilla-options.h b/browser-plugin/totem-plugin-viewer-options.h index 6a2359000..0bd595513 100644 --- a/src/totem-mozilla-options.h +++ b/browser-plugin/totem-plugin-viewer-options.h @@ -1,6 +1,6 @@ -/* Totem Mozilla plugin +/* Totem Plugin Viewer options * - * Copyright (C) <2005> Bastien Nocera <hadess@hadess.net> + * Copyright © 2005 Bastien Nocera <hadess@hadess.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,16 +18,22 @@ * Boston, MA 02111-1307, USA. */ -#define TOTEM_OPTION_XID "xid" -#define TOTEM_OPTION_WIDTH "width" -#define TOTEM_OPTION_HEIGHT "height" -#define TOTEM_OPTION_URL "url" -#define TOTEM_OPTION_CONTROLS_HIDDEN "nocontrols" -#define TOTEM_OPTION_STATUSBAR "statusbar" -#define TOTEM_OPTION_HREF "href" -#define TOTEM_OPTION_TARGET "target" +#ifndef __TOTEM_PLUGIN_VIEWER_OPTIONS_H__ +#define __TOTEM_PLUGIN_VIEWER_OPTIONS_H__ + +#define TOTEM_PLUGIN_VIEWER_INTERFACE_NAME "org.gnome.totem.PluginViewer" +#define TOTEM_PLUGIN_VIEWER_NAME_TEMPLATE "org.gnome.totem.PluginViewer_%d" +#define TOTEM_PLUGIN_VIEWER_DBUS_PATH "/org/gnome/totem/PluginViewer" + +#define TOTEM_OPTION_BASE_URI "base-uri" +#define TOTEM_OPTION_CONTROLS_HIDDEN "no-controls" #define TOTEM_OPTION_HIDDEN "hidden" #define TOTEM_OPTION_MIMETYPE "mimetype" +#define TOTEM_OPTION_NOAUTOSTART "no-autostart" #define TOTEM_OPTION_PLAYLIST "playlist" +#define TOTEM_OPTION_PLUGIN_TYPE "plugin-type" #define TOTEM_OPTION_REPEAT "repeat" -#define TOTEM_OPTION_NOAUTOSTART "noautostart" +#define TOTEM_OPTION_USER_AGENT "user-agent" +#define TOTEM_OPTION_STATUSBAR "statusbar" + +#endif /* !__TOTEM_PLUGIN_VIEWER_OPTIONS_H__ */ diff --git a/browser-plugin/totem-plugin-viewer.c b/browser-plugin/totem-plugin-viewer.c new file mode 100644 index 000000000..079da7713 --- /dev/null +++ b/browser-plugin/totem-plugin-viewer.c @@ -0,0 +1,1566 @@ +/* Totem Plugin Viewer + * + * Copyright © 2004-2006 Bastien Nocera <hadess@hadess.net> + * Copyright © 2002 David A. Schleef <ds@schleef.org> + * Copyright © 2006 Christian Persch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> +#include <glade/glade.h> +#include <gconf/gconf-client.h> + +#include <totem-pl-parser.h> + +#include <dbus/dbus-glib.h> + +#include <libgnomevfs/gnome-vfs.h> +#include <libgnomevfs/gnome-vfs-mime-handlers.h> + +#include "bacon-video-widget.h" +#include "totem-interface.h" +#include "totem-statusbar.h" +#include "bacon-volume.h" +#include "video-utils.h" + +#include "totem-plugin-viewer-commands.h" +#include "totem-plugin-viewer-options.h" + +GtkWidget *totem_statusbar_create (void); +GtkWidget *totem_volume_create (void); + +#define VOLUME_DOWN_OFFSET -8 +#define VOLUME_UP_OFFSET 8 + +/* For newer D-Bus version */ +#ifndef DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT +#define DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT 0 +#endif + +typedef enum { + STATE_PLAYING, + STATE_PAUSED, + STATE_STOPPED, + LAST_STATE +} TotemStates; + +typedef enum { + TOTEM_PLUGIN_TYPE_BASIC, + TOTEM_PLUGIN_TYPE_GMP, + TOTEM_PLUGIN_TYPE_COMPLEX, + TOTEM_PLUGIN_TYPE_NARROWSPACE, + TOTEM_PLUGIN_TYPE_MULLY, + TOTEM_PLUGIN_TYPE_LAST +} TotemPluginType; + +#define TOTEM_TYPE_EMBEDDED (totem_embedded_get_type ()) +#define TOTEM_EMBEDDED(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TOTEM_TYPE_EMBEDDED, TotemEmbedded)) +#define TOTEM_EMBEDDED_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TOTEM_TYPE_EMBEDDED, TotemEmbeddedClass)) +#define TOTEM_IS_EMBEDDED(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TOTEM_TYPE_EMBEDDED)) +#define TOTEM_IS_EMBEDDED_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TOTEM_TYPE_EMBEDDED)) +#define TOTEM_EMBEDDED_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), TOTEM_TYPE_EMBEDDED, TotemEmbeddedClass)) + +typedef GObjectClass TotemEmbeddedClass; + +typedef struct _TotemEmbedded { + GObject parent; + + GtkWidget *window; + GladeXML *menuxml, *xml; + GtkWidget *about; + TotemStatusbar *statusbar; + int width, height; + const char *mimetype; + char *base_uri; + char *current_uri; + char *href; + char *target; + char *stream_uri; + BaconVideoWidget *bvw; + TotemStates state; + GdkCursor *cursor; + + /* Playlist */ + GList *playlist, *current; + guint parser_id; + int num_items; + + /* Open menu item */ + GnomeVFSMimeApplication *app; + GtkWidget *menu_item; + + /* Seek bits */ + GtkAdjustment *seekadj; + GtkWidget *seek; + + /* Error */ + + guint type : 3; /* TotemPluginType */ + + guint is_browser_stream : 1; + guint is_playlist : 1; + guint controller_hidden : 1; + guint show_statusbar : 1; + guint hidden : 1; + guint repeat : 1; + guint seeking : 1; + guint noautostart : 1; + guint autostart : 1; +} TotemEmbedded; + +GType totem_embedded_get_type (void); + +#define TOTEM_EMBEDDED_ERROR_QUARK (g_quark_from_static_string ("TotemEmbeddedErrorQuark")) + +enum +{ + TOTEM_EMBEDDED_UNKNOWN_PLUGIN_TYPE, + TOTEM_EMBEDDED_SETWINDOW_UNSUPPORTED_CONTROLS, + TOTEM_EMBEDDED_SETWINDOW_HAVE_WINDOW, + TOTEM_EMBEDDED_SETWINDOW_INVALID_XID, + TOTEM_EMBEDDED_NO_URI, + TOTEM_EMBEDDED_OPEN_FAILED, + TOTEM_EMBEDDED_UNKNOWN_COMMAND +}; + +G_DEFINE_TYPE (TotemEmbedded, totem_embedded, G_TYPE_OBJECT); +static void totem_embedded_init (TotemEmbedded *emb) { } + +static gboolean totem_embedded_do_command (TotemEmbedded *emb, const char *command, GError **err); +static gboolean totem_embedded_push_parser (gpointer data); +static gboolean totem_embedded_play (TotemEmbedded *embedded, GError **error); + +static void totem_embedded_clear_playlist (TotemEmbedded *embedded); + +static void totem_embedded_set_menu (TotemEmbedded *emb, gboolean enable_copy); +static void on_open1_activate (GtkButton *button, TotemEmbedded *emb); + +enum { + BUTTON_PRESS, + START_STREAM, + STOP_STREAM, + LAST_SIGNAL +}; +static int signals[LAST_SIGNAL] = { 0 }; + +static void +totem_embedded_finalize (GObject *object) +{ + TotemEmbedded *embedded = TOTEM_EMBEDDED (object); + + if (embedded->window) + gtk_widget_destroy (embedded->window); + + if (embedded->xml) + g_object_unref (embedded->xml); + if (embedded->menuxml) + g_object_unref (embedded->menuxml); + + /* FIXME etc */ + + G_OBJECT_CLASS (totem_embedded_parent_class)->finalize (object); +} + +static void totem_embedded_class_init (TotemEmbeddedClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = totem_embedded_finalize; + + signals[BUTTON_PRESS] = + g_signal_newv ("button-press", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + NULL /* class closure */, + NULL /* accu */, NULL /* accu data */, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0, NULL); + signals[START_STREAM] = + g_signal_newv ("start-stream", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + NULL /* class closure */, + NULL /* accu */, NULL /* accu data */, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0, NULL); + signals[STOP_STREAM] = + g_signal_newv ("stop-stream", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + NULL /* class closure */, + NULL /* accu */, NULL /* accu data */, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0, NULL); +} + +static void +totem_embedded_exit (TotemEmbedded *emb) +{ + //FIXME what happens when embedded, and we can't go on? + exit (1); +} + +static void +totem_embedded_error_and_exit (char *title, char *reason, TotemEmbedded *emb) +{ + /* FIXME send a signal to the plugin with the error message instead! */ + totem_interface_error_blocking (title, reason, + GTK_WINDOW (emb->window)); + totem_embedded_exit (emb); +} + +static void +totem_embedded_set_error (TotemEmbedded *emb, + char *primary, + char *secondary) +{ + /* FIXME */ +} + +static void +totem_embedded_set_state (TotemEmbedded *emb, TotemStates state) +{ + GtkWidget *image; + GdkCursor *cursor; + char id[32]; + static const char *states[] = { + "PLAYING", + "PAUSED", + "STOPPED", + "INVALID" + }; + + if (state == emb->state) + return; + + g_message ("Viewer state: %s", states[state]); + + cursor = NULL; + image = glade_xml_get_widget (emb->xml, "emb_pp_button_image"); + + switch (state) { + case STATE_STOPPED: + if (emb->href != NULL) + cursor = emb->cursor; + + g_snprintf (id, sizeof (id), "gtk-media-play-%s", + gtk_widget_get_direction (image) ? "ltr" : "rtl"); + totem_statusbar_set_text (emb->statusbar, _("Stopped")); + totem_statusbar_set_time_and_length (emb->statusbar, 0, 0); + break; + case STATE_PAUSED: + g_snprintf (id, sizeof (id), "gtk-media-play-%s", + gtk_widget_get_direction (image) ? "ltr" : "rtl"); + totem_statusbar_set_text (emb->statusbar, _("Paused")); + break; + case STATE_PLAYING: + g_snprintf (id, sizeof (id), "gtk-media-pause"); + totem_statusbar_set_text (emb->statusbar, _("Playing")); + break; + default: + break; + } + + gtk_image_set_from_icon_name (GTK_IMAGE (image), id, GTK_ICON_SIZE_MENU); + + if (emb->hidden == FALSE && cursor != NULL) + gdk_window_set_cursor (GTK_WIDGET (emb->bvw)->window, cursor); + + emb->state = state; +} + +static void +totem_embedded_set_pp_state (TotemEmbedded *emb, gboolean state) +{ + GtkWidget *item; + + item = glade_xml_get_widget (emb->xml, "pp_button"); + gtk_widget_set_sensitive (item, state); +} + +static gboolean +totem_embedded_open_internal (TotemEmbedded *emb, + GError **error) +{ + GError *err = NULL; + gboolean retval; + const char *uri; + + /* FIXME: stop previous content, or is that automatic ? */ + + if (emb->is_browser_stream) + uri = "fd://0"; + else + uri = emb->current_uri; + + if (!uri) { + g_set_error (error, + TOTEM_EMBEDDED_ERROR_QUARK, + TOTEM_EMBEDDED_NO_URI, + _("No URI to play")); + //FIXME totem_embedded_set_error (emb, error); |error| may be null? + + return FALSE; + } + + g_message ("totem_embedded_open_internal '%s' is-browser-stream %d", uri, emb->is_browser_stream); + + bacon_video_widget_set_logo_mode (emb->bvw, FALSE); + + /* FIXME: remove |err| and rely on async on_error? */ + g_message ("BEFORE _open"); + retval = bacon_video_widget_open (emb->bvw, uri, &err); + g_message ("AFTER _open"); + if (retval == FALSE) + { + GError *errint; + char *primary; + + /* FIXME we haven't even started sending yet! */ + //g_signal_emit (emb, signals[STOP_STREAM], 0); + + totem_embedded_set_state (emb, STATE_STOPPED); + bacon_video_widget_set_logo_mode (emb->bvw, TRUE); + + errint = g_error_new (TOTEM_EMBEDDED_ERROR_QUARK, + TOTEM_EMBEDDED_OPEN_FAILED, + _("Totem could not play '%s'"), + emb->current_uri); + + //FIXME disp = gnome_vfs_unescape_string_for_display (totem->mrl); ? + primary = g_strdup_printf(_("Totem could not play '%s'"), emb->current_uri); + totem_embedded_set_error (emb, primary, err->message);; + g_free (primary); + + g_propagate_error (error, err); + + totem_embedded_set_pp_state (emb, FALSE); + } else { + /* FIXME this doesn't deal correctly with playlists */ + //if (emb->autostart) + totem_embedded_play (emb, NULL); + + //totem_embedded_set_state (emb, STATE_PAUSED); + totem_embedded_set_pp_state (emb, TRUE); + } + + if (emb->is_browser_stream) { + totem_embedded_set_menu (emb, FALSE); + } else { + totem_embedded_set_menu (emb, TRUE); + } + + return retval; +} + +static gboolean +totem_embedded_play (TotemEmbedded *emb, + GError **error) +{ + if (bacon_video_widget_play (emb->bvw, NULL)) + totem_embedded_set_state (emb, STATE_PLAYING); + + return TRUE; +} + +static gboolean +totem_embedded_pause (TotemEmbedded *emb, + GError **error) +{ + bacon_video_widget_pause (emb->bvw); + totem_embedded_set_state (emb, STATE_PAUSED); + + return TRUE; +} + +static gboolean +totem_embedded_stop (TotemEmbedded *emb, + GError **error) +{ + bacon_video_widget_stop (emb->bvw); + totem_embedded_set_state (emb, STATE_STOPPED); + + return TRUE; +} + +static gboolean +totem_embedded_do_command (TotemEmbedded *embedded, + const char *command, + GError **error) +{ + g_return_val_if_fail (command != NULL, FALSE); + + if (strcmp (command, TOTEM_COMMAND_PLAY) == 0) { + return totem_embedded_play (embedded, error); + } + if (strcmp (command, TOTEM_COMMAND_PAUSE) == 0) { + return totem_embedded_pause (embedded, error); + } + if (strcmp (command, TOTEM_COMMAND_STOP) == 0) { + return totem_embedded_stop (embedded, error); + } + + g_set_error (error, + TOTEM_EMBEDDED_ERROR_QUARK, + TOTEM_EMBEDDED_UNKNOWN_COMMAND, + "Unknown command '%s'", command); + return FALSE; +} + +static void +totem_embedded_set_uri (TotemEmbedded *emb, + const char *uri, + const char *base_uri, + gboolean is_browser_stream) +{ + char *old_uri, *old_base; + + old_uri = emb->current_uri; + old_base = emb->base_uri; + + emb->current_uri = g_strdup (uri); + emb->base_uri = g_strdup (base_uri); + emb->is_browser_stream = (is_browser_stream != FALSE); + + g_free (old_uri); + g_free (old_base); +} + +static gboolean +totem_embedded_open_uri (TotemEmbedded *emb, + const char *uri, + const char *base_uri, + GError **error) +{ + totem_embedded_clear_playlist (emb); + + bacon_video_widget_close (emb->bvw); + + totem_embedded_set_uri (emb, uri, base_uri, FALSE); + + return totem_embedded_open_internal (emb, error); +} + +static gboolean +totem_embedded_open_stream (TotemEmbedded *emb, + const char *uri, + const char *base_uri, + GError **error) +{ + totem_embedded_clear_playlist (emb); + + bacon_video_widget_close (emb->bvw); + + totem_embedded_set_uri (emb, uri, base_uri, TRUE); + + /* FIXME: consume any remaining input from stdin */ + + return totem_embedded_open_internal (emb, error); +} + +static gboolean +totem_embedded_close_stream (TotemEmbedded *emb, + GError *error) +{ + if (!emb->is_browser_stream) + return TRUE; + + /* FIXME this enough? */ + bacon_video_widget_close (emb->bvw); + + return TRUE; +} + +static gboolean +totem_embedded_open_playlist_item (TotemEmbedded *emb, + GList *item) +{ + gboolean eop; + + if (!emb->playlist) + return FALSE; + + eop = item == NULL; + + /* Start at the head */ + if (item == NULL) + item = emb->playlist; + + /* FIXME: if (emb->current == item) { just start again, depending on repeat/autostart settings } */ + emb->current = item; + g_assert (item != NULL); + + totem_embedded_set_uri (emb, + (const char *) item->data, + emb->base_uri /* FIXME? */, + FALSE); + + bacon_video_widget_close (emb->bvw); + if (totem_embedded_open_internal (emb, NULL /* FIXME */) && + ((eop != FALSE && emb->repeat != FALSE) + || (eop == FALSE))) { + totem_embedded_play (emb, NULL); + } + + return TRUE; +} + +static gboolean +totem_embedded_set_local_file (TotemEmbedded *emb, + const char *path, + const char *uri, + const char *base_uri, + GError **error) +{ + char *file_uri; + + g_message ("Setting the current path to %s", path); + + totem_embedded_clear_playlist (emb); + + file_uri = g_filename_to_uri (path, NULL, error); + if (!file_uri) + return FALSE; + + /* FIXME what about |uri| param?!! */ + totem_embedded_set_uri (emb, file_uri, base_uri, FALSE); + g_free (file_uri); + + return totem_embedded_open_internal (emb, error); +} + +static gboolean +totem_embedded_set_playlist (TotemEmbedded *emb, + const char *path, + const char *uri, + const char *base_uri, + GError **error) +{ + char *file_uri; + + g_message ("Setting the current playlist to %s", path); + + totem_embedded_clear_playlist (emb); + + file_uri = g_filename_to_uri (path, NULL, error); + if (!file_uri) + return FALSE; + + totem_embedded_set_uri (emb, file_uri, base_uri, FALSE); + g_free (file_uri); + + /* Schedule parsing on idle */ + if (emb->parser_id == 0) + emb->parser_id = g_idle_add (totem_embedded_push_parser, + emb); + + return TRUE; +} + +static void +totem_embedded_set_menu (TotemEmbedded *emb, + gboolean enable_copy) +{ + GtkWidget *menu, *item, *image; + GtkWidget *copy; + char *label; + + copy = glade_xml_get_widget (emb->menuxml, "copy_location1"); + gtk_widget_set_sensitive (copy, enable_copy); + + if (emb->menu_item != NULL) { + gtk_widget_destroy (emb->menu_item); + emb->menu_item = NULL; + } + if (emb->app != NULL) { + gnome_vfs_mime_application_free (emb->app); + emb->app = NULL; + } + + if (enable_copy == FALSE) + return; + + if (emb->mimetype) { + emb->app = gnome_vfs_mime_get_default_application_for_uri + (emb->current_uri, emb->mimetype); + } else { + emb->app = gnome_vfs_mime_get_default_application_for_uri + (emb->current_uri, + gnome_vfs_get_mime_type_for_name (emb->current_uri)); + } + + if (emb->app == NULL) + return; + + /* translators: this is: + * Open With ApplicationName + * as in nautilus' right-click menu */ + label = g_strdup_printf ("_Open with \"%s\"", emb->app->name); + item = gtk_image_menu_item_new_with_mnemonic (label); + g_free (label); + image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); + g_signal_connect (G_OBJECT (item), "activate", + G_CALLBACK (on_open1_activate), emb); + gtk_widget_show (item); + emb->menu_item = item; + + menu = glade_xml_get_widget (emb->menuxml, "menu"); + gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); +} + +static void +on_open1_activate (GtkButton *button, TotemEmbedded *emb) +{ + GList *l = NULL; + + g_return_if_fail (emb->app != NULL); + + /* FIXME */ + if (emb->type == TOTEM_PLUGIN_TYPE_NARROWSPACE && + emb->href != NULL) { + l = g_list_prepend (l, emb->href); + } else { + l = g_list_prepend (l, emb->current_uri); + } + + /* FIXME: launch with startup ID and startup notification! */ + if (gnome_vfs_mime_application_launch (emb->app, l) == GNOME_VFS_OK) { + totem_embedded_stop (emb, NULL); + } + + g_list_free (l); +} + +static void +on_about1_activate (GtkButton *button, TotemEmbedded *emb) +{ + char *backend_version, *description; + + const char *authors[] = + { + "Bastien Nocera <hadess@hadess.net>", + "Ronald Bultje <rbultje@ronald.bitfreak.net>", + "Christian Persch" " <" "chpe" "@" "gnome" "." "org" ">", + NULL + }; + + if (emb->about != NULL) + { + gtk_window_present (GTK_WINDOW (emb->about)); + return; + } + + backend_version = bacon_video_widget_get_backend_name (emb->bvw); + description = g_strdup_printf (_("Browser Plugin using %s"), + backend_version); + + emb->about = g_object_new (GTK_TYPE_ABOUT_DIALOG, + "name", _("Totem Browser Plugin"), + "version", VERSION, + "copyright", _("Copyright \xc2\xa9 2002-2006 Bastien Nocera"), + "comments", description, + "authors", authors, + "translator-credits", _("translator-credits"), + "logo-icon-name", "totem", + NULL); + + g_free (backend_version); + g_free (description); + + totem_interface_set_transient_for (GTK_WINDOW (emb->about), + GTK_WINDOW (emb->window)); + + g_object_add_weak_pointer (G_OBJECT (emb->about), + (gpointer *)&emb->about); + g_signal_connect (G_OBJECT (emb->about), "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_widget_show (emb->about); +} + +static void +on_copy_location1_activate (GtkButton *button, TotemEmbedded *emb) +{ + GdkDisplay *display; + GtkClipboard *clip; + const char *uri; + + if (emb->is_browser_stream) { + uri = emb->base_uri; /* FIXME! */ + } else if (emb->href != NULL) { + uri = emb->href; + } else { + uri = emb->current_uri; + } + + display = gtk_widget_get_display (GTK_WIDGET (emb->window)); + + /* Set both the middle-click and the super-paste buffers */ + clip = gtk_clipboard_get_for_display (display, + GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text (clip, uri, -1); + + clip = gtk_clipboard_get_for_display (display, + GDK_SELECTION_PRIMARY); + gtk_clipboard_set_text (clip, uri, -1); +} + +static void +on_play_pause (GtkWidget *widget, TotemEmbedded *emb) +{ + if (emb->state == STATE_PLAYING) { + totem_embedded_pause (emb, NULL); + } else { + totem_embedded_play (emb, NULL); + } +} + +static void +on_got_redirect (GtkWidget *bvw, const char *mrl, TotemEmbedded *emb) +{ + char *new_uri = NULL; + + g_message ("url: %s", emb->base_uri); + g_message ("redirect: %s", mrl); + + bacon_video_widget_close (emb->bvw); + + if (emb->current_uri) + new_uri = totem_resolve_relative_link (emb->current_uri, mrl); + /* FIXME: not sure that this is actually correct... */ + else if (emb->base_uri) + new_uri = totem_resolve_relative_link (emb->base_uri, mrl); + + if (!new_uri) + return; + + g_message ("Redirecting to '%s'", new_uri); + + /* FIXME: clear playlist? or replace current entry? or add a new entry? */ + /* FIXME: use totem_embedded_open_uri? */ + + totem_embedded_set_uri (emb, new_uri, emb->base_uri /* FIXME? */, FALSE); + + totem_embedded_set_state (emb, STATE_STOPPED); + + if (totem_embedded_open_internal (emb, NULL /* FIXME? */) != FALSE) + totem_embedded_play (emb, NULL); +} + +static gboolean +on_video_button_press_event (BaconVideoWidget *bvw, + GdkEventButton *event, + TotemEmbedded *emb) +{ + guint state = event->state & gtk_accelerator_get_default_mod_mask (); + gboolean handled = FALSE; + + //g_print ("button-press type %d button %d state %d play-state %d\n", + // event->type, event->button, state, emb->state); + + if (event->type == GDK_BUTTON_PRESS && + event->button == 1 && + state == 0 && + emb->state == STATE_STOPPED) { + g_signal_emit (emb, signals[BUTTON_PRESS], 0, + event->time, + event->button); + handled = TRUE; + } + +#if 0 + if (emb->state == STATE_STOPPED) { + g_signal_emit (emb, signals[START_PLAY], 0); + } +#ifdef TOTEM_RUN_IN_SOURCE_TREE + if (g_file_test ("./totem", + G_FILE_TEST_EXISTS) != FALSE) { + cmd = g_strdup_printf ("./totem %s", + emb->href); + } else +#endif + { + cmd = g_strdup_printf (BINDIR"/totem %s", + emb->href); + } + /* FIXME Shouldn't this use gdk_spawn_on_screen? */ + if (!g_spawn_command_line_async (cmd, &err)) { +#ifdef NOTPORTED + /* FIXME this is unacceptable! */ + totem_interface_error_blocking ( + _("Failed to start stand-alone movie player"), + err->message, + GTK_WINDOW (emb->window)); + g_error_free (err); +#endif + } + g_free (cmd); +#endif + + else if (event->type == GDK_BUTTON_PRESS && + event->button == 3 && + state == 0) { + GtkMenu *menu; + + menu = GTK_MENU (glade_xml_get_widget (emb->menuxml, "menu")); + gtk_menu_popup (menu, NULL, NULL, NULL, NULL, + event->button, event->time); + + handled = TRUE; + } + + return handled; +} + +static void +on_eos_event (GtkWidget *bvw, TotemEmbedded *emb) +{ + totem_embedded_set_state (emb, STATE_STOPPED); + gtk_adjustment_set_value (emb->seekadj, 0); + + /* FIXME: the plugin needs to handle EOS itself, e.g. for QTNext */ + + /* No playlist if we have fd://0, right? */ + if (emb->is_browser_stream) { + /* FIXME: totem_embedded_set_state missing? */ + /* FIXME: should find a way to enable playback of the stream again without re-requesting it */ + totem_embedded_set_pp_state (emb, FALSE); + /* FIXME? else if (emb->playing_nth_item == emb->playlist_num_items) ? */ + } else if (emb->num_items == 1) { + if (g_str_has_prefix (emb->current_uri, "file://") != FALSE) { + if (bacon_video_widget_is_seekable (emb->bvw) != FALSE) { + bacon_video_widget_pause (emb->bvw); + bacon_video_widget_seek (emb->bvw, 0.0, NULL); + } else { + bacon_video_widget_close (emb->bvw); + totem_embedded_open_internal (emb, NULL /* FIXME? */); + } + } else { + bacon_video_widget_close (emb->bvw); + totem_embedded_open_internal (emb, NULL /* FIXME? */); + } + if (emb->repeat != FALSE && emb->autostart) + totem_embedded_play (emb, NULL); + } else if (emb->current) { + totem_embedded_open_playlist_item (emb, emb->current->next); + } +} + +static void +on_error_event (BaconVideoWidget *bvw, + char *message, + gboolean playback_stopped, + gboolean fatal, + TotemEmbedded *emb) +{ + if (playback_stopped) { + /* FIXME: necessary? */ + if (emb->is_browser_stream) + g_signal_emit (emb, signals[STOP_STREAM], 0); + + totem_embedded_set_state (emb, STATE_STOPPED); + } + + if (fatal) { + /* FIXME: report error back to plugin */ + exit (1); + } + + totem_embedded_set_error (emb, _("An error occurred"), message); +} + +static void +cb_vol (GtkWidget *val, TotemEmbedded *emb) +{ + bacon_video_widget_set_volume (emb->bvw, + bacon_volume_button_get_value (BACON_VOLUME_BUTTON (val))); +} + +static gboolean +on_volume_scroll_event (GtkWidget *win, GdkEventScroll *event, TotemEmbedded *emb) +{ + GtkWidget *vbut; + int vol, offset; + + switch (event->direction) { + case GDK_SCROLL_UP: + offset = VOLUME_UP_OFFSET; + break; + case GDK_SCROLL_DOWN: + offset = VOLUME_DOWN_OFFSET; + break; + default: + return FALSE; + } + + vbut = glade_xml_get_widget (emb->xml, "volume_button"); + vol = bacon_volume_button_get_value (BACON_VOLUME_BUTTON (vbut)); + bacon_volume_button_set_value (BACON_VOLUME_BUTTON (vbut), + vol + offset); + + return FALSE; +} + +static void +on_tick (GtkWidget *bvw, + gint64 current_time, + gint64 stream_length, + float current_position, + gboolean seekable, + TotemEmbedded *emb) +{ + if (emb->state != STATE_STOPPED) { + gtk_widget_set_sensitive (emb->seek, seekable); + if (emb->seeking == FALSE) + gtk_adjustment_set_value (emb->seekadj, + current_position * 65535); + if (stream_length == 0) { + totem_statusbar_set_time_and_length (emb->statusbar, + (int) (current_time / 1000), -1); + } else { + totem_statusbar_set_time_and_length (emb->statusbar, + (int) (current_time / 1000), + (int) (stream_length / 1000)); + } + } +} + +static gboolean +on_seek_start (GtkWidget *widget, GdkEventButton *event, TotemEmbedded *emb) +{ + emb->seeking = TRUE; + + return FALSE; +} + +static gboolean +cb_on_seek (GtkWidget *widget, GdkEventButton *event, TotemEmbedded *emb) +{ + bacon_video_widget_seek (emb->bvw, + gtk_range_get_value (GTK_RANGE (widget)) / 65535, NULL); + emb->seeking = FALSE; + + return FALSE; +} + +#ifdef GNOME_ENABLE_DEBUG +static void +controls_size_allocate_cb (GtkWidget *controls, + GtkAllocation *allocation, + gpointer data) +{ + g_message ("Viewer: Controls height is %dpx", allocation->height); + g_signal_handlers_disconnect_by_func (controls, G_CALLBACK (controls_size_allocate_cb), NULL); +} +#endif + +static gboolean +totem_embedded_construct (TotemEmbedded *emb, + GdkNativeWindow xid, + int width, + int height) +{ + GtkWidget *child, *container, *pp_button, *vbut; + BvwUseType type; + GError *err = NULL; + GConfClient *gc; + char *logo_path; + int volume; + + if (xid != 0) { + g_assert (!emb->hidden); + + emb->window = gtk_plug_new (xid); + + /* Can't do anything before it's realized */ + gtk_widget_realize (emb->window); + + emb->xml = totem_interface_load_with_root ("mozilla-viewer.glade", + "content_box", _("Plugin") /* FIXME! */, TRUE, + GTK_WINDOW (emb->window)); + g_assert (emb->xml); + + child = glade_xml_get_widget (emb->xml, "content_box"); + gtk_container_add (GTK_CONTAINER (emb->window), child); + } else { + g_assert (emb->hidden); + + emb->xml = totem_interface_load_with_root ("mozilla-viewer.glade", + "window", _("Plugin") /* FIXME! */, TRUE, + GTK_WINDOW (emb->window)); + g_assert (emb->xml); + + emb->window = glade_xml_get_widget (emb->xml, "window"); + } + + if (emb->hidden) + type = BVW_USE_TYPE_AUDIO; + else + type = BVW_USE_TYPE_VIDEO; + + emb->bvw = BACON_VIDEO_WIDGET (bacon_video_widget_new + (-1, -1, type, &err)); + + /* FIXME! */ + if (emb->bvw == NULL) + { + /* FIXME! */ + /* FIXME construct and show error message */ + totem_embedded_error_and_exit (_("The Totem plugin could not startup."), err != NULL ? err->message : _("No reason."), emb); + + if (err != NULL) + g_error_free (err); + } + + logo_path = g_build_filename (SHAREDIR, LOGO_NAME, NULL); + bacon_video_widget_set_logo (emb->bvw, logo_path); + g_free (logo_path); + + bacon_video_widget_set_logo_mode (emb->bvw, TRUE); + + g_signal_connect (G_OBJECT(emb->bvw), "got-redirect", + G_CALLBACK (on_got_redirect), emb); + g_signal_connect (G_OBJECT (emb->bvw), "eos", + G_CALLBACK (on_eos_event), emb); + g_signal_connect (G_OBJECT (emb->bvw), "error", + G_CALLBACK (on_error_event), emb); + g_signal_connect (G_OBJECT(emb->bvw), "button-press-event", + G_CALLBACK (on_video_button_press_event), emb); + g_signal_connect (G_OBJECT(emb->bvw), "tick", + G_CALLBACK (on_tick), emb); + + container = glade_xml_get_widget (emb->xml, "video_box"); + gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (emb->bvw)); + if (type == BVW_USE_TYPE_VIDEO) { + /* FIXME: why can't this wait until the whole window is realised? */ + gtk_widget_realize (GTK_WIDGET (emb->bvw)); + gtk_widget_show (GTK_WIDGET (emb->bvw)); + } + + emb->seek = glade_xml_get_widget (emb->xml, "time_hscale"); + emb->seekadj = gtk_range_get_adjustment (GTK_RANGE (emb->seek)); + g_signal_connect (emb->seek, "button-press-event", + G_CALLBACK (on_seek_start), emb); + g_signal_connect (emb->seek, "button-release-event", + G_CALLBACK (cb_on_seek), emb); + + pp_button = glade_xml_get_widget (emb->xml, "pp_button"); + g_signal_connect (G_OBJECT (pp_button), "clicked", + G_CALLBACK (on_play_pause), emb); + + gc = gconf_client_get_default (); + volume = gconf_client_get_int (gc, GCONF_PREFIX"/volume", NULL); + g_object_unref (G_OBJECT (gc)); + + bacon_video_widget_set_volume (emb->bvw, volume); + vbut = glade_xml_get_widget (emb->xml, "volume_button"); + bacon_volume_button_set_value (BACON_VOLUME_BUTTON (vbut), volume); + g_signal_connect (G_OBJECT (vbut), "value-changed", + G_CALLBACK (cb_vol), emb); + gtk_widget_add_events (vbut, GDK_SCROLL_MASK); + g_signal_connect (G_OBJECT (vbut), "scroll_event", + G_CALLBACK (on_volume_scroll_event), emb); + + emb->statusbar = TOTEM_STATUSBAR (glade_xml_get_widget (emb->xml, "statusbar")); + + gtk_widget_set_size_request (emb->window, width, height); + +#if GNOME_ENABLE_DEBUG + child = glade_xml_get_widget (emb->xml, "controls"); + g_signal_connect_after (child, "size-allocate", G_CALLBACK (controls_size_allocate_cb), NULL); +#endif + + if (emb->controller_hidden != FALSE) { + child = glade_xml_get_widget (emb->xml, "controls"); + gtk_widget_hide (child); + } + + if (!emb->show_statusbar) { + child = glade_xml_get_widget (emb->xml, "statusbar"); + gtk_widget_hide (child); + } + + /* Try to make controls smaller */ + { + GtkRcStyle *rcstyle; + + rcstyle = gtk_rc_style_new (); + rcstyle->xthickness = rcstyle->ythickness = 0; + + child = glade_xml_get_widget (emb->xml, "pp_button"); + gtk_widget_modify_style (child, rcstyle); + + child = glade_xml_get_widget (emb->xml, "time_hscale"); + gtk_widget_modify_style (child, rcstyle); + + child = glade_xml_get_widget (emb->xml, "volume_button"); + gtk_widget_modify_style (child, rcstyle); + + gtk_rc_style_unref (rcstyle); + } + + totem_embedded_set_state (emb, STATE_STOPPED); + + if (!emb->hidden) { + gtk_widget_show (emb->window); + } + + /* popup */ + emb->menuxml = totem_interface_load_with_root ("mozilla-viewer.glade", + "menu", _("Menu"), TRUE, + GTK_WINDOW (emb->window)); + g_assert (emb->menuxml); + + child = glade_xml_get_widget (emb->menuxml, "about1"); + g_signal_connect (G_OBJECT (child), "activate", + G_CALLBACK (on_about1_activate), emb); + child = glade_xml_get_widget (emb->menuxml, "copy_location1"); + g_signal_connect (G_OBJECT (child), "activate", + G_CALLBACK (on_copy_location1_activate), emb); + child = glade_xml_get_widget (emb->menuxml, "preferences1"); + gtk_widget_hide (child); + + /* Create cursor */ + /* FIXME: this looks hideous! */ + if (!emb->hidden) { + emb->cursor = gdk_cursor_new_for_display (gtk_widget_get_display (emb->window), + GDK_HAND1); + } + + return TRUE; +} + +static gboolean +totem_embedded_construct_placeholder (TotemEmbedded *emb, + GdkNativeWindow xid, + int width, + int height) +{ + GtkWidget *window, *label; + + if (xid == 0) + return TRUE; + + window = gtk_plug_new (xid); + /* FIXME why? */ + gtk_widget_realize (window); + + label = gtk_label_new ("Not yet supported"); + gtk_container_add (GTK_CONTAINER (window), label); + + gtk_widget_show_all (window); + + return TRUE; +} + +static gboolean +totem_embedded_set_window (TotemEmbedded *embedded, + const char *controls, + guint window, + int width, + int height, + GError **error) +{ + g_print ("Viewer: SetWindow XID %u size %d:%d\n", window, width, height); + + if (embedded->type == TOTEM_PLUGIN_TYPE_COMPLEX) { + /* FIXME!!! */ + if (strcmp (controls, "All") != 0 && + strcmp (controls, "ImageWindow") != 0) { + totem_embedded_construct_placeholder (embedded, (GdkNativeWindow) window, width, height); + return TRUE; + } + } else { + if (strcmp (controls, "All") != 0 && + strcmp (controls, "ImageWindow") != 0) { + g_set_error (error, + TOTEM_EMBEDDED_ERROR_QUARK, + TOTEM_EMBEDDED_SETWINDOW_UNSUPPORTED_CONTROLS, + "Unsupported controls '%s'", controls); + return FALSE; + } + } + + if (embedded->window != NULL) { + g_warning ("Viewer: Already have a window!"); + + g_set_error (error, + TOTEM_EMBEDDED_ERROR_QUARK, + TOTEM_EMBEDDED_SETWINDOW_HAVE_WINDOW, + "Already have a window"); + return FALSE; + } + + if (window == 0) { + g_set_error (error, + TOTEM_EMBEDDED_ERROR_QUARK, + TOTEM_EMBEDDED_SETWINDOW_INVALID_XID, + "Invalid XID"); + return FALSE; + } + + embedded->width = width; + embedded->height = height; + + totem_embedded_construct (embedded, (GdkNativeWindow) window, + width, height); + + return TRUE; +} + +static gboolean +totem_embedded_unset_window (TotemEmbedded *embedded, + guint window, + GError **error) +{ + g_warning ("UnsetWindow unimplemented"); + return TRUE; +} + +static void +totem_embedded_clear_playlist (TotemEmbedded *embedded) +{ + g_list_foreach (embedded->playlist, (GFunc) g_free, NULL); + g_list_free (embedded->playlist); + + embedded->playlist = NULL; + embedded->current = NULL; + embedded->num_items = 0; +} + +static void +entry_added (TotemPlParser *parser, + const char *uri, + const char *title, + const char *genre, + gpointer data) +{ + GList **list = (GList **) data; + + g_print ("added URI '%s' with title '%s' genre '%s'\n", uri, + title ? title : "empty", genre); + + //FIXMEchpe + //FIXME need new struct to hold that + *list = g_list_prepend (*list, g_strdup (uri)); +} + +static gboolean +totem_embedded_push_parser (gpointer data) +{ + TotemEmbedded *emb = (TotemEmbedded *) data; + TotemPlParser *parser; + TotemPlParserResult res; + GList *list = NULL; + + emb->parser_id = 0; + + totem_embedded_clear_playlist (emb); + + parser = totem_pl_parser_new (); + g_object_set (parser, "force", TRUE, + "disable-unsafe", TRUE, + NULL); + g_signal_connect (parser, "entry", G_CALLBACK (entry_added), &list); + res = totem_pl_parser_parse (parser, emb->current_uri, FALSE); /* FIXME base_uri? */ + g_object_unref (parser); + + if (res != TOTEM_PL_PARSER_RESULT_SUCCESS) { + //FIXME show a proper error message + switch (res) { + case TOTEM_PL_PARSER_RESULT_UNHANDLED: + g_print ("url '%s' unhandled\n", emb->current_uri); + break; + case TOTEM_PL_PARSER_RESULT_ERROR: + g_print ("error handling url '%s'\n", emb->current_uri); + break; + case TOTEM_PL_PARSER_RESULT_IGNORED: + g_print ("ignored url '%s'\n", emb->current_uri); + break; + default: + g_assert_not_reached (); + ;; + } + } + + /* Check if we have anything in the playlist now */ + if (list == NULL) { + g_message ("No playlist or playlist empty"); + totem_embedded_set_error (emb, _("No playlist or playlist empty") /* FIXME */, + NULL); + return FALSE; + } + + emb->playlist = g_list_reverse (list); + emb->num_items = g_list_length (emb->playlist); + + totem_embedded_open_playlist_item (emb, NULL /* first */); + + /* don't run again */ + return FALSE; +} + +G_MODULE_EXPORT GtkWidget * +totem_volume_create (void) +{ + GtkWidget *widget; + + widget = bacon_volume_button_new (GTK_ICON_SIZE_MENU, + 0, 100, -1); + gtk_button_set_focus_on_click (GTK_BUTTON (widget), FALSE); + gtk_widget_show (widget); + + return widget; +} + +G_MODULE_EXPORT GtkWidget * +totem_statusbar_create (void) +{ + GtkWidget *widget; + + widget = totem_statusbar_new (); + totem_statusbar_set_has_resize_grip (TOTEM_STATUSBAR (widget), FALSE); + gtk_widget_show (widget); + + return widget; +} + +static char *arg_user_agent = NULL; +static char *arg_mime_type = NULL; +static char **arg_remaining = NULL; +static gboolean arg_no_controls = FALSE; +static gboolean arg_statusbar = FALSE; +static gboolean arg_hidden = FALSE; +static gboolean arg_is_playlist = FALSE; +static gboolean arg_repeat = FALSE; +static gboolean arg_no_autostart = FALSE; +static TotemPluginType arg_plugin_type = TOTEM_PLUGIN_TYPE_LAST; + +static gboolean +parse_plugin_type (const gchar *option_name, + const gchar *value, + gpointer data, + GError **error) +{ + const char types[TOTEM_PLUGIN_TYPE_LAST][12] = { + "basic", + "gmp", + "complex", + "narrowspace", + "mully" + }; + TotemPluginType type; + + for (type = 0; type < TOTEM_PLUGIN_TYPE_LAST; ++type) { + if (strcmp (value, types[type]) == 0) { + arg_plugin_type = type; + return TRUE; + } + } + + g_print ("Unknown plugin type '%s'\n", value); + exit (1); +} + +static GOptionEntry option_entries [] = +{ + { TOTEM_OPTION_PLUGIN_TYPE, 0, 0, G_OPTION_ARG_CALLBACK, parse_plugin_type, NULL, NULL }, + { TOTEM_OPTION_USER_AGENT, 0, 0, G_OPTION_ARG_STRING, &arg_user_agent, NULL, NULL }, + { TOTEM_OPTION_MIMETYPE, 0, 0, G_OPTION_ARG_STRING, &arg_mime_type, NULL, NULL }, + { TOTEM_OPTION_CONTROLS_HIDDEN, 0, 0, G_OPTION_ARG_NONE, &arg_no_controls, NULL, NULL }, + { TOTEM_OPTION_STATUSBAR, 0, 0, G_OPTION_ARG_NONE, &arg_statusbar, NULL, NULL }, + { TOTEM_OPTION_HIDDEN, 0, 0, G_OPTION_ARG_NONE, &arg_hidden, NULL, NULL }, + { TOTEM_OPTION_PLAYLIST, 0, 0, G_OPTION_ARG_NONE, &arg_is_playlist, NULL, NULL }, + { TOTEM_OPTION_REPEAT, 0, 0, G_OPTION_ARG_NONE, &arg_repeat, NULL, NULL }, + { TOTEM_OPTION_NOAUTOSTART, 0, 0, G_OPTION_ARG_NONE, &arg_no_autostart, NULL, NULL }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY /* STRING? */, &arg_remaining, NULL }, + { NULL } +}; + +#include "totem-plugin-viewer-interface.h" + +int main (int argc, char **argv) +{ + TotemEmbedded *emb; + DBusGProxy *proxy; + DBusGConnection *conn; + guint res; + GError *e = NULL; + char svcname[256]; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + +#ifdef GNOME_ENABLE_DEBUG + { + int i; + g_print ("Viewer: PID %d, args: ", getpid ()); + for (i = 0; i < argc; i++) + g_print ("%s ", argv[i]); + g_print ("\n"); + } +#endif + + if (XInitThreads () == 0) + { + gtk_init (&argc, &argv); + totem_embedded_error_and_exit (_("Could not initialise the thread-safe libraries."), _("Verify your system installation. The Totem plugin will now exit."), NULL); + } + + g_thread_init (NULL); + dbus_g_thread_init (); + +#ifdef GNOME_ENABLE_DEBUG +{ + const char *env; + + env = g_getenv ("TOTEM_EMBEDDED_GDB"); + if (env && g_ascii_strtoull (env, NULL, 10) == 1) { + char *gdbargv[6]; + char pid[32]; + GError *gdberr = NULL; + int gdbargc = 0; + + g_snprintf (pid, sizeof (pid), "%d", getpid ()); + + gdbargv[gdbargc++] = "/usr/bin/xterm"; + gdbargv[gdbargc++] = "-e"; + gdbargv[gdbargc++] = "gdb"; + gdbargv[gdbargc++] = argv[0]; + gdbargv[gdbargc++] = pid; + gdbargv[gdbargc++] = NULL; + + if (!g_spawn_async (NULL, + gdbargv, + NULL /* env */, + 0, + NULL, NULL, + NULL, + &gdberr)) { + g_warning ("Failed to spawn debugger: %s", gdberr->message); + g_error_free (gdberr); + } else { + g_print ("Sleeping....\n"); + g_usleep (10* 1000 * 1000); /* 10s */ + } + } +} +#endif + + if (!gtk_init_with_args (&argc, &argv, NULL, option_entries, GETTEXT_PACKAGE, &e)) + { + g_print ("%s\n", e->message); + g_error_free (e); + exit (1); + } + + // FIXME check that ALL necessary params were given! + if (arg_plugin_type == TOTEM_PLUGIN_TYPE_LAST) { + g_warning ("Plugin type is required\n"); + exit (1); + } + + /* FIXME: check the UA strings of the legacy plugins themselves */ + /* FIXME: at least hxplayer seems to send different UAs depending on the protocol!? */ + if (arg_user_agent != NULL) { + g_setenv ("GNOME_VFS_HTTP_USER_AGENT", arg_user_agent, TRUE); + g_free (arg_user_agent); + arg_user_agent = NULL; + } + + bacon_video_widget_init_backend (NULL, NULL); + gnome_vfs_init (); + + dbus_g_object_type_install_info (TOTEM_TYPE_EMBEDDED, + &dbus_glib_totem_embedded_object_info); + g_snprintf (svcname, sizeof (svcname), + TOTEM_PLUGIN_VIEWER_NAME_TEMPLATE, getpid()); + + if (!(conn = dbus_g_bus_get (DBUS_BUS_SESSION, &e))) { + g_warning ("Failed to get DBUS connection: %s", e->message); + g_error_free (e); + exit (1); + } + + proxy = dbus_g_proxy_new_for_name (conn, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + g_assert (proxy != NULL); + + if (!dbus_g_proxy_call (proxy, "RequestName", &e, + G_TYPE_STRING, svcname, + G_TYPE_UINT, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT, + G_TYPE_INVALID, + G_TYPE_UINT, &res, + G_TYPE_INVALID)) { + g_warning ("RequestName for '%s'' failed: %s\n", + svcname, e->message); + g_error_free (e); + + exit (1); + } + + emb = g_object_new (TOTEM_TYPE_EMBEDDED, NULL); + + emb->state = LAST_STATE; + emb->width = -1; + emb->height = -1; + emb->controller_hidden = arg_no_controls; + emb->show_statusbar = arg_statusbar; + emb->current_uri = arg_remaining ? arg_remaining[0] : NULL; + emb->mimetype = arg_mime_type; + emb->hidden = arg_hidden; + emb->is_playlist = arg_is_playlist; + emb->repeat = arg_repeat; + emb->autostart = !arg_no_autostart; + emb->type = arg_plugin_type; + + /* FIXME: register this BEFORE requesting the service name? */ + dbus_g_connection_register_g_object (conn, TOTEM_PLUGIN_VIEWER_DBUS_PATH, + G_OBJECT (emb)); + + /* If we're hidden, construct a hidden window; + * else wait to be plugged in. + */ + if (emb->hidden) { + totem_embedded_construct (emb, 0, -1, -1); + } + + gtk_main (); + + g_object_unref (emb); + + g_message ("Viewer [PID %d]: exiting\n", getpid ()); + + return 0; +} diff --git a/browser-plugin/totemBasicPlugin.cpp b/browser-plugin/totemBasicPlugin.cpp index 1704784be..790a6cd7e 100644 --- a/browser-plugin/totemBasicPlugin.cpp +++ b/browser-plugin/totemBasicPlugin.cpp @@ -183,7 +183,7 @@ totemScriptablePlugin::Play () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Play (); + return mPlugin->DoCommand (TOTEM_COMMAND_PLAY); } NS_IMETHODIMP @@ -191,7 +191,7 @@ totemScriptablePlugin::Rewind () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Pause (); + return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE); } NS_IMETHODIMP @@ -199,5 +199,5 @@ totemScriptablePlugin::Stop () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Stop (); + return mPlugin->DoCommand (TOTEM_COMMAND_STOP); } diff --git a/browser-plugin/totemBasicPlugin.h b/browser-plugin/totemBasicPlugin.h index 57b277773..dbc15c7b6 100644 --- a/browser-plugin/totemBasicPlugin.h +++ b/browser-plugin/totemBasicPlugin.h @@ -40,7 +40,7 @@ class totemScriptablePlugin : public totemIBasicPlayer, totemScriptablePlugin (totemPlugin *aPlugin); PRBool IsValid () { return mPlugin != nsnull; } - void UnsetPlugin () { mPlugin = nsnull; } + void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; } static char *PluginDescription (); static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *); diff --git a/browser-plugin/totemComplexPlugin.cpp b/browser-plugin/totemComplexPlugin.cpp index b1db7fec6..70de3c2db 100644 --- a/browser-plugin/totemComplexPlugin.cpp +++ b/browser-plugin/totemComplexPlugin.cpp @@ -181,7 +181,7 @@ totemScriptablePlugin::DoPause (PRBool *_retval) { NS_ENSURE_STATE (IsValid ()); - nsresult rv = mPlugin->Pause (); + nsresult rv = mPlugin->DoCommand (TOTEM_COMMAND_PAUSE); *_retval = PR_TRUE; return rv; @@ -192,7 +192,7 @@ totemScriptablePlugin::DoPlay (PRBool *_retval) { NS_ENSURE_STATE (IsValid ()); - nsresult rv = mPlugin->Play (); + nsresult rv = mPlugin->DoCommand (TOTEM_COMMAND_PLAY); *_retval = PR_TRUE; return rv; @@ -203,7 +203,7 @@ totemScriptablePlugin::DoStop (PRBool *_retval) { NS_ENSURE_STATE (IsValid ()); - nsresult rv = mPlugin->Stop (); + nsresult rv = mPlugin->DoCommand (TOTEM_COMMAND_STOP); *_retval = PR_TRUE; return rv; diff --git a/browser-plugin/totemComplexPlugin.h b/browser-plugin/totemComplexPlugin.h index 8d842e823..7fa46a0de 100644 --- a/browser-plugin/totemComplexPlugin.h +++ b/browser-plugin/totemComplexPlugin.h @@ -40,7 +40,7 @@ class totemScriptablePlugin : public totemIComplexPlayer, totemScriptablePlugin (totemPlugin *aPlugin); PRBool IsValid () { return mPlugin != nsnull; } - void UnsetPlugin () { mPlugin = nsnull; } + void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; } static char *PluginDescription (); static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *); diff --git a/browser-plugin/totemGMPPlugin.cpp b/browser-plugin/totemGMPPlugin.cpp index ea95e5b7a..29b31c868 100644 --- a/browser-plugin/totemGMPPlugin.cpp +++ b/browser-plugin/totemGMPPlugin.cpp @@ -208,7 +208,7 @@ totemScriptablePlugin::Pause () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Pause (); + return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE); } NS_IMETHODIMP @@ -216,7 +216,7 @@ totemScriptablePlugin::Play () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Play (); + return mPlugin->DoCommand (TOTEM_COMMAND_PLAY); } NS_IMETHODIMP @@ -224,5 +224,5 @@ totemScriptablePlugin::Stop () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Stop (); + return mPlugin->DoCommand (TOTEM_COMMAND_STOP); } diff --git a/browser-plugin/totemGMPPlugin.h b/browser-plugin/totemGMPPlugin.h index f2a530d42..8a54fe4e2 100644 --- a/browser-plugin/totemGMPPlugin.h +++ b/browser-plugin/totemGMPPlugin.h @@ -44,7 +44,7 @@ class totemScriptablePlugin : public totemIGMPPlayer, totemScriptablePlugin (totemPlugin *aPlugin); PRBool IsValid () { return mPlugin != nsnull; } - void UnsetPlugin () { mPlugin = nsnull; } + void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; } static char *PluginDescription (); static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *); diff --git a/browser-plugin/totemMullYPlugin.cpp b/browser-plugin/totemMullYPlugin.cpp index a60aad89d..dbd58588e 100644 --- a/browser-plugin/totemMullYPlugin.cpp +++ b/browser-plugin/totemMullYPlugin.cpp @@ -179,7 +179,7 @@ totemScriptablePlugin::Rewind () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Pause (); + return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE); } NS_IMETHODIMP @@ -187,7 +187,7 @@ totemScriptablePlugin::Play () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Play (); + return mPlugin->DoCommand (TOTEM_COMMAND_PLAY); } NS_IMETHODIMP @@ -195,5 +195,5 @@ totemScriptablePlugin::Stop () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Stop (); + return mPlugin->DoCommand (TOTEM_COMMAND_STOP); } diff --git a/browser-plugin/totemMullYPlugin.h b/browser-plugin/totemMullYPlugin.h index 8117531f1..476523e3c 100644 --- a/browser-plugin/totemMullYPlugin.h +++ b/browser-plugin/totemMullYPlugin.h @@ -41,7 +41,7 @@ class totemScriptablePlugin : public totemIMullYPlayer, totemScriptablePlugin (totemPlugin *aPlugin); PRBool IsValid () { return mPlugin != nsnull; } - void UnsetPlugin () { mPlugin = nsnull; } + void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; } static char *PluginDescription (); static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *); diff --git a/browser-plugin/totemNarrowSpacePlugin.cpp b/browser-plugin/totemNarrowSpacePlugin.cpp index 9275b8bdc..2c22e45ac 100644 --- a/browser-plugin/totemNarrowSpacePlugin.cpp +++ b/browser-plugin/totemNarrowSpacePlugin.cpp @@ -181,7 +181,7 @@ totemScriptablePlugin::Play () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Play (); + return mPlugin->DoCommand (TOTEM_COMMAND_PLAY); } NS_IMETHODIMP @@ -189,7 +189,7 @@ totemScriptablePlugin::Rewind () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Pause (); + return mPlugin->DoCommand (TOTEM_COMMAND_PAUSE); } NS_IMETHODIMP @@ -197,5 +197,5 @@ totemScriptablePlugin::Stop () { NS_ENSURE_STATE (IsValid ()); - return mPlugin->Stop (); + return mPlugin->DoCommand (TOTEM_COMMAND_STOP); } diff --git a/browser-plugin/totemNarrowSpacePlugin.h b/browser-plugin/totemNarrowSpacePlugin.h index 0028a2cf8..840e9c47b 100644 --- a/browser-plugin/totemNarrowSpacePlugin.h +++ b/browser-plugin/totemNarrowSpacePlugin.h @@ -40,7 +40,7 @@ class totemScriptablePlugin : public totemINarrowSpacePlayer, totemScriptablePlugin (totemPlugin *aPlugin); PRBool IsValid () { return mPlugin != nsnull; } - void UnsetPlugin () { mPlugin = nsnull; } + void SetPlugin (totemPlugin *aPlugin) { mPlugin = aPlugin; } static char *PluginDescription (); static void PluginMimeTypes (const totemPluginMimeEntry **, PRUint32 *); diff --git a/browser-plugin/totemPlugin.cpp b/browser-plugin/totemPlugin.cpp index e55a7bf21..2661b6b25 100644 --- a/browser-plugin/totemPlugin.cpp +++ b/browser-plugin/totemPlugin.cpp @@ -1,8 +1,8 @@ /* Totem Mozilla plugin * - * Copyright (C) 2004-2006 Bastien Nocera <hadess@hadess.net> - * Copyright (C) 2002 David A. Schleef <ds@schleef.org> - * Copyright (C) 2006 Christian Persch + * Copyright © 2004-2006 Bastien Nocera <hadess@hadess.net> + * Copyright © 2002 David A. Schleef <ds@schleef.org> + * Copyright © 2006 Christian Persch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -37,7 +37,7 @@ #include <libgnomevfs/gnome-vfs-mime-utils.h> #include "totem-pl-parser-mini.h" -#include "totem-mozilla-options.h" +#include "totem-plugin-viewer-options.h" #include "npapi.h" #include "npupp.h" @@ -49,10 +49,27 @@ #include <nsIInterfaceRequestorUtils.h> #include <nsIWebNavigation.h> +#include <nsIServiceManager.h> +#include <nsIDOMDocument.h> +#include <nsIDOMElement.h> +#include <nsIDOM3Node.h> +#include <nsIIOService.h> +#include <nsIURI.h> +#include <nsITimer.h> +#include <nsIComponentManager.h> +#include <nsIServiceManager.h> + +/* for NS_IOSERVICE_CONTRACTID */ +#include <nsNetCID.h> + #define GNOME_ENABLE_DEBUG 1 /* define GNOME_ENABLE_DEBUG for more debug spew */ +/* FIXME define D() so that it prints the |this| pointer, so we can differentiate between different concurrent plugins! */ #include "debug.h" +// really noisy debug +#define DD(args...) + #include "totemPluginGlue.h" #include "totemPlugin.h" @@ -61,102 +78,672 @@ /* How much data bytes to request */ #define PLUGIN_STREAM_CHUNK_SIZE (8 * 1024) -extern NPNetscapeFuncs sMozillaFuncs; +NPNetscapeFuncs totemPlugin::sNPN; + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) +nsTArray<totemPlugin*> *totemPlugin::sPlugins; + +/* Keep the same order as totemPlugin::Control enum! */ +static const char *kControl[] = { + "All", + "ControlPanel", + "FFCtrl", + "HomeCtrl", + "ImageWindow", + "InfoPanel", + "InfoVolumePanel", + "MuteCtrl", + "MuteVolume", + "PauseButton", + "PlayButton", + "PlayOnlyButton", + "PositionField", + "PositionSlider", + "RWCtrl", + "StatusBar", + "StatusField", + "StopButton", + "TACCtrl", + "VolumeSlider", +}; +#endif /* TOTEM_COMPLEX_PLUGIN */ void* totemPlugin::operator new (size_t aSize) CPP_THROW_NEW { - void *object = ::operator new (aSize); - if (object) { - memset (object, 0, aSize); - } + void *object = ::operator new (aSize); + if (object) { + memset (object, 0, aSize); + } - return object; + return object; } totemPlugin::totemPlugin (NPP aInstance) -: mInstance(aInstance), - mWidth(-1), - mHeight(-1), - mSendFD(-1) +: mInstance (aInstance), + mWidth (-1), + mHeight (-1), + mViewerFD (-1), +#ifdef TOTEM_COMPLEX_PLUGIN + mAutostart (PR_FALSE), +#else + mAutostart (PR_TRUE), +#endif /* TOTEM_NARROWSPACE_PLUGIN */ + mNeedViewer (PR_TRUE) { - D ("totemPlugin ctor [%p]", (void*) this); + D ("totemPlugin ctor [%p]", (void*) this); + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + /* Add |this| to the global plugins list */ + NS_ASSERTION (sPlugins->IndexOf (this) == NoIndex, "WTF?"); + if (!sPlugins->AppendElement (this)) { + D ("Couldn't maintain plugin list!"); + } +#endif /* TOTEM_COMPLEX_PLUGIN */ } totemPlugin::~totemPlugin () { - if (mScriptable) { - mScriptable->UnsetPlugin (); - NS_RELEASE (mScriptable); - } - - if (mSendFD >= 0) { - close (mSendFD); - mSendFD = -1; - } - - if (mPlayerPID) { - kill (mPlayerPID, SIGKILL); - g_spawn_close_pid (mPlayerPID); - mPlayerPID = 0; - } - - g_free (mLocal); - g_free (mTarget); - g_free (mSrc); - g_free (mHref); - - if (mProxy) - g_object_unref (mProxy); - - D ("totemPlugin dtor [%p]", (void*) this); +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + /* Remove us from the plugins list */ + NS_ASSERTION (sPlugins->IndexOf (this) != NoIndex, "WTF?"); + sPlugins->RemoveElement (this); + + TransferConsole (); +#endif /* TOTEM_COMPLEX_PLUGIN */ + + if (mScriptable) { + mScriptable->SetPlugin (nsnull); + NS_RELEASE (mScriptable); + mScriptable = nsnull; + } + + if (mBusProxy) { + dbus_g_proxy_disconnect_signal (mBusProxy, + "NameOwnerChanged", + G_CALLBACK (NameOwnerChangedCallback), + NS_REINTERPRET_CAST (void*, this)); + g_object_unref (mBusProxy); + mBusProxy = NULL; + } + + ViewerCleanup (); + + if (mTimer) { + mTimer->Cancel (); + NS_RELEASE (mTimer); + mTimer = nsnull; + } + + NS_IF_RELEASE (mServiceManager); + NS_IF_RELEASE (mIOService); + NS_IF_RELEASE (mPluginDOMElement); + NS_IF_RELEASE (mBaseURI); + NS_IF_RELEASE (mRequestBaseURI); + NS_IF_RELEASE (mRequestURI); + NS_IF_RELEASE (mSrcURI); + +#ifdef TOTEM_GMP_PLUGIN + NS_IF_RELEASE (mURLURI); +#endif + +#ifdef TOTEM_NARROWSPACE_PLUGIN + NS_IF_RELEASE (mHrefURI); + NS_IF_RELEASE (mQtsrcURI); +#endif + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + NS_IF_RELEASE (mPluginOwnerDocument); +#endif /* TOTEM_COMPLEX_PLUGIN */ + + D ("totemPlugin dtor [%p]", (void*) this); } /* public functions */ nsresult -totemPlugin::Play () +totemPlugin::DoCommand (const char *aCommand) { - D ("play"); + D ("DoCommand '%s'", aCommand); - if (!mGotSvc) + /* FIXME: queue the action instead */ + if (!mViewerReady) return NS_OK; - NS_ASSERTION (mProxy, "No DBUS proxy!"); - dbus_g_proxy_call_no_reply(mProxy, "Play", - G_TYPE_INVALID, G_TYPE_INVALID); + NS_ASSERTION (mViewerProxy, "No viewer proxy"); + dbus_g_proxy_call_no_reply (mViewerProxy, + "DoCommand", + G_TYPE_STRING, aCommand, + G_TYPE_INVALID, + G_TYPE_INVALID); return NS_OK; } -nsresult -totemPlugin::Stop () +/* Viewer interaction */ + +NPError +totemPlugin::ViewerFork () { - D ("stop"); +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + /* Don't fork a viewer if we're going to use another one */ + if (!mNeedViewer) + return NPERR_NO_ERROR; +#endif /* TOTEM_COMPLEX_PLUGIN */ + + const char *userAgent = CallNPN_UserAgentProc (sNPN.uagent, + mInstance); + if (!userAgent) { + /* See https://bugzilla.mozilla.org/show_bug.cgi?id=328778 */ + D ("User agent has more than 127 characters; fix your browser!"); + } - if (!mGotSvc) - return NS_OK; + GPtrArray *arr = g_ptr_array_new (); + /* FIXME! no need to strdup, all args are const! */ - NS_ASSERTION (mProxy, "No DBUS proxy!"); - dbus_g_proxy_call_no_reply (mProxy, "Stop", - G_TYPE_INVALID, G_TYPE_INVALID); + /* FIXME what use is this anyway? */ +#ifdef TOTEM_RUN_IN_SOURCE_TREE + if (g_file_test ("../src/totem-plugin-viewer", + G_FILE_TEST_EXISTS) != FALSE) { + g_ptr_array_add (arr, g_strdup ("../src/totem-plugin-viewer")); + } else { + g_ptr_array_add (arr, + g_build_filename (LIBEXECDIR, "totem-plugin-viewer", NULL)); + } +#else + g_ptr_array_add (arr, + g_build_filename (LIBEXECDIR, "totem-plugin-viewer", NULL)); +#endif - return NS_OK; + /* So we can debug X errors in the viewer */ + const char *sync = g_getenv ("TOTEM_EMBEDDED_DEBUG_SYNC"); + if (sync && sync[0] == '1') { + g_ptr_array_add (arr, g_strdup ("--sync")); + } + + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_PLUGIN_TYPE)); +#if defined(TOTEM_BASIC_PLUGIN) + g_ptr_array_add (arr, g_strdup ("basic")); +#elif defined(TOTEM_GMP_PLUGIN) + g_ptr_array_add (arr, g_strdup ("gmp")); +#elif defined(TOTEM_COMPLEX_PLUGIN) + g_ptr_array_add (arr, g_strdup ("complex")); +#elif defined(TOTEM_NARROWSPACE_PLUGIN) + g_ptr_array_add (arr, g_strdup ("narrowspace")); +#elif defined(TOTEM_MULLY_PLUGIN) + g_ptr_array_add (arr, g_strdup ("mully")); +#else +#error Unknown plugin type +#endif + + if (userAgent) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_USER_AGENT)); + g_ptr_array_add (arr, g_strdup (userAgent)); + } + + /* FIXME: remove this */ + if (!mMimeType.IsEmpty ()) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_MIMETYPE)); + g_ptr_array_add (arr, g_strdup (mMimeType.get())); + } + + if (mControllerHidden) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_CONTROLS_HIDDEN)); + } + + if (mShowStatusbar) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_STATUSBAR)); + } + + if (mHidden) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HIDDEN)); + } + + if (mRepeat) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_REPEAT)); + } + + if (!mAutostart) { + g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_NOAUTOSTART)); + } + + g_ptr_array_add (arr, NULL); + char **argv = (char **) g_ptr_array_free (arr, FALSE); + +#ifdef GNOME_ENABLE_DEBUG + { + GString *s; + int i; + + s = g_string_new ("Launching: "); + for (i = 0; argv[i] != NULL; i++) { + g_string_append (s, argv[i]); + g_string_append (s, " "); + } + g_string_append (s, "\n"); + D("%s", s->str); + g_string_free (s, TRUE); + } +#endif + + mViewerReady = PR_FALSE; + + /* Don't wait forever! */ + const PRUint32 kViewerTimeout = 30 * 1000; /* ms */ + nsresult rv = mTimer->InitWithFuncCallback (ViewerForkTimeoutCallback, + NS_REINTERPRET_CAST (void*, this), + kViewerTimeout, + nsITimer::TYPE_ONE_SHOT); + if (NS_FAILED (rv)) { + D ("Failed to initialise timer"); + return NPERR_GENERIC_ERROR; + } + + /* FIXME: once gecko is multihead-safe, this should use gdk_spawn_on_screen_with_pipes */ + GError *error = NULL; + if (g_spawn_async_with_pipes (NULL /* working directory FIXME: use $TMPDIR ? */, + argv, + NULL /* environment */, + GSpawnFlags(0), + NULL /* child setup func */, NULL, + &mViewerPID, + &mViewerFD, NULL, NULL, + &error) == FALSE) + { + g_warning ("Failed to spawn viewer: %s", error->message); + g_error_free(error); + + g_strfreev (argv); + + return NPERR_GENERIC_ERROR; + } + + g_strfreev (argv); + + D("Viewer spawned, PID %d", mViewerPID); + + /* FIXME: can this happen? */ + if (mViewerFD < 0) { + ViewerCleanup (); + return NPERR_GENERIC_ERROR; + } + + /* Set mViewerFD nonblocking */ + fcntl (mViewerFD, F_SETFL, O_NONBLOCK); + + return NPERR_NO_ERROR; } -nsresult -totemPlugin::Pause () +void +totemPlugin::ViewerSetup () { - D ("pause"); + /* already set up */ + if (mViewerSetUp) + return; - if (!mGotSvc) - return NS_OK; + mViewerSetUp = PR_TRUE; - NS_ASSERTION (mProxy, "No DBUS proxy!"); - dbus_g_proxy_call_no_reply (mProxy, "Pause", - G_TYPE_INVALID, G_TYPE_INVALID); + D ("ViewerSetup"); - return NS_OK; + /* Cancel timeout */ + nsresult rv = mTimer->Cancel (); + if (NS_FAILED (rv)) { + D ("Failed to cancel timer"); + } + + mViewerProxy = dbus_g_proxy_new_for_name (mBusConnection, + mViewerServiceName.get (), + TOTEM_PLUGIN_VIEWER_DBUS_PATH, + TOTEM_PLUGIN_VIEWER_INTERFACE_NAME); + + dbus_g_proxy_add_signal (mViewerProxy, "ButtonPress", + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (mViewerProxy, + "ButtonPress", + G_CALLBACK (ButtonPressCallback), + NS_REINTERPRET_CAST (void*, this), + NULL); + + dbus_g_proxy_add_signal (mViewerProxy, + "StopStream", + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (mViewerProxy, + "StopStream", + G_CALLBACK (StopStreamCallback), + NS_REINTERPRET_CAST (void*, this), + NULL); + + if (mHidden) { + ViewerReady (); + } else { + ViewerSetWindow (); + } + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + /* Notify all consoles */ + PRUint32 count = sPlugins->Length (); + for (PRUint32 i = 0; i < count; ++i) { + totemPlugin *plugin = sPlugins->ElementAt (i); + + if (plugin->mConsoleClassRepresentant == this) + plugin->UnownedViewerSetup (); + } +#endif /* TOTEM_COMPLEX_PLUGIN */ +} + + +void +totemPlugin::ViewerCleanup () +{ + mViewerBusAddress.SetLength (0); + mViewerServiceName.SetLength (0); + + if (mViewerPendingCall) { + dbus_g_proxy_cancel_call (mViewerProxy, mViewerPendingCall); + mViewerPendingCall = NULL; + } + + if (mViewerProxy) { + dbus_g_proxy_disconnect_signal (mViewerProxy, + "ButtonPress", + G_CALLBACK (ButtonPressCallback), + NS_REINTERPRET_CAST (void*, this)); + dbus_g_proxy_disconnect_signal (mViewerProxy, + "StopStream", + G_CALLBACK (StopStreamCallback), + NS_REINTERPRET_CAST (void*, this)); + + g_object_unref (mViewerProxy); + mViewerProxy = NULL; + } + + /* FIXME: we shouldn't get this! */ + if (mViewerStream) { + fclose (mViewerStream); + mViewerStream = NULL; + } + + if (mViewerFD >= 0) { + close (mViewerFD); + mViewerFD = -1; + } + + if (mViewerPID) { + kill (mViewerPID, SIGKILL); + g_spawn_close_pid (mViewerPID); + mViewerPID = 0; + } +} + +void +totemPlugin::ViewerSetWindow () +{ + if (mWindowSet || mWindow == 0) + return; + + if (!mViewerProxy) { + D ("No viewer proxy yet, deferring SetWindow"); + return; + } + + /* FIXME this shouldn't happen here */ + if (mHidden) { + mWindowSet = PR_TRUE; + ViewerReady (); + return; + } + + NS_ASSERTION (mViewerPendingCall == NULL, "Have a pending call"); + + D ("Calling SetWindow"); + mViewerPendingCall = + dbus_g_proxy_begin_call (mViewerProxy, + "SetWindow", + ViewerSetWindowCallback, + NS_REINTERPRET_CAST (void*, this), + NULL, +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + G_TYPE_STRING, mControls.get (), +#else + G_TYPE_STRING, "All", +#endif + G_TYPE_UINT, (guint) mWindow, + G_TYPE_INT, (gint) mWidth, + G_TYPE_INT, (gint) mHeight, + G_TYPE_INVALID); + + mWindowSet = PR_TRUE; +} + +void +totemPlugin::ViewerReady () +{ + D ("ViewerReady"); + + NS_ASSERTION (!mViewerReady, "Viewer already ready"); + + mViewerReady = PR_TRUE; + + if (mAutostart) { + RequestStream (); + } else { + mWaitingForButtonPress = PR_TRUE; + } +} + +void +totemPlugin::ViewerButtonPressed () +{ + D ("ButtonPress"); + +#ifdef TOTEM_NARROWSPACE_PLUGIN + /* FIXME set href="" afterwards, so we don't try to launch again when the user clicks again? */ + if (!mHref.IsEmpty () && + !mTarget.IsEmpty ()) { + if (g_ascii_strcasecmp (mTarget.get (), "quicktimeplayer") == 0) { + D ("Launching totem with URL '%s'", mHref.get ()); + LaunchTotem (mHref, 0 /* FIXME */); + return; + } + if (g_ascii_strcasecmp (mTarget.get (), "myself") == 0 || + mTarget.Equals (NS_LITERAL_CSTRING ("_current")) || + mTarget.Equals (NS_LITERAL_CSTRING ("_self"))) { + D ("Opening movie '%s'", mHref.get ()); + /* FIXME this isn't right, we should just create a mHrefURI and instruct to load that one */ + SetQtsrc (mHref); + RequestStream (); + return; + } + + /* Load URL in browser. This will either open a new website, + * or execute some javascript. + */ + nsCString href; + if (mHrefURI) { + mHrefURI->GetSpec (href); + } else { + href = mHref; + } + + if (CallNPN_GetURLProc (sNPN.geturl, + mInstance, + href.get (), + mTarget.get ()) != NPERR_NO_ERROR) { + D ("Failed to launch URL '%s' in browser", mHref.get ()); + } + + return; + } +#endif + + if (!mWaitingForButtonPress) + return; + + mWaitingForButtonPress = PR_FALSE; + + /* Now is the time to start the stream */ + if (!mAutostart && + !mStream) { + RequestStream (); + } +} + +void +totemPlugin::NameOwnerChanged (const char *aName, + const char *aOldOwner, + const char *aNewOwner) +{ + if (!mViewerPID) + return; + + /* Construct viewer interface name */ + if (NS_UNLIKELY (mViewerServiceName.IsEmpty ())) { + char name[256]; + + g_snprintf (name, sizeof (name), + TOTEM_PLUGIN_VIEWER_NAME_TEMPLATE, + mViewerPID); + mViewerServiceName.Assign (name); + + D ("Viewer DBus interface name is '%s'", mViewerServiceName.get ()); + } + + if (!mViewerServiceName.Equals (nsDependentCString (aName))) + return; + + D ("NameOwnerChanged old-owner '%s' new-owner '%s'", aOldOwner, aNewOwner); + + if (aOldOwner[0] == '\0' /* empty */ && + aNewOwner[0] != '\0' /* non-empty */) { + if (mViewerBusAddress.Equals (nsDependentCString (aNewOwner))) { + D ("Already have owner, why are we notified again?"); + } else if (!mViewerBusAddress.IsEmpty ()) { + D ("WTF, new owner!?"); + } else { + /* This is the regular case */ + D ("Viewer now connected to the bus"); + } + + mViewerBusAddress.Assign (aNewOwner); + + ViewerSetup (); + } else if (!mViewerBusAddress.IsEmpty () && + mViewerBusAddress.Equals (nsDependentCString (aOldOwner))) { + D ("Viewer lost connection!"); + + mViewerBusAddress.SetLength (0); /* truncate */ + /* FIXME */ + /* ViewerCleanup () ? */ + /* FIXME if we're not quitting, put up error viewer */ + } + /* FIXME do we really need the lost-connection case? + * We could just disconnect the handler in ViewerSetup + */ +} + +/* Stream handling */ + +void +totemPlugin::ClearRequest () +{ + if (mRequestBaseURI) { + NS_RELEASE (mRequestBaseURI); + mRequestBaseURI = nsnull; + } + if (mRequestURI) { + NS_RELEASE (mRequestURI); + mRequestURI = nsnull; + } +} + +void +totemPlugin::RequestStream () +{ + NS_ASSERTION (mViewerReady, "Viewer not ready"); + + if (mStream) { + D ("Unexpectedly have a stream!"); + /* FIXME cancel existing stream, schedule new timer to try again */ + return; + } + + ClearRequest (); + + /* Now work out which URL to request */ + nsIURI *baseURI = nsnull; + nsIURI *requestURI = nsnull; + +#ifdef TOTEM_GMP_PLUGIN + /* Prefer filename over src */ + if (mURLURI) { + requestURI = mURLURI; + baseURI = mSrcURI; /* FIXME: that correct? */ + } +#endif +#ifdef TOTEM_NARROWSPACE_PLUGIN + /* Prefer qtsrc over src */ + if (mQtsrcURI) { + requestURI = mQtsrcURI; + baseURI = mSrcURI; + } +#if 0 + if (href && !requestURL) { + /* FIXME this looks wrong? any real-world testcase sites around? */ + requestURL = href; + } +#endif +#endif + + /* Fallback */ + if (!requestURI) + requestURI = mSrcURI; + + if (!baseURI) + baseURI = mBaseURI; + + /* Nothing to do */ + if (!requestURI) + return; + + NS_ADDREF (mRequestBaseURI = baseURI); + NS_ADDREF (mRequestURI = requestURI); + + /* FIXME use the right base! */ + nsCString baseSpec, spec; + baseURI->GetSpec (baseSpec); + requestURI->GetSpec (spec); + + /* Shouldn't happen, but who knows */ + if (spec.IsEmpty ()) + return; + + /* If the URL is supported we call OpenStream, and otherwise OpenURI. */ + if (IsSchemeSupported (requestURI)) { + /* FIXME: Purge any buffered output from mViewerFD */ + // __fpurge (mViewerFD); + + mViewerPendingCall = + dbus_g_proxy_begin_call (mViewerProxy, + "OpenStream", + ViewerOpenStreamCallback, + NS_REINTERPRET_CAST (void*, this), + NULL, + G_TYPE_STRING, spec.get (), + G_TYPE_STRING, baseSpec.get (), + G_TYPE_INVALID); + } else { + mViewerPendingCall = + dbus_g_proxy_begin_call (mViewerProxy, + "OpenURI", + ViewerOpenURICallback, + NS_REINTERPRET_CAST (void*, this), + NULL, + G_TYPE_STRING, spec.get (), + G_TYPE_STRING, baseSpec.get (), + G_TYPE_INVALID); + } + + /* FIXME: start playing in the callbacks ! */ } void @@ -165,72 +752,201 @@ totemPlugin::UnsetStream () if (!mStream) return; - if (CallNPN_DestroyStreamProc (sMozillaFuncs.destroystream, + if (CallNPN_DestroyStreamProc (sNPN.destroystream, mInstance, mStream, NPRES_DONE) != NPERR_NO_ERROR) { g_warning ("Couldn't destroy the stream"); + /* FIXME: set mStream to NULL here too? */ return; } - mStream = nsnull; + /* Calling DestroyStream should already have set this to NULL */ + NS_ASSERTION (!mStream, "Should not have a stream anymore"); + mStream = nsnull; /* just to make sure */ } +/* Callbacks */ + /* static */ void PR_CALLBACK totemPlugin::NameOwnerChangedCallback (DBusGProxy *proxy, - const char *svc, - const char *old_owner, - const char *new_owner, - totemPlugin *plugin) + const char *aName, + const char *aOldOwner, + const char *aNewOwner, + void *aData) { - D ("Received notification for '%s' old-owner '%s' new-owner '%s'", - svc, old_owner, new_owner); + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); - if (plugin->mWaitForSvc && - strcmp (svc, plugin->mWaitForSvc) == 0) { - plugin->mGotSvc = TRUE; - } + plugin->NameOwnerChanged (aName, aOldOwner, aNewOwner); } /* static */ void PR_CALLBACK -totemPlugin::StopSendingDataCallback (DBusGProxy *proxy, - totemPlugin *plugin) +totemPlugin::ViewerForkTimeoutCallback (nsITimer *aTimer, + void *aData) { - D("Stop sending data signal received"); + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); + + D ("ViewerForkTimeoutCallback"); + + /* FIXME: can this really happen? */ + NS_ASSERTION (!plugin->mViewerReady, "Viewer ready but timeout running?"); + + plugin->ViewerCleanup (); + /* FIXME start error viewer */ +} + +/* static */ void PR_CALLBACK +totemPlugin::ButtonPressCallback (DBusGProxy *proxy, + //guint aButton, + //guint aTimestamp, + void *aData) +{ + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); + + D ("ButtonPress signal received"); + + plugin->ViewerButtonPressed (); +} + +/* static */ void PR_CALLBACK +totemPlugin::StopStreamCallback (DBusGProxy *proxy, + void *aData) +{ + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); + + D ("StopStream signal received"); - /* FIXME do it from a timer? */ plugin->UnsetStream (); } -char * -totemPlugin::GetRealMimeType (const char *mimetype) +/* static */ void PR_CALLBACK +totemPlugin::ViewerSetWindowCallback (DBusGProxy *aProxy, + DBusGProxyCall *aCall, + void *aData) +{ + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); + + D ("SetWindow reply"); + + NS_ASSERTION (aCall == plugin->mViewerPendingCall, "SetWindow not the current call"); + + plugin->mViewerPendingCall = NULL; + + GError *error = NULL; + if (!dbus_g_proxy_end_call (aProxy, aCall, &error, G_TYPE_INVALID)) { + /* FIXME: mViewerFailed = PR_TRUE */ + g_warning ("SetWindow failed: %s", error->message); + g_error_free (error); + return; + } + + plugin->ViewerReady (); +} + +/* static */ void PR_CALLBACK +totemPlugin::ViewerOpenStreamCallback (DBusGProxy *aProxy, + DBusGProxyCall *aCall, + void *aData) +{ + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); + + D ("OpenStream reply"); + + NS_ASSERTION (aCall == plugin->mViewerPendingCall, "OpenStream not the current call"); + + plugin->mViewerPendingCall = NULL; + + GError *error = NULL; + if (!dbus_g_proxy_end_call (aProxy, aCall, &error, G_TYPE_INVALID)) { + g_warning ("OpenStream failed: %s", error->message); + g_error_free (error); + return; + } + + /* FIXME this isn't the best way... */ + if (plugin->mHidden && + plugin->mAutostart) { + plugin->DoCommand (TOTEM_COMMAND_PLAY); + } + + NS_ASSERTION (!plugin->mExpectingStream, "Already expecting a stream"); + NS_ENSURE_TRUE (plugin->mRequestURI, ); + + plugin->mExpectingStream = PR_TRUE; + + nsCString spec; + plugin->mRequestURI->GetSpec (spec); + + NPError err = CallNPN_GetURLProc (sNPN.geturl, + plugin->mInstance, + spec.get (), + NULL); + if (err != NPERR_NO_ERROR) { + plugin->mExpectingStream = PR_FALSE; + + D ("GetURL '%s' failed with error %d", spec.get (), err); + } +} + +/* static */ void PR_CALLBACK +totemPlugin::ViewerOpenURICallback (DBusGProxy *aProxy, + DBusGProxyCall *aCall, + void *aData) +{ + totemPlugin *plugin = NS_REINTERPRET_CAST (totemPlugin*, aData); + + D ("OpenURI reply"); + + NS_ASSERTION (aCall == plugin->mViewerPendingCall, "OpenURI not the current call"); + + plugin->mViewerPendingCall = NULL; + + GError *error = NULL; + if (!dbus_g_proxy_end_call (aProxy, aCall, &error, G_TYPE_INVALID)) { + g_warning ("OpenURI failed: %s", error->message); + g_error_free (error); + return; + } + + /* FIXME this isn't the best way... */ + if (plugin->mAutostart) { + plugin->DoCommand (TOTEM_COMMAND_PLAY); + } +} + +/* Auxiliary functions */ + +void +totemPlugin::GetRealMimeType (const char *mimetype, + nsACString &_retval) { + _retval.SetLength (0); + const totemPluginMimeEntry *mimetypes; PRUint32 count; - totemScriptablePlugin::PluginMimeTypes (&mimetypes, &count); for (PRUint32 i = 0; i < count; ++i) { if (strcmp (mimetypes[i].mimetype, mimetype) == 0) { - if (mimetypes[i].mime_alias != NULL) - return g_strdup (mimetypes[i].mime_alias); - else - return g_strdup (mimetype); + if (mimetypes[i].mime_alias != NULL) { + _retval.Assign (mimetypes[i].mime_alias); + } else { + _retval.Assign (mimetype); + } + return; } } D ("Real mime-type for '%s' not found", mimetype); - return NULL; } PRBool -totemPlugin::IsMimeTypeSupported (const char *mimetype, const char *url) +totemPlugin::IsMimeTypeSupported (const char *mimetype, + const char *url) { - const totemPluginMimeEntry *mimetypes; - PRUint32 count; - const char *guessed; + NS_ENSURE_TRUE (mimetype, PR_FALSE); -#if defined(TOTEM_MULLY_PLUGIN) || defined (TOTEM_GMP_PLUGIN) +#if defined(TOTEM_MULLY_PLUGIN) || defined(TOTEM_GMP_PLUGIN) /* We can always play those image types */ if (strcmp (mimetype, "image/jpeg") == 0) return PR_TRUE; @@ -242,6 +958,8 @@ totemPlugin::IsMimeTypeSupported (const char *mimetype, const char *url) if (strcmp (mimetype, GNOME_VFS_MIME_TYPE_UNKNOWN) == 0) return PR_TRUE; + const totemPluginMimeEntry *mimetypes; + PRUint32 count; totemScriptablePlugin::PluginMimeTypes (&mimetypes, &count); for (PRUint32 i = 0; i < count; ++i) { @@ -250,7 +968,7 @@ totemPlugin::IsMimeTypeSupported (const char *mimetype, const char *url) } /* Not supported? Probably a broken webserver */ - guessed = gnome_vfs_get_mime_type_for_name (url); + const char *guessed = gnome_vfs_get_mime_type_for_name (url); D ("Guessed mime-type '%s' for '%s'", guessed, url); for (PRUint32 i = 0; i < count; ++i) { @@ -278,301 +996,482 @@ totemPlugin::IsMimeTypeSupported (const char *mimetype, const char *url) } PRBool -totemPlugin::IsSchemeSupported (const char *url) +totemPlugin::IsSchemeSupported (nsIURI *aURI) { - if (url == NULL) + if (!aURI) return PR_FALSE; - if (g_str_has_prefix (url, "mms:") != FALSE) + /* FIXME: use protocol handler to find out if gecko supports this proto, instead */ + + PRBool value; + /* FIXME what about mmsh etc? */ + if (NS_SUCCEEDED (aURI->SchemeIs ("mms", &value)) && value) return PR_FALSE; - if (g_str_has_prefix (url, "rtsp:") != FALSE) + + if (NS_SUCCEEDED (aURI->SchemeIs ("rtsp", &value)) && value) return PR_FALSE; return PR_TRUE; } PRBool -totemPlugin::Fork () +totemPlugin::ParseBoolean (const char *key, + const char *value, + PRBool default_val) { - GTimeVal then, now; - GPtrArray *arr; - char **argv; - GError *err = NULL; - gboolean use_fd = FALSE; + if (value == NULL || strcmp (value, "") == 0) + return default_val; + if (g_ascii_strcasecmp (value, "false") == 0 + || g_ascii_strcasecmp (value, "0") == 0) + return PR_FALSE; + if (g_ascii_strcasecmp (value, "true") == 0 + || g_ascii_strcasecmp (value, "1") == 0) + return PR_TRUE; - if (mSrc == NULL) { - g_warning ("No URLs passed!"); - return FALSE; - } + D ("Unknown value '%s' for parameter '%s'", value, key); - /* Make sure we don't get both an XID and hidden */ - if (mWindow && mHidden) { - g_warning ("Both hidden and a window!"); - return FALSE; - } + return default_val; +} - arr = g_ptr_array_new (); +PRBool +totemPlugin::GetBooleanValue (GHashTable *args, + const char *key, + PRBool default_val) +{ + const char *value; -#ifdef TOTEM_RUN_IN_SOURCE_TREE - if (g_file_test ("./totem-mozilla-viewer", - G_FILE_TEST_EXISTS) != FALSE) { - g_ptr_array_add (arr, g_strdup ("./totem-mozilla-viewer")); - } else { - g_ptr_array_add (arr, - g_strdup (LIBEXECDIR"/totem-mozilla-viewer")); - } -#else - g_ptr_array_add (arr, - g_strdup (LIBEXECDIR"/totem-mozilla-viewer")); -#endif + value = (const char *) g_hash_table_lookup (args, key); + if (value == NULL) + return default_val; - /* Most for RealAudio streams, but also used as a replacement for - * HIDDEN=TRUE */ - if (mWidth == 0 && mHeight == 0) { - mWindow = 0; - mHidden = TRUE; - } + return ParseBoolean (key, value, default_val); +} - if (mWindow) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_XID)); - g_ptr_array_add (arr, g_strdup_printf ("%lu", mWindow)); +PRUint32 +totemPlugin::GetEnumIndex (GHashTable *args, + const char *key, + const char *values[], + PRUint32 n_values, + PRUint32 default_value) +{ + const char *value = (const char *) g_hash_table_lookup (args, key); + if (!value) + return default_value; + + for (PRUint32 i = 0; i < n_values; ++i) { + if (g_ascii_strcasecmp (value, values[i]) == 0) + return i; } - if (mWidth > 0) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_WIDTH)); - g_ptr_array_add (arr, g_strdup_printf ("%d", mWidth)); + return default_value; +} + +/* Public functions for use by the scriptable plugin */ + +nsresult +totemPlugin::SetSrc (const nsACString& aURL) +{ + if (mSrcURI) { + NS_RELEASE (mSrcURI); + mSrcURI = nsnull; } - if (mHeight > 0) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HEIGHT)); - g_ptr_array_add (arr, g_strdup_printf ("%d", mHeight)); + mSrc = aURL; + + /* If |src| is empty, don't resolve the URI! Otherwise we may + * try to load an (probably iframe) html document as our video stream. + */ + if (mSrc.IsEmpty ()) + return NS_OK; + + nsresult rv = mIOService->NewURI (aURL, nsnull /* FIXME! use document charset */, + mBaseURI, &mSrcURI); + if (NS_FAILED (rv)) { + D ("Failed to create src URI (rv=%x)", rv); + mSrcURI = nsnull; } - if (mSrc) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_URL)); - g_ptr_array_add (arr, g_strdup (mSrc)); + return rv; +} + +#ifdef TOTEM_GMP_PLUGIN + +nsresult +totemPlugin::SetURL (const nsACString& aURL) +{ + if (mURLURI) { + NS_RELEASE (mURLURI); + mURLURI = nsnull; } - if (mHref) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HREF)); - g_ptr_array_add (arr, g_strdup (mHref)); + /* Don't allow empty URL */ + if (aURL.IsEmpty ()) + return NS_OK; + + /* FIXME: what is the correct base for the URL param? */ + nsresult rv; + nsIURI *baseURI; + if (mSrcURI) { + baseURI = mSrcURI; + } else { + baseURI = mBaseURI; } - if (mTarget) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_TARGET)); - g_ptr_array_add (arr, g_strdup (mTarget)); + rv = mIOService->NewURI (aURL, nsnull /* FIXME document charset? */, + baseURI, &mURLURI); + if (NS_FAILED (rv)) { + D ("Failed to create URL URI (rv=%x)", rv); } - if (mMimeType) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_MIMETYPE)); - g_ptr_array_add (arr, g_strdup (mMimeType)); + /* FIXME: security checks? */ + + return rv; +} + +#endif /* TOTEM_GMP_PLUGIN */ + +#ifdef TOTEM_NARROWSPACE_PLUGIN + +nsresult +totemPlugin::SetQtsrc (const nsACString& aURL) +{ + /* FIXME can qtsrc have URL extensions? */ + + if (mQtsrcURI) { + NS_RELEASE (mQtsrcURI); + mQtsrcURI = nsnull; } - if (mControllerHidden) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_CONTROLS_HIDDEN)); + /* Don't allow empty qtsrc */ + if (aURL.IsEmpty ()) + return NS_OK; + + nsresult rv; + nsIURI *baseURI; + if (mSrcURI) { + baseURI = mSrcURI; + } else { + baseURI = mBaseURI; } - if (mStatusbar) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_STATUSBAR)); + rv = mIOService->NewURI (aURL, nsnull /* FIXME document charset? */, + baseURI, &mQtsrcURI); + if (NS_FAILED (rv)) { + D ("Failed to create QTSRC URI (rv=%x)", rv); } - if (mHidden) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_HIDDEN)); + /* FIXME: security checks? */ + + return rv; +} + +nsresult +totemPlugin::SetHref (const nsACString& aURL) +{ + nsCString url, target; + PRBool hasExtensions = ParseURLExtensions (aURL, url, target); + + D ("SetHref '%s' has-extensions %d (url: '%s' target: '%s')", + nsCString (aURL).get (), hasExtensions, url.get (), target.get ()); + + nsresult rv; + nsIURI *baseURI; + if (mQtsrcURI) { + baseURI = mQtsrcURI; + } else if (mSrcURI) { + baseURI = mSrcURI; + } else { + baseURI = mBaseURI; } - if (mRepeat) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_REPEAT)); - } + if (hasExtensions) { + rv = baseURI->Resolve (url, mHref); - if (mNoAutostart) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_NOAUTOSTART)); + if (!target.IsEmpty ()) + mTarget = target; + } else { + rv = baseURI->Resolve (aURL, mHref); } - - if (mIsPlaylist) { - g_ptr_array_add (arr, g_strdup (DASHES TOTEM_OPTION_PLAYLIST)); - g_ptr_array_add (arr, g_strdup (mLocal)); - } else { - /* mLocal is only TRUE for playlists */ - if (mIsSupportedSrc == FALSE) { - g_ptr_array_add (arr, g_strdup (mSrc)); - } else { - g_ptr_array_add (arr, g_strdup ("fd://0")); - use_fd = TRUE; - } + if (NS_SUCCEEDED (rv)) { + D ("Resolved HREF '%s'", mHref.get()); + } else { + D ("Failed to resolve HREF (rv=%x)", rv); + mHref = hasExtensions ? url : aURL; /* save unresolved HREF */ } - g_ptr_array_add (arr, NULL); - argv = (char **) g_ptr_array_free (arr, FALSE); + return rv; +} -#ifdef GNOME_ENABLE_DEBUG - { - GString *s; - int i; +PRBool +totemPlugin::ParseURLExtensions (const nsACString &aString, + nsACString &_url, + nsACString &_target) +{ + const nsCString string (aString); - s = g_string_new ("Launching: "); - for (i = 0; argv[i] != NULL; i++) { - g_string_append (s, argv[i]); - g_string_append (s, " "); + const char *str = string.get (); + if (str[0] != '<') + return PR_FALSE; + + /* The expected form is "<URL> T<target> E<name=value pairs>". + * But since this is untrusted input from the web, we'll make sure it conforms to this! + */ + const char *end = strchr (str, '>'); + if (!end) + return PR_FALSE; + + _url = nsDependentCSubstring (string, 1, PRUint32 (end - str - 1)); + + const char *ext = strstr (end, " T<"); + if (ext) { + const char *extend = strchr (ext, '>'); + if (extend) { + _target = nsDependentCSubstring (ext + 3, PRUint32 (extend - ext - 3)); + } + } + +#if 0 + ext = strstr (end, " E<"); + if (ext) { + const char *extend = strchr (ext, '>'); + if (extend) { + D ("E = %s", nsCString (ext + 3, PRUint32 (extend - ext - 3)).get ()); } - g_string_append (s, "\n"); - D("%s", s->str); - g_string_free (s, TRUE); } #endif - if (g_spawn_async_with_pipes (NULL, argv, NULL, - GSpawnFlags(0), NULL, NULL, &mPlayerPID, - use_fd ? &mSendFD : NULL, NULL, NULL, &err) == FALSE) + return PR_TRUE; +} + +void +totemPlugin::LaunchTotem (const nsCString &aURL, + PRUint32 aTimestamp) +{ + const char *argv[3]; + +#ifdef TOTEM_RUN_IN_SOURCE_TREE + if (g_file_test ("./totem", G_FILE_TEST_EXISTS)) { + argv[0] = "./totem"; + } else +#endif { - D("Spawn failed"); + /* FIXME: g_build_filename */ + argv[0] = BINDIR "/totem"; + } - if (err) { - g_warning ("%s\n", err->message); - g_error_free(err); - } + argv[1] = aURL.get (); + argv[2] = NULL; + + /* FIXME use startup notification and startup timestamp */ + /* FIXME Shouldn't this use gdk_spawn_on_screen? */ + GError *error = NULL; + if (!g_spawn_async (NULL /* FIXME working directory, $TMPDIR? */, + (char **) argv, + NULL /* env */, + GSpawnFlags (0), + NULL, NULL, + NULL, + &error)) { + /* FIXME: show an error dialogue? */ + D ("Spawning totem failed: %s", error->message); + g_error_free (error); + } +} - g_strfreev (argv); - return FALSE; +#endif /* TOTEM_NARROWSPACE_PLUGIN */ + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + +totemPlugin * +totemPlugin::FindConsoleClassRepresentant () +{ + /* FIXME: this treats "master" incorrectly */ + if (!mSrcURI || + mConsole.IsEmpty () || + mConsole.Equals (NS_LITERAL_CSTRING ("_unique")) || + mConsole.Equals (NS_LITERAL_CSTRING ("_master"))) { + D ("We're the representant for the console class"); + return nsnull; } - g_strfreev (argv); + totemPlugin *representant = nsnull; - D("player PID: %d", mPlayerPID); - - /* FIXME: not >= 0 ? */ - if (mSendFD > 0) - fcntl(mSendFD, F_SETFL, O_NONBLOCK); - - /* now wait until startup is complete */ - mGotSvc = FALSE; - mWaitForSvc = g_strdup_printf - ("org.totem_%d.MozillaPluginService", mPlayerPID); - D("waiting for signal %s", mWaitForSvc); - dbus_g_proxy_add_signal (mProxy, "NameOwnerChanged", - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_INVALID); - dbus_g_proxy_connect_signal (mProxy, "NameOwnerChanged", - G_CALLBACK (NameOwnerChangedCallback), - this, NULL); - - /* Store this in a local, since we cannot access mScriptable if we get deleted in the loop */ - totemScriptablePlugin *scriptable = mScriptable; - g_get_current_time (&then); - g_time_val_add (&then, G_USEC_PER_SEC * 5); - NS_ADDREF (scriptable); - /* FIXME FIXME! this loop is evil! */ - do { - g_main_context_iteration (NULL, TRUE); - g_get_current_time (&now); - } while (scriptable->IsValid () && - !mGotSvc && - (now.tv_sec <= then.tv_sec)); - - if (!scriptable->IsValid ()) { - /* we were destroyed in one of the iterations of the - * mainloop, get out ASAP */ - D("We no longer exist"); - NS_RELEASE (scriptable); - return FALSE; - } - NS_RELEASE (scriptable); - - dbus_g_proxy_disconnect_signal (mProxy, "NameOwnerChanged", - G_CALLBACK (NameOwnerChangedCallback), this); - if (!mGotSvc) { - g_warning ("Failed to receive DBUS interface response"); - g_free (mWaitForSvc); - mWaitForSvc = nsnull; - - if (mPlayerPID) { - kill (mPlayerPID, SIGKILL); - g_spawn_close_pid (mPlayerPID); - mPlayerPID = 0; + /* Try to find a the representant of the console class */ + PRUint32 count = sPlugins->Length (); + for (PRUint32 i = 0; i < count; ++i) { + totemPlugin *plugin = sPlugins->ElementAt (i); + + PRBool equal = PR_FALSE; + /* FIXME: is this correct? Maybe we should use the toplevel document + * to allow frames (and check the frames for same-origin, obviously) ? + */ + if (plugin != this && + plugin->mPluginOwnerDocument == mPluginOwnerDocument && + mConsole.Equals (plugin->mConsole) && + plugin->mSrcURI && + NS_SUCCEEDED (plugin->mSrcURI->Equals (mSrcURI, &equal)) && + equal) { + if (plugin->mConsoleClassRepresentant) { + representant = plugin->mConsoleClassRepresentant; + } else { + representant = plugin; + } + break; } - return FALSE; } - g_object_unref (mProxy); - /* now get the proxy for the player functions */ - mProxy = - dbus_g_proxy_new_for_name (mConn, mWaitForSvc, - "/TotemEmbedded", - "org.totem.MozillaPluginInterface"); - dbus_g_proxy_add_signal (mProxy, "StopSendingData", - G_TYPE_INVALID); - dbus_g_proxy_connect_signal (mProxy, "StopSendingData", - G_CALLBACK (StopSendingDataCallback), - this, NULL); + D ("Representant for the console class is %p", (void*) representant); - g_free (mWaitForSvc); - mWaitForSvc = nsnull; - D("Done forking, new proxy=%p", mProxy); + return representant; +} + +nsresult +totemPlugin::SetConsole (const nsACString &aConsole) +{ + /* Can't change console group */ + if (!mConsole.IsEmpty ()) + return NS_ERROR_ALREADY_INITIALIZED; + + /* FIXME: we might allow this, and kill the viewer instead. + * Or maybe not spawn the viewer if we don't have a console yet? + */ + if (mViewerPID) + return NS_ERROR_ALREADY_INITIALIZED; - return TRUE; + mConsole = aConsole; + + NS_ASSERTION (mConsoleClassRepresentant == nsnull, "Already have a representant"); + + mConsoleClassRepresentant = FindConsoleClassRepresentant (); + mNeedViewer = (nsnull == mConsoleClassRepresentant); + + return NS_OK; } -static char * -resolve_relative_uri (nsIURI *docURI, - const char *uri) +void +totemPlugin::TransferConsole () { - if (docURI) { - nsresult rv; - nsEmbedCString resolved; - rv = docURI->Resolve (nsEmbedCString (uri), resolved); - if (NS_SUCCEEDED (rv)) { - return g_strdup (resolved.get()); + /* Find replacement representant */ + totemPlugin *representant = nsnull; + + PRUint32 i, count = sPlugins->Length (); + for (i = 0; i < count; ++i) { + totemPlugin *plugin = sPlugins->ElementAt (i); + + if (plugin->mConsoleClassRepresentant == this) { + representant = plugin; + break; } } - return g_strdup (uri); + /* If there are no other elements in this console class, there's nothing to do */ + if (!representant) + return; + + D ("Transferring console from %p to %p", (void*) this, (void*) representant); + + /* Store new representant in the plugins */ + representant->mConsoleClassRepresentant = nsnull; + /* We can start at i since we got out when we found the first one in the loop above */ + for ( ; i < count; ++i) { + totemPlugin *plugin = sPlugins->ElementAt (i); + + if (plugin->mConsoleClassRepresentant == this) + plugin->mConsoleClassRepresentant = representant; + } + + /* Now transfer viewer ownership */ + if (mScriptable) { + NS_ASSERTION (!representant->mScriptable, "WTF"); + representant->mScriptable = mScriptable; + mScriptable->SetPlugin (representant); + mScriptable = nsnull; + } + + representant->mNeedViewer = PR_TRUE; + + representant->mViewerPID = mViewerPID; + mViewerPID = 0; + + representant->mViewerFD = mViewerFD; + mViewerFD = -1; + + representant->mViewerBusAddress = mViewerBusAddress; + representant->mViewerServiceName = mViewerServiceName; + + /* FIXME correct condition? */ + if (mViewerSetUp) + representant->ViewerSetup (); } -#ifdef TOTEM_NARROWSPACE_PLUGIN -static char * -parse_url_extensions (char *href) +void +totemPlugin::UnownedViewerSetup () { - char *uri, *s1, *s2; + /* already set up */ + if (mUnownedViewerSetUp) + return; - g_return_val_if_fail (href != NULL || href[0] != '<', NULL); + mUnownedViewerSetUp = PR_TRUE; - s1 = href + 1; - s2 = strchr (s1, '>'); - if (s2 == NULL) - return g_strdup (href); + D ("UnownedViewerSetup"); - uri = g_strndup (s1, s2 - s1); + NS_ASSERTION (mConsoleClassRepresentant, "We own the viewer!?"); - return uri; + UnownedViewerSetWindow (); } -#endif -gboolean -totem_parse_boolean (char *key, char *value, gboolean default_val) +void +totemPlugin::UnownedViewerSetWindow () { - if (value == NULL || strcmp (value, "") == 0) - return default_val; - if (g_ascii_strcasecmp (value, "false") == 0 - || g_ascii_strcasecmp (value, "0") == 0) - return FALSE; - if (g_ascii_strcasecmp (value, "true") == 0 - || g_ascii_strcasecmp (value, "1") == 0) - return TRUE; + if (mWindowSet || mWindow == 0) + return; - g_warning ("Unknown value '%s' for parameter '%s'", value, key); - return default_val; + if (!mUnownedViewerSetUp) { + D ("No unowned viewer yet, deferring SetWindow"); + return; + } + + NS_ASSERTION (mConsoleClassRepresentant, "We own the viewer!"); + + NS_ENSURE_TRUE (mConsoleClassRepresentant->mViewerProxy, ); + + /* FIXME: do we need a reply callback? */ + dbus_g_proxy_call_no_reply (mConsoleClassRepresentant->mViewerProxy, + "SetWindow", + G_TYPE_STRING, mControls.get (), + G_TYPE_UINT, (guint) mWindow, + G_TYPE_INT, (gint) mWidth, + G_TYPE_INT, (gint) mHeight, + G_TYPE_INVALID); + + mWindowSet = PR_TRUE; } -gboolean -totem_get_boolean_value (GHashTable *args, char *key, gboolean default_val) +void +totemPlugin::UnownedViewerUnsetWindow () { - char *value; + if (!mWindowSet || mWindow == 0) + return; - value = (char *) g_hash_table_lookup (args, key); - if (value == NULL) - return default_val; - return totem_parse_boolean (key, value, default_val); + if (!mUnownedViewerSetUp) + return; + + NS_ASSERTION (mConsoleClassRepresentant, "We own the viewer!"); + + NS_ENSURE_TRUE (mConsoleClassRepresentant->mViewerProxy, ); + + dbus_g_proxy_call_no_reply (mConsoleClassRepresentant->mViewerProxy, + "UnsetWindow", + G_TYPE_UINT, (guint) mWindow, + G_TYPE_INVALID); + + mWindowSet = PR_FALSE; } +#endif /* TOTEM_COMPLEX_PLUGIN */ + +/* Plugin glue functions */ + NPError totemPlugin::Init (NPMIMEType mimetype, uint16_t mode, @@ -581,168 +1480,296 @@ totemPlugin::Init (NPMIMEType mimetype, char *argv[], NPSavedData *saved) { - GError *e = NULL; - gboolean need_req = FALSE; - int i; - GHashTable *args; - char *value; - - mScriptable = new totemScriptablePlugin (this); - if (!mScriptable) - return NPERR_OUT_OF_MEMORY_ERROR; + D ("Init mimetype '%s' mode %d", (const char *) mimetype, mode); - NS_ADDREF (mScriptable); + /* Make sure the plugin stays resident, to avoid + * reloading the GObject types. + */ + CallNPN_SetValueProc (sNPN.setvalue, + mInstance, + NPPVpluginKeepLibraryInMemory, + NS_INT32_TO_PTR (PR_TRUE)); - if (!(mConn = dbus_g_bus_get (DBUS_BUS_SESSION, &e))) { - printf ("Failed to open DBUS session: %s\n", e->message); - g_error_free (e); - return NPERR_OUT_OF_MEMORY_ERROR; - } else if (!(mProxy = dbus_g_proxy_new_for_name (mConn, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus"))) { - printf ("Failed to open DBUS proxy: %s\n", e->message); - g_error_free (e); - return NPERR_OUT_OF_MEMORY_ERROR; + /* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */ + /* FIXME we should error out if we are in fullscreen mode + * FIXME: This might be possible on gecko trunk by returning an + * error code from the NewStream function. + */ + + NPError err; + err = CallNPN_GetValueProc (sNPN.getvalue, + mInstance, NPNVserviceManager, + NS_REINTERPRET_CAST (void *, + NS_REINTERPRET_CAST (void **, &mServiceManager))); + if (err != NPERR_NO_ERROR || !mServiceManager) { + D ("Failed to get the service manager"); + return NPERR_GENERIC_ERROR; } - /* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */ - //FIXME we should error out if we are in fullscreen mode - printf("mode %d\n",mode); - printf("mime type: %s\n", mimetype); + nsresult rv; + rv = mServiceManager->GetServiceByContractID (NS_IOSERVICE_CONTRACTID, + NS_GET_IID (nsIIOService), + NS_REINTERPRET_CAST (void **, &mIOService)); + if (NS_FAILED (rv) || !mIOService) { + D ("Failed to get IO service"); + return NPERR_GENERIC_ERROR; + } - /* to resolve relative URLs */ - nsIDOMWindow *domWin = nsnull; - sMozillaFuncs.getvalue (mInstance, NPNVDOMWindow, - NS_REINTERPRET_CAST (void**, &domWin)); - nsIInterfaceRequestor *irq = nsnull; - if (domWin) { - domWin->QueryInterface (NS_GET_IID (nsIInterfaceRequestor), - NS_REINTERPRET_CAST (void**, &irq)); - NS_RELEASE (domWin); + err = CallNPN_GetValueProc (sNPN.getvalue, + mInstance, NPNVDOMElement, + NS_REINTERPRET_CAST (void *, + NS_REINTERPRET_CAST (void **, &mPluginDOMElement))); + if (err != NPERR_NO_ERROR || !mPluginDOMElement) { + D ("Failed to get our DOM Element"); + return NPERR_GENERIC_ERROR; } - nsIWebNavigation *webNav = nsnull; - if (irq) { - irq->GetInterface (NS_GET_IID (nsIWebNavigation), - NS_REINTERPRET_CAST (void**, &webNav)); - NS_RELEASE (irq); +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + rv = mPluginDOMElement->GetOwnerDocument (&mPluginOwnerDocument); + if (NS_FAILED (rv) || !mPluginOwnerDocument) { + D ("Plugin in a document!?"); + return NPERR_GENERIC_ERROR; + } +#endif /* TOTEM_COMPLEX_PLUGIN */ + + /* We'd like to get the base URI of our DOM element as a nsIURI, + * but there's no frozen method to do so (nsIContent/nsINode isn't available + * for non-MOZILLA_INTERNAL_API code). nsIDOM3Node isn't frozen either, + * but should be safe enough. + */ + nsIDOM3Node *dom3Node = nsnull; + rv = CallQueryInterface (mPluginDOMElement, &dom3Node); + if (NS_FAILED (rv) || !dom3Node) { + D ("Failed to QI the DOM element to nsIDOM3Node"); + return NPERR_GENERIC_ERROR; } - nsIURI *docURI = nsnull; - if (webNav) { - webNav->GetCurrentURI (&docURI); - NS_RELEASE (webNav); + /* FIXME: can we cache this, or can it change (so we'll need to re-get every time we use it)? */ + nsString baseASpec; + rv = dom3Node->GetBaseURI (baseASpec); + if (NS_FAILED (rv) || baseASpec.IsEmpty ()) { + /* We can't go on without a base URI ! */ + D ("Failed to get base URI spec"); + return NPERR_GENERIC_ERROR; + } + + NS_ConvertUTF16toUTF8 baseSpec (baseASpec); + + D ("Base URI is '%s'", baseSpec.get ()); + + rv = mIOService->NewURI (baseSpec, nsnull /* FIXME: use document charset */, + nsnull, &mBaseURI); + if (NS_FAILED (rv) || !mBaseURI) { + D ("Failed to construct base URI"); + return NPERR_GENERIC_ERROR; } + /* Create timer */ + nsIComponentManager *compMan = nsnull; + rv = CallQueryInterface (mServiceManager, &compMan); + if (NS_FAILED (rv) || !compMan) { + D ("Failed to get component manager"); + return NPERR_GENERIC_ERROR; + } + + /* FIXME ? */ + rv = compMan->CreateInstanceByContractID (NS_TIMER_CONTRACTID, + nsnull, + NS_GET_IID (nsITimer), + NS_REINTERPRET_CAST (void **, &mTimer)); + if (NS_FAILED (rv) || !mTimer) { + D ("Failed to create timer: rv=%x", rv); + return NPERR_GENERIC_ERROR; + } + + /* Setup DBus connection handling */ + GError *error = NULL; + if (!(mBusConnection = dbus_g_bus_get (DBUS_BUS_SESSION, &error))) { + g_message ("Failed to open DBUS session: %s", error->message); + g_error_free (error); + + return NPERR_GENERIC_ERROR; + }; + + if (!(mBusProxy = dbus_g_proxy_new_for_name (mBusConnection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS))) { + D ("Failed to get DBUS proxy"); + return NPERR_OUT_OF_MEMORY_ERROR; + } + + dbus_g_proxy_add_signal (mBusProxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal (mBusProxy, + "NameOwnerChanged", + G_CALLBACK (NameOwnerChangedCallback), + NS_REINTERPRET_CAST (void*, this), + NULL); + /* Find the "real" mime-type */ - mMimeType = GetRealMimeType (mimetype); + GetRealMimeType (mimetype, mMimeType); + + D ("Real mimetype for '%s' is '%s'", mimetype, mMimeType.get()); - args = g_hash_table_new_full (g_str_hash, g_str_equal, - (GDestroyNotify) g_free, (GDestroyNotify) g_free); - for (i = 0; i < argc; i++) { + /* Now parse the attributes */ + GHashTable *args = g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_free); + for (int16_t i = 0; i < argc; i++) { printf ("argv[%d] %s %s\n", i, argn[i], argv[i]); g_hash_table_insert (args, g_ascii_strdown (argn[i], -1), - g_strdup (argv[i])); + g_strdup (argv[i])); } - /* The QuickTime href system, with the src video just being a link - * to this video */ -#ifdef TOTEM_NARROWSPACE_PLUGIN - /* New URLs extensions: - * http://developer.apple.com/documentation/QuickTime/WhatsNewQT5/QT5NewChapt1/chapter_1_section_32.html - * http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_HTML/QTScripting_HTML_Document/chapter_1000_section_3.html */ + const char *value; + + /* We only use the size attributes to detect whether we're hidden; + * we'll get our real size from SetWindow. + */ + PRInt32 width = -1, height = -1; - value = (char *) g_hash_table_lookup (args, "href"); + value = (const char *) g_hash_table_lookup (args, "width"); if (value != NULL) { - if (value[0] == '<') { - //FIXME fill the hashtable with the new values - //from the extended URLs - char *href = parse_url_extensions (value); - - //FIXME - // http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_HTML/QTScripting_HTML_Document/chapter_1000_section_3.html - // "Important: If you pass a relative URL in the HREF parameter, it must be relative to the currentlyloadedmovie, not relative to the current web page. If your movies are in a separate folder, specify URLs relative to the movies folder." - mHref = resolve_relative_uri (docURI, href); - g_free (href); - } else { - //FIXME see above - mHref = resolve_relative_uri (docURI, value); - } + width = strtol (value, NULL, 0); } -#endif /* TOTEM_NARROWSPACE_PLUGIN */ - - value = (char *) g_hash_table_lookup (args, "width"); + value = (const char *) g_hash_table_lookup (args, "height"); if (value != NULL) { - /* FIXME! sanitize this value */ - mWidth = strtol (value, NULL, 0); + height = strtol (value, NULL, 0); } + + /* Are we hidden? */ + mHidden = GetBooleanValue (args, "hidden", PR_FALSE); - value = (char *) g_hash_table_lookup (args, "height"); - if (value != NULL) { - /* FIXME! sanitize this value */ - mHeight = strtol (value, NULL, 0); + /* Most for RealAudio streams, but also used as a replacement for + * HIDDEN=TRUE attribute. + */ + if (width == 0 && height == 0) { + mHidden = PR_TRUE; } - //FIXME get the base URL here + /* Whether to automatically stream and play the content */ + mAutostart = GetBooleanValue (args, "autoplay", + GetBooleanValue (args, "autostart", mAutostart)); - value = (char *) g_hash_table_lookup (args, "src"); - if (value != NULL) - mSrc = resolve_relative_uri (docURI, value); + /* Whether to loop */ + mRepeat = GetBooleanValue (args, "repeat", + GetBooleanValue (args, "loop", PR_FALSE)); + + /* Now collect URI attributes */ + const char *src = nsnull, *href = nsnull, *qtsrc = nsnull, *filename = nsnull; + + src = (const char *) g_hash_table_lookup (args, "src"); /* DATA is only used in OBJECTs, see: - * http://developer.mozilla.org/en/docs/Gecko_Plugin_API_Reference:Plug-in_Basics#Plug-in_Display_Modes */ - if (mSrc == NULL) { - value = (char *) g_hash_table_lookup (args, "data"); - if (value != NULL) - mSrc = resolve_relative_uri (docURI, value); + * http://developer.mozilla.org/en/docs/Gecko_Plugin_API_Reference:Plug-in_Basics#Plug-in_Display_Modes + */ + /* FIXME: this is unnecessary, since gecko will automatically a synthetic + * "src" attribute with the "data" atttribute's content if "src" is missing, + * see http://lxr.mozilla.org/seamonkey/source/layout/generic/nsObjectFrame.cpp#2479 + */ + if (!src) { + src = (const char *) g_hash_table_lookup (args, "data"); + } + if (src) { + SetSrc (nsDependentCString (src)); } - /* Those parameters might replace the current src */ #ifdef TOTEM_GMP_PLUGIN /* http://windowssdk.msdn.microsoft.com/en-us/library/aa392440(VS.80).aspx */ - value = (char *) g_hash_table_lookup (args, "filename"); - if (value == NULL) - value = (char *) g_hash_table_lookup (args, "url"); -#elif TOTEM_NARROWSPACE_PLUGIN + filename = (const char *) g_hash_table_lookup (args, "filename"); + if (!filename) + filename = (const char *) g_hash_table_lookup (args, "url"); + if (filename) { + SetURL (nsDependentCString (filename)); + } +#endif /* TOTEM_GMP_PLUGIN */ + +#ifdef TOTEM_NARROWSPACE_PLUGIN + /* Target */ + value = (const char *) g_hash_table_lookup (args, "target"); + if (value) { + mTarget.Assign (value); + } + + href = (const char *) g_hash_table_lookup (args, "href"); + if (href) { + SetHref (nsDependentCString (href)); + } + /* http://developer.apple.com/documentation/QuickTime/QT6WhatsNew/Chap1/chapter_1_section_13.html */ - value = (char *) g_hash_table_lookup (args, "qtsrc"); -#elif TOTEM_MULLY_PLUGIN + qtsrc = (const char *) g_hash_table_lookup (args, "qtsrc"); + if (qtsrc) { + SetQtsrc (nsDependentCString (qtsrc)); + } +#endif /* TOTEM_NARROWSPACE_PLUGIN */ + +/* FIXME */ +#if 0 //def TOTEM_MULLY_PLUGIN /* Click to play behaviour of the DivX plugin */ - value = (char *) g_hash_table_lookup (args, "previewimage"); + char *previewimage = (const char *) g_hash_table_lookup (args, "previewimage"); if (value != NULL) mHref = g_strdup (mSrc); -#else - value = NULL; -#endif - if (value != NULL) { - if (mSrc == NULL || strcmp (mSrc, value) != 0) { - //FIXME need to cancel SRC if there's one - g_free (mSrc); - mSrc = resolve_relative_uri (docURI, value); - need_req = TRUE; - } +#endif /* TOTEM_NARROWSPACE_PLUGIN */ + + /* If we're set to start automatically, we'll use the src stream */ + if (mRequestURI && + mRequestURI == mSrcURI) { + mExpectingStream = mAutostart; } -#ifdef TOTEM_NARROWSPACE_PLUGIN /* Caching behaviour */ - mCache = totem_get_boolean_value (args, "cache", FALSE); - /* Target */ - value = (char *) g_hash_table_lookup (args, "target"); - if (value != NULL) - mTarget = g_strdup (value); -#endif /* TOTEM_NARROWSPACE_PLUGIN */ +#ifdef TOTEM_NARROWSPACE_PLUGIN + if (strcmp (mimetype, "video/quicktime") != 0) { + mCache = PR_TRUE; + } - /* Whether the controls are all hidden, MSIE parameter - * http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_CONTROLLER.html */ - gboolean controller; - controller = totem_get_boolean_value (args, "controller", TRUE); - mControllerHidden = (controller == FALSE); + mCache = GetBooleanValue (args, "cache", mCache); - //FIXME add Netscape controls support - // http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_CONTROLS.html + mControllerHidden = !GetBooleanValue (args, "controller", PR_TRUE); - /* Are we hidden? */ - mHidden = totem_get_boolean_value (args, "hidden", FALSE); +#endif /* TOTEM_NARROWSPACE_PLUGIN */ + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + value = (const char *) g_hash_table_lookup (args, "console"); + if (value) { + rv = SetConsole (nsDependentCString (value)); + if (NS_FAILED (rv)) + return NPERR_GENERIC_ERROR; + } + + const char *kControls[] = { + "All", + "ControlPanel", + "FFCtrl", + "HomeCtrl", + "ImageWindow", + "InfoPanel", + "InfoVolumePanel", + "MuteCtrl", + "MuteVolume", + "PauseButton", + "PlayButton", + "PlayOnlyButton", + "PositionField", + "PositionSlider", + "RWCtrl", + "StatusBar", + "StatusField", + "StopButton", + "TACCtrl", + "VolumeSlider", + }; + PRUint32 control = GetEnumIndex (args, "controls", + kControls, G_N_ELEMENTS (kControls), + 0); + mControls = kControls[control]; + +#endif /* TOTEM_COMPLEX_PLUGIN */ #ifdef TOTEM_GMP_PLUGIN /* uimode is either invisible, none, mini, or full @@ -750,23 +1777,24 @@ totemPlugin::Init (NPMIMEType mimetype, value = (char *) g_hash_table_lookup (args, "uimode"); if (value != NULL) { if (g_ascii_strcasecmp (value, "none") == 0) { - mControllerHidden = TRUE; + mControllerHidden = PR_TRUE; } else if (g_ascii_strcasecmp (value, "invisible") == 0) { - mHidden = TRUE; + mHidden = PR_TRUE; } else if (g_ascii_strcasecmp (value, "full") == 0) { - mStatusbar = TRUE; + mShowStatusbar = PR_TRUE; } else if (g_ascii_strcasecmp (value, "mini") == 0) { ; } } + + /* Whether the controls are all hidden, MSIE parameter + * http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_CONTROLLER.html */ /* ShowXXX parameters as per http://support.microsoft.com/kb/285154 */ - controller = totem_get_boolean_value (args, "showcontrols", TRUE); - if (mControllerHidden == FALSE) - mControllerHidden = (controller == FALSE); + mControllerHidden = !GetBooleanValue (args, "controller", + GetBooleanValue (args, "showcontrols", PR_TRUE)); + + mShowStatusbar = GetBooleanValue (args, "showstatusbar", mShowStatusbar); - gboolean statusbar; - statusbar = totem_get_boolean_value (args, "showstatusbar", mStatusbar); - mStatusbar = (statusbar == FALSE); //FIXME add showdisplay // see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmp6sdk/htm/userinterfaceelements.asp #endif /* TOTEM_GMP_PLUGIN */ @@ -774,105 +1802,147 @@ totemPlugin::Init (NPMIMEType mimetype, /* Whether to NOT autostart */ //FIXME Doesn't handle playcount, or loop with numbers // http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_LOOP.html - gboolean autostart; - autostart = totem_get_boolean_value (args, "autostart", TRUE); - autostart = totem_get_boolean_value (args, "autoplay", autostart); - mNoAutostart = (autostart == FALSE); - - /* Whether to loop */ - mRepeat = totem_get_boolean_value (args, "loop", FALSE); - mRepeat = totem_get_boolean_value (args, "repeat", mRepeat); - - g_message ("mSrc: %s", mSrc); - g_message ("mHref: %s", mHref); - g_message ("mHeight: %d mWidth: %d", mHeight, mWidth); - g_message ("mCache: %d", mCache); - g_message ("mTarget: %s", mTarget); - g_message ("mControllerHidden: %d", mControllerHidden); - g_message ("mStatusbar: %d", mStatusbar); - g_message ("mHidden: %d", mHidden); - g_message ("mNoAutostart: %d mRepeat: %d", mNoAutostart, mRepeat); //FIXME handle starttime and endtime // http://www.htmlcodetutorial.com/embeddedobjects/_EMBED_STARTTIME.html - g_hash_table_destroy (args); - NS_IF_RELEASE (docURI); - - mIsSupportedSrc = IsSchemeSupported (mSrc); + /* Dump some disagnostics */ + D ("mSrc: %s", mSrc.get ()); + D ("mCache: %d", mCache); + D ("mControllerHidden: %d", mControllerHidden); + D ("mShowStatusbar: %d", mShowStatusbar); + D ("mHidden: %d", mHidden); + D ("mAutostart: %d, mRepeat: %d", mAutostart, mRepeat); +#ifdef TOTEM_NARROWSPACE_PLUGIN + D ("mHref: %s", mHref.get ()); + D ("mTarget: %s", mTarget.get ()); +#endif /* TOTEM_NARROWSPACE_PLUGIN */ +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + D ("mConsole: %s", mConsole.get ()); + D ("mControls: %s", mControls.get ()); +#endif /* TOTEM_COMPLEX_PLUGIN */ - /* If filename is used, we need to request the stream ourselves */ - if (need_req != FALSE) { - if (mIsSupportedSrc != FALSE) { - CallNPN_GetURLProc(sMozillaFuncs.geturl, - mInstance, mSrc, NULL); - } - } + g_hash_table_destroy (args); - return NPERR_NO_ERROR; + return ViewerFork (); } NPError totemPlugin::SetWindow (NPWindow *window) { - D("SetWindow [%p]", (void*) this); - - if (mWindow) { - D ("existing window"); - if (mWindow == (Window) window->window) { - D("resize"); - /* Resize event */ - /* Not currently handled */ - } else { - D("change"); - printf ("ack. window changed!\n"); - } - } else { - /* If not, wait for the first bits of data to come, - * but not when the stream type isn't supported */ - mWindow = (Window) window->window; - if (mStream && mIsSupportedSrc) { - if (!Fork ()) - return NPERR_GENERIC_ERROR; - /* If it's not a supported stream, we need - * to launch now */ - } else if (!mIsSupportedSrc) { - if (!Fork ()) - return NPERR_GENERIC_ERROR; - } else { - D("waiting for data to come"); - } + if (mHidden && window->window != 0) { + D("SetWindow: hidden, can't set window"); + return NPERR_GENERIC_ERROR; } - D("leaving plugin_set_window"); + if (mWindow != 0 && + mWindow == (Window) window->window) { + mWidth = window->width; + mHeight = window->height; + DD ("Window resized or moved, now %dx%d", mWidth, mHeight); + } else if (mWindow == 0) { + mWindow = (Window) window->window; + + mWidth = window->width; + mHeight = window->height; + + D ("Initial window set, XID %x size %dx%d", + (guint) (Window) window->window, mWidth, mHeight); + + ViewerSetWindow (); + } else { + D ("Setting a new window != mWindow, this is unsupported!"); + } return NPERR_NO_ERROR; } NPError totemPlugin::NewStream (NPMIMEType type, - NPStream* stream_ptr, + NPStream* stream, NPBool seekable, uint16* stype) { - D("plugin_new_stream"); + if (!stream || !stream->url) + return NPERR_GENERIC_ERROR; + + D ("NewStream mimetype '%s' URL '%s'", (const char *) type, stream->url); /* We already have a live stream */ if (mStream) { - D("plugin_new_stream exiting, already have a live stream"); - return NPERR_GENERIC_ERROR; + D ("Already have a live stream, aborting stream"); + + /* We don't just return NPERR_GENERIC_ERROR (or any other error code), + * since, using gecko trunk (1.9), this causes the plugin to be destroyed, + * if this is the automatic |src| stream. Same for the other calls below. + */ + return CallNPN_DestroyStreamProc (sNPN.destroystream, + mInstance, + stream, + NPRES_DONE); } - D("plugin_new_stream type: %s url: %s", type, mSrc); + /* Either: + * - this is the automatic first stream from the |src| or |data| attribute, + * but we want to request a different URL, or + * - Gecko sometimes sends us 2 stream, and if the first is already in cache we'll + * be done it before it starts the 2nd time so the "if (mStream)" check above + * doesn't catch always this. + */ + if (!mExpectingStream) { + D ("Not expecting a new stream; aborting stream"); + + return CallNPN_DestroyStreamProc (sNPN.destroystream, + mInstance, + stream, + NPRES_DONE); + } + + /* This was an expected stream, no more expected */ + mExpectingStream = PR_FALSE; + +#if 1 // #if 0 + // This is fixed now _except_ the "if (!mViewerReady)" problem in StreamAsFile - if (IsMimeTypeSupported (type, mSrc) == FALSE) { - D("plugin_new_stream type: %s not supported, exiting\n", type); - return NPERR_INVALID_PLUGIN_ERROR; + /* For now, we'll re-request the stream when the viewer is ready. + * As an optimisation, we could either just allow small (< ~128ko) streams + * (which are likely to be playlists!), or any stream and cache them to disk + * until the viewer is ready. + */ + if (!mViewerReady) { + D ("Viewer not ready, aborting stream"); + + return CallNPN_DestroyStreamProc (sNPN.destroystream, + mInstance, + stream, + NPRES_DONE); } +#endif - //FIXME need to find better semantics? - //what about saving the state, do we get confused? - if (g_str_has_prefix (mSrc, "file://")) { + if (!IsMimeTypeSupported (type, stream->url)) { + D ("Unsupported mimetype, aborting stream"); + + return CallNPN_DestroyStreamProc (sNPN.destroystream, + mInstance, + stream, + NPRES_DONE); + } + + /* FIXME: assign the stream URL to mRequestURI ? */ + + /* Open a FILE* for our FD */ + mViewerStream = fdopen (mViewerFD, "w"); + if (!mViewerStream) { + int err = errno; + D ("Failed to fdopen the viewer fd, errno: %d", err); + + return CallNPN_DestroyStreamProc (sNPN.destroystream, + mInstance, + stream, + NPRES_DONE); + } + + if (g_str_has_prefix (stream->url, "file://")) { *stype = NP_ASFILEONLY; mStreamType = NP_ASFILEONLY; } else { @@ -880,7 +1950,13 @@ totemPlugin::NewStream (NPMIMEType type, mStreamType = NP_ASFILE; } - mStream = stream_ptr; + mStream = stream; + + mCheckedForPlaylist = PR_FALSE; + mIsPlaylist = PR_FALSE; + + /* To track how many data we get from ::Write */ + mBytesStreamed = 0; return NPERR_NO_ERROR; } @@ -889,35 +1965,57 @@ NPError totemPlugin::DestroyStream (NPStream* stream, NPError reason) { - D("plugin_destroy_stream, reason: %d", reason); + if (!mStream || mStream != stream) + return NPERR_GENERIC_ERROR; - if (mSendFD >= 0) { - close(mSendFD); - mSendFD = -1; - } + D ("DestroyStream reason %d", reason); mStream = nsnull; + NS_ASSERTION (mViewerStream, "No viewer stream?"); + + int ret = fclose (mViewerStream); + if (ret < 0) { + int err = errno; + D ("Failed to close viewer stream with errno %d: %s", err, g_strerror (err)); + } + + mViewerStream = NULL; + +#if 0 + /* Close the viewer */ + dbus_g_proxy_call_no_reply (mViewerProxy, + "CloseStream", + G_TYPE_INVALID, + G_TYPE_INVALID); +#endif + return NPERR_NO_ERROR; } int32 totemPlugin::WriteReady (NPStream *stream) { - //D("plugin_write_ready"); + /* FIXME this could probably be an assertion instead */ + if (!mStream || mStream != stream) + return -1; - if (mIsSupportedSrc == FALSE) - return 0; /* FIXME not -1 ? */ + /* Suspend the request until the viewer is ready; + * we'll wake up in 100ms for another try. + */ + if (!mViewerReady) + return 0; - if (mSendFD < 0) - return (PLUGIN_STREAM_CHUNK_SIZE); + DD ("WriteReady"); + /* FIXME: does this mix well with using mViewerStream ? */ struct pollfd fds; fds.events = POLLOUT; - fds.fd = mSendFD; + fds.fd = mViewerFD; if (poll (&fds, 1, 0) > 0) return (PLUGIN_STREAM_CHUNK_SIZE); + /* suspend the request, we'll wake up in 100ms for another try */ return 0; } @@ -927,59 +2025,58 @@ totemPlugin::Write (NPStream *stream, int32 len, void *buffer) { - int ret; + /* FIXME this could probably be an assertion instead */ + if (!mStream || mStream != stream) + return -1; - //D("plugin_write"); + DD ("Write offset %d len %d", offset, len); - /* We already know it's a playlist, don't try to check it again - * and just wait for it to be on-disk */ - if (mIsPlaylist != FALSE) + /* We already know it's a playlist, just wait for it to be on-disk. */ + if (mIsPlaylist) return len; - if (!mPlayerPID) { - if (!mStream) { - g_warning ("No stream in NPP_Write!?"); - return -1; - } + /* Check for playlist. + * Ideally we'd just always forward the data to the viewer and the viewer + * always parse the playlist itself, but that's not yet implemented. + */ + /* FIXME we can only look at the current buffer, not at all the data so far. + * So we can only do this at the start of the stream. + */ + if (!mCheckedForPlaylist) { + NS_ASSERTION (offset == 0, "Not checked for playlist but not at the start of the stream!?"); - mTriedWrite = TRUE; + mCheckedForPlaylist = PR_TRUE; + + if (totem_pl_parser_can_parse_from_data ((const char *) buffer, len, TRUE /* FIXME */)) { + D ("Is playlist; need to wait for the file to be downloaded completely"); + mIsPlaylist = PR_TRUE; - /* FIXME this looks wrong since it'll look at the current data buffer, - * not the cumulative data since the stream started - */ - if (totem_pl_parser_can_parse_from_data ((const char *) buffer, len, TRUE /* FIXME */) != FALSE) { - D("Need to wait for the file to be downloaded completely"); - mIsPlaylist = TRUE; return len; } - - if (!Fork ()) - return -1; } - if (mSendFD < 0) - return -1; - - if (mIsSupportedSrc == FALSE) - return -1; + int ret = fwrite (buffer, 1, len, mViewerStream); + /* FIXME shouldn't we retry if errno is EINTR ? */ - ret = write (mSendFD, buffer, len); - if (ret < 0) { - /* FIXME what's this for? gecko will destroy the stream automatically if we return -1 */ + if (NS_UNLIKELY (ret < 0)) { int err = errno; - D("ret %d: [%d]%s", ret, errno, g_strerror (err)); + D ("Write failed with errno %d: %s", err, g_strerror (err)); + if (err == EPIPE) { /* fd://0 got closed, probably because the backend - * crashed on us */ - if (CallNPN_DestroyStreamProc - (sMozillaFuncs.destroystream, - mInstance, - mStream, - NPRES_DONE) != NPERR_NO_ERROR) { + * crashed on us. Destroy the stream. + */ + if (CallNPN_DestroyStreamProc (sNPN.destroystream, + mInstance, + mStream, + NPRES_DONE) != NPERR_NO_ERROR) { g_warning ("Couldn't destroy the stream"); } } - /* FIXME shouldn't we set ret=0 here? otherwise the stream will be destroyed */ + } else /* ret >= 0 */ { + DD ("Wrote %d bytes", ret); + + mBytesStreamed += ret; } return ret; @@ -989,49 +2086,142 @@ void totemPlugin::StreamAsFile (NPStream *stream, const char* fname) { - NS_ASSERTION (stream == mStream, "Unknown stream"); + if (!mStream || mStream != stream) + return; - D("plugin_stream_as_file: %s", fname); + D ("StreamAsFile filename '%s'", fname); - if (!mTriedWrite) { + /* FIXME: this reads the whole file into memory... try to + * find a way to avoid it. + */ + if (!mCheckedForPlaylist) { mIsPlaylist = totem_pl_parser_can_parse_from_filename - (fname, TRUE); + (fname, TRUE) != FALSE; } - if (!mPlayerPID && mIsPlaylist) { - mLocal = g_filename_to_uri (fname, NULL, NULL); - Fork (); + /* FIXME! This happens when we're using the automatic |src| stream and + * it finishes before we're ready. + */ + if (!mViewerReady) { + D ("Viewer not ready yet, deferring SetLocalFile"); return; - } else if (!mPlayerPID) { - if (!Fork ()) - return; } - if (mIsPlaylist != FALSE) - return; - - GError *err = NULL; - if (!dbus_g_proxy_call (mProxy, "SetLocalFile", &err, - G_TYPE_STRING, fname, G_TYPE_INVALID, - G_TYPE_INVALID)) { - g_warning ("Error: %s", err->message); - g_error_free (err); + NS_ASSERTION (mViewerProxy, "No viewer proxy"); + NS_ASSERTION (mViewerReady, "Viewer not ready"); + + NS_ENSURE_TRUE (mRequestBaseURI && mRequestURI, ); + + nsCString baseSpec, spec; + mRequestBaseURI->GetSpec (baseSpec); + mRequestURI->GetSpec (spec); + + /* FIXME: these calls need to be async!! + * But the file may be unlinked as soon as we return from this + * function... do we need to keep a link? + */ + gboolean retval = TRUE; + GError *error = NULL; + if (mIsPlaylist) { + retval = dbus_g_proxy_call (mViewerProxy, + "SetPlaylist", + &error, + G_TYPE_STRING, fname, + G_TYPE_STRING, spec.get (), + G_TYPE_STRING, baseSpec.get (), + G_TYPE_INVALID, + G_TYPE_INVALID); + } + /* Only call SetLocalFile if we haven't already streamed the file! + * (It happens that we get no ::Write calls if the file is + * completely in the cache.) + */ + else if (mBytesStreamed == 0) { + retval = dbus_g_proxy_call (mViewerProxy, + "SetLocalFile", + &error, + G_TYPE_STRING, fname, + G_TYPE_STRING, spec.get (), + G_TYPE_STRING, baseSpec.get (), + G_TYPE_INVALID, + G_TYPE_INVALID); + } else { + D ("mBytesStreamed %u", mBytesStreamed); } - D("plugin_stream_as_file done"); + if (!retval) { + g_warning ("Viewer error: %s", error->message); + g_error_free (error); + } +} + +void +totemPlugin::URLNotify (const char *url, + NPReason reason, + void *notifyData) +{ + D ("URLNotify URL '%s' reason %d", url ? url : "", reason); + + /* If we get called when we expect a stream, + * it means that the stream failed. + */ + if (reason == NPRES_NETWORK_ERR && + mExpectingStream) { + /* FIXME: set/show error */ + mExpectingStream = PR_FALSE; + } } NPError totemPlugin::GetScriptable (void *_retval) { - D ("GetScriptable [%p], mInstance %p", (void*) this, mInstance); + D ("GetScriptable [%p]", (void*) this); + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + if (mConsoleClassRepresentant) { + return mConsoleClassRepresentant->GetScriptable (_retval); + } +#endif /* TOTEM_COMPLEX_PLUGIN */ + + if (!mScriptable) { + mScriptable = new totemScriptablePlugin (this); + if (!mScriptable) + return NPERR_OUT_OF_MEMORY_ERROR; - /* FIXME this shouldn't happen, but seems to happen nevertheless?!? */ - if (!mScriptable) - return NPERR_INVALID_PLUGIN_ERROR; + NS_ADDREF (mScriptable); + } nsresult rv = mScriptable->QueryInterface (NS_GET_IID (nsISupports), NS_REINTERPRET_CAST (void **, _retval)); return NS_SUCCEEDED (rv) ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR; } + +/* static */ NPError +totemPlugin::Initialise () +{ +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + sPlugins = new nsTArray<totemPlugin*> (32); + if (!sPlugins) + return NPERR_OUT_OF_MEMORY_ERROR; +#endif /* TOTEM_COMPLEX_PLUGIN */ + + return NPERR_NO_ERROR; +} + +/* static */ NPError +totemPlugin::Shutdown () +{ +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + if (sPlugins) { + if (!sPlugins->IsEmpty ()) { + D ("WARNING: sPlugins not empty on shutdown, count: %d", sPlugins->Length ()); + } + + delete sPlugins; + sPlugins = nsnull; + } +#endif /* TOTEM_COMPLEX_PLUGIN */ + + return NPERR_NO_ERROR; +} diff --git a/browser-plugin/totemPlugin.h b/browser-plugin/totemPlugin.h index aa7e9564a..8fc5162dc 100644 --- a/browser-plugin/totemPlugin.h +++ b/browser-plugin/totemPlugin.h @@ -1,7 +1,8 @@ -/* Totem Mozilla plugin +/* Totem browser plugin * - * Copyright (C) 2004 Bastien Nocera <hadess@hadess.net> - * Copyright (C) 2002 David A. Schleef <ds@schleef.org> + * Copyright © 2004 Bastien Nocera <hadess@hadess.net> + * Copyright © 2002 David A. Schleef <ds@schleef.org> + * Copyright © 2006 Christian Persch * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,9 +25,24 @@ #include <stdint.h> #include <dbus/dbus-glib.h> -#include "npapi.h" +#include <npapi.h> +#include <nsStringAPI.h> +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) +#include <nsTArray.h> +#endif /* TOTEM_COMPLEX_PLUGIN */ + +#include "totem-plugin-viewer-commands.h" + +class nsIDOMDocument; +class nsIDOMElement; +class nsIIOService; +class nsIServiceManager; +class nsITimer; +class nsIURI; +class nsVoidArray; class totemScriptablePlugin; +struct _NPNetscapeFuncs; class totemPlugin { public: @@ -35,9 +51,17 @@ class totemPlugin { void* operator new (size_t aSize) CPP_THROW_NEW; - nsresult Play (); - nsresult Stop (); - nsresult Pause (); + /* Interface to scriptable */ + + nsresult DoCommand (const char *aCommand); + + nsresult SetSrc (const nsACString &aURL); + + /* plugin glue */ + static _NPNetscapeFuncs sNPN; + + static NPError Initialise (); + static NPError Shutdown (); NPError Init (NPMIMEType mimetype, uint16_t mode, @@ -49,11 +73,11 @@ class totemPlugin { NPError SetWindow (NPWindow *aWindow); NPError NewStream (NPMIMEType type, - NPStream* stream_ptr, - NPBool seekable, - uint16* stype); + NPStream* stream_ptr, + NPBool seekable, + uint16* stype); NPError DestroyStream (NPStream* stream, - NPError reason); + NPError reason); int32 WriteReady (NPStream *stream); int32 Write (NPStream *stream, @@ -61,64 +85,187 @@ class totemPlugin { int32 len, void *buffer); void StreamAsFile (NPStream *stream, - const char* fname); - + const char* fname); + + void URLNotify (const char *url, + NPReason reason, + void *notifyData); + NPError GetScriptable (void *_retval); + private: + static void PR_CALLBACK NameOwnerChangedCallback (DBusGProxy *proxy, const char *svc, const char *old_owner, const char *new_owner, - totemPlugin *plugin); + void *aData); - static void PR_CALLBACK StopSendingDataCallback (DBusGProxy *proxy, - totemPlugin *plugin); + static void PR_CALLBACK ViewerForkTimeoutCallback (nsITimer *aTimer, + void *aData); - private: + static void PR_CALLBACK ButtonPressCallback (DBusGProxy *proxy, + //guint aButton, + //guint aTimestamp, + void *aData); + + static void PR_CALLBACK StopStreamCallback (DBusGProxy *proxy, + void *aData); + + static void PR_CALLBACK ViewerSetWindowCallback (DBusGProxy *aProxy, + DBusGProxyCall *aCall, + void *aData); + static void PR_CALLBACK ViewerOpenStreamCallback (DBusGProxy *aProxy, + DBusGProxyCall *aCall, + void *aData); + static void PR_CALLBACK ViewerOpenURICallback (DBusGProxy *aProxy, + DBusGProxyCall *aCall, + void *aData); + + NPError ViewerFork (); + void ViewerSetup (); + void ViewerSetWindow (); + void ViewerReady (); + void ViewerCleanup (); + void ViewerButtonPressed (); + + void NameOwnerChanged (const char *aName, + const char *aOldOwner, + const char *aNewOwner); - PRBool Fork (); + void ComputeRequest (); + void ClearRequest (); + void RequestStream (); void UnsetStream (); - PRBool IsMimeTypeSupported (const char *aMimeType, const char *aURL); - PRBool IsSchemeSupported (const char *aURL); - char *GetRealMimeType (const char *aMimeType); + PRBool IsMimeTypeSupported (const char *aMimeType, + const char *aURL); + PRBool IsSchemeSupported (nsIURI *aURI); + void GetRealMimeType (const char *aMimeType, + nsACString &_retval); + PRBool ParseBoolean (const char *key, + const char *value, + PRBool default_val); + PRBool GetBooleanValue (GHashTable *args, + const char *key, + PRBool default_val); + PRUint32 GetEnumIndex (GHashTable *args, + const char *key, + const char *values[], + PRUint32 n_values, + PRUint32 default_value); NPP mInstance; - /* FIXME make this a nsCOMPtr */ + /* FIXME make these use nsCOMPtr<> !! */ totemScriptablePlugin *mScriptable; + nsIServiceManager *mServiceManager; + nsIIOService *mIOService; + nsIDOMElement *mPluginDOMElement; + nsITimer *mTimer; + nsIURI *mBaseURI; + + nsIURI *mRequestBaseURI; + nsIURI *mRequestURI; NPStream *mStream; - char *mMimeType; + PRUint32 mBytesStreamed; PRUint8 mStreamType; + nsCString mMimeType; + + nsCString mSrc; + nsIURI *mSrcURI; + Window mWindow; + PRInt32 mWidth; + PRInt32 mHeight; - char *mSrc; - char *mLocal; - char *mHref; - char *mTarget; + DBusGConnection *mBusConnection; + DBusGProxy *mBusProxy; + DBusGProxy *mViewerProxy; + DBusGProxyCall *mViewerPendingCall; + nsCString mViewerBusAddress; + nsCString mViewerServiceName; + int mViewerPID; + int mViewerFD; + FILE *mViewerStream; - int mWidth; - int mHeight; +#ifdef TOTEM_GMP_PLUGIN + public: + nsresult SetURL (const nsACString &aURL); - DBusGConnection *mConn; - DBusGProxy *mProxy; - char *mWaitForSvc; + private: + nsIURI *mURLURI; +#endif - int mSendFD; - int mPlayerPID; +#ifdef TOTEM_NARROWSPACE_PLUGIN + public: + nsresult SetQtsrc (const nsACString &aURL); + nsresult SetHref (const nsACString& aURL); + + private: + PRBool ParseURLExtensions (const nsACString &aString, + nsACString &_url, + nsACString &_target); + + void LaunchTotem (const nsCString &aURL, + PRUint32 aTimestamp); + + nsIURI *mQtsrcURI; + + nsCString mHref; + nsIURI *mHrefURI; + + nsCString mTarget; +#endif + +#if defined(TOTEM_COMPLEX_PLUGIN) && defined(HAVE_NSTARRAY_H) + public: + + nsresult SetConsole (const nsACString &aConsole); + + private: + + totemPlugin* FindConsoleClassRepresentant (); + void TransferConsole (); + + void UnownedViewerSetup (); + void UnownedViewerSetWindow (); + void UnownedViewerUnsetWindow (); + + nsIDOMDocument *mPluginOwnerDocument; + nsCString mConsole; + nsCString mControls; + + /* nsnull if we're the representant ourself */ + totemPlugin *mConsoleClassRepresentant; + + static nsTArray<totemPlugin*> *sPlugins; + +#endif /* TOTEM_COMPLEX_PLUGIN */ + + private: + PRUint32 mAutostart : 1; PRUint32 mCache : 1; + PRUint32 mCheckedForPlaylist : 1; PRUint32 mControllerHidden : 1; - PRUint32 mGotSvc : 1; + PRUint32 mExpectingStream : 1; + PRUint32 mHadStream : 1; PRUint32 mHidden : 1; PRUint32 mIsPlaylist : 1; PRUint32 mIsSupportedSrc : 1; - PRUint32 mNoAutostart : 1; + PRUint32 mNeedViewer : 1; PRUint32 mRepeat : 1; - PRUint32 mTriedWrite : 1; - PRUint32 mStatusbar : 1; + PRUint32 mRequestIsSrc : 1; + PRUint32 mShowStatusbar : 1; + PRUint32 mTimerRunning : 1; + PRUint32 mUnownedViewerSetUp : 1; + PRUint32 mViewerReady : 1; + PRUint32 mViewerSetUp : 1; + PRUint32 mWaitingForButtonPress : 1; + PRUint32 mWindowSet : 1; }; typedef struct { diff --git a/browser-plugin/totemPluginGlue.cpp b/browser-plugin/totemPluginGlue.cpp index 0db55ec5e..9eb573b9e 100644 --- a/browser-plugin/totemPluginGlue.cpp +++ b/browser-plugin/totemPluginGlue.cpp @@ -37,7 +37,6 @@ #include "totemPluginGlue.h" #include "totemPlugin.h" -NPNetscapeFuncs sMozillaFuncs; static char *mime_list = NULL; static NPError @@ -49,20 +48,10 @@ totem_plugin_new_instance (NPMIMEType mimetype, char *argv[], NPSavedData *savedData) { - totemPlugin *plugin; - - D("totem_plugin_new_instance"); - if (!instance) return NPERR_INVALID_INSTANCE_ERROR; - /* Make sure the plugin stays resident to avoid crashers when - * reloading the GObject types. - */ - CallNPN_SetValueProc (sMozillaFuncs.setvalue, instance, - NPPVpluginKeepLibraryInMemory, NS_INT32_TO_PTR(PR_TRUE)); - - plugin = new totemPlugin (instance); + totemPlugin *plugin = new totemPlugin (instance); if (!plugin) return NPERR_OUT_OF_MEMORY_ERROR; @@ -81,8 +70,6 @@ static NPError totem_plugin_destroy_instance (NPP instance, NPSavedData **save) { - D("plugin_destroy"); - if (!instance) return NPERR_INVALID_INSTANCE_ERROR; @@ -198,14 +185,21 @@ totem_plugin_url_notify (NPP instance, NPReason reason, void* notifyData) { - D("plugin_url_notify"); + if (!instance) + return; + + totemPlugin *plugin = (totemPlugin *) instance->pdata; + if (!plugin) + return; + + plugin->URLNotify (url, reason, notifyData); } static void totem_plugin_print (NPP instance, NPPrint* platformPrint) { - D("plugin_print"); + D ("Print"); } static char * @@ -223,7 +217,7 @@ totem_plugin_get_value (NPP instance, NPError err = NPERR_NO_ERROR; /* See NPPVariable in npapi.h */ - D("plugin_get_value %d (%x)\n", variable, variable); + D ("GetValue variable %d (%x)", variable, variable); if (instance) { plugin = (totemPlugin *) instance->pdata; @@ -240,7 +234,7 @@ totem_plugin_get_value (NPP instance, *((NPBool *)value) = TRUE; break; case NPPVpluginScriptableIID: { - nsIID* ptr = NS_STATIC_CAST (nsIID *, sMozillaFuncs.memalloc (sizeof (nsIID))); + nsIID* ptr = NS_STATIC_CAST (nsIID *, totemPlugin::sNPN.memalloc (sizeof (nsIID))); if (ptr) { *ptr = NS_GET_IID (nsISupports); *NS_STATIC_CAST (nsIID **, value) = ptr; @@ -259,7 +253,7 @@ totem_plugin_get_value (NPP instance, break; } default: - D("unhandled variable %d (%x)", variable, variable); + D ("Unhandled variable"); err = NPERR_INVALID_PARAM; break; } @@ -272,7 +266,7 @@ totem_plugin_set_value (NPP instance, NPNVariable variable, void *value) { - D("plugin_set_value %d (%x)", variable, variable); + D ("SetValue variable %d (%x)", variable, variable); return NPERR_NO_ERROR; } @@ -332,7 +326,7 @@ NP_Initialize (NPNetscapeFuncs * aMozillaFuncs, NPBool supportsXEmbed = PR_FALSE; NPNToolkitType toolkit = (NPNToolkitType) 0; - D("NP_Initialize\n"); + D ("NP_Initialize"); /* Do we support XEMBED? */ err = CallNPN_GetValueProc (aMozillaFuncs->getvalue, NULL, @@ -354,6 +348,7 @@ NP_Initialize (NPNetscapeFuncs * aMozillaFuncs, if ((aMozillaFuncs->version >> 8) > NP_VERSION_MAJOR) return NPERR_INCOMPATIBLE_VERSION_ERROR; + /* FIXME: check instead: indexof (last known entry in NPNetscapeFuncs) */ if (aMozillaFuncs->size < sizeof (NPNetscapeFuncs)) return NPERR_INVALID_FUNCTABLE_ERROR; if (plugin_funcs->size < sizeof (NPPluginFuncs)) @@ -366,29 +361,29 @@ NP_Initialize (NPNetscapeFuncs * aMozillaFuncs, * structure, because the Mozilla function table could actually be * bigger than what we expect. */ - sMozillaFuncs.size = aMozillaFuncs->size; - sMozillaFuncs.version = aMozillaFuncs->version; - sMozillaFuncs.geturl = aMozillaFuncs->geturl; - sMozillaFuncs.posturl = aMozillaFuncs->posturl; - sMozillaFuncs.requestread = aMozillaFuncs->requestread; - sMozillaFuncs.newstream = aMozillaFuncs->newstream; - sMozillaFuncs.write = aMozillaFuncs->write; - sMozillaFuncs.destroystream = aMozillaFuncs->destroystream; - sMozillaFuncs.status = aMozillaFuncs->status; - sMozillaFuncs.uagent = aMozillaFuncs->uagent; - sMozillaFuncs.memalloc = aMozillaFuncs->memalloc; - sMozillaFuncs.memfree = aMozillaFuncs->memfree; - sMozillaFuncs.memflush = aMozillaFuncs->memflush; - sMozillaFuncs.reloadplugins = aMozillaFuncs->reloadplugins; - sMozillaFuncs.getJavaEnv = aMozillaFuncs->getJavaEnv; - sMozillaFuncs.getJavaPeer = aMozillaFuncs->getJavaPeer; - sMozillaFuncs.geturlnotify = aMozillaFuncs->geturlnotify; - sMozillaFuncs.posturlnotify = aMozillaFuncs->posturlnotify; - sMozillaFuncs.getvalue = aMozillaFuncs->getvalue; - sMozillaFuncs.setvalue = aMozillaFuncs->setvalue; - sMozillaFuncs.invalidaterect = aMozillaFuncs->invalidaterect; - sMozillaFuncs.invalidateregion = aMozillaFuncs->invalidateregion; - sMozillaFuncs.forceredraw = aMozillaFuncs->forceredraw; + totemPlugin::sNPN.size = aMozillaFuncs->size; + totemPlugin::sNPN.version = aMozillaFuncs->version; + totemPlugin::sNPN.geturl = aMozillaFuncs->geturl; + totemPlugin::sNPN.posturl = aMozillaFuncs->posturl; + totemPlugin::sNPN.requestread = aMozillaFuncs->requestread; + totemPlugin::sNPN.newstream = aMozillaFuncs->newstream; + totemPlugin::sNPN.write = aMozillaFuncs->write; + totemPlugin::sNPN.destroystream = aMozillaFuncs->destroystream; + totemPlugin::sNPN.status = aMozillaFuncs->status; + totemPlugin::sNPN.uagent = aMozillaFuncs->uagent; + totemPlugin::sNPN.memalloc = aMozillaFuncs->memalloc; + totemPlugin::sNPN.memfree = aMozillaFuncs->memfree; + totemPlugin::sNPN.memflush = aMozillaFuncs->memflush; + totemPlugin::sNPN.reloadplugins = aMozillaFuncs->reloadplugins; + totemPlugin::sNPN.getJavaEnv = aMozillaFuncs->getJavaEnv; + totemPlugin::sNPN.getJavaPeer = aMozillaFuncs->getJavaPeer; + totemPlugin::sNPN.geturlnotify = aMozillaFuncs->geturlnotify; + totemPlugin::sNPN.posturlnotify = aMozillaFuncs->posturlnotify; + totemPlugin::sNPN.getvalue = aMozillaFuncs->getvalue; + totemPlugin::sNPN.setvalue = aMozillaFuncs->setvalue; + totemPlugin::sNPN.invalidaterect = aMozillaFuncs->invalidaterect; + totemPlugin::sNPN.invalidateregion = aMozillaFuncs->invalidateregion; + totemPlugin::sNPN.forceredraw = aMozillaFuncs->forceredraw; /* * Set up a plugin function table that Mozilla will use to call @@ -422,14 +417,18 @@ NP_Initialize (NPNetscapeFuncs * aMozillaFuncs, plugin_funcs->getvalue = NewNPP_GetValueProc(totem_plugin_get_value); plugin_funcs->setvalue = NewNPP_SetValueProc(totem_plugin_set_value); - return NPERR_NO_ERROR; + D ("NP_Initialize succeeded"); + + return totemPlugin::Initialise (); } NPError NP_Shutdown(void) { + D ("NP_Shutdown"); + g_free (mime_list); mime_list = NULL; - return NPERR_NO_ERROR; + return totemPlugin::Shutdown (); } diff --git a/configure.in b/configure.in index 2a2465a4c..2577642bc 100644 --- a/configure.in +++ b/configure.in @@ -158,6 +158,10 @@ if test x$HAVE_XINE = xno; then done fi +if test "$HAVE_GSTREAMER" = "yes"; then + AC_DEFINE([HAVE_GSTREAMER],[1],[Define if using gst backend]) +fi + AM_CONDITIONAL(TOTEM_GST, test "x$HAVE_GSTREAMER" = "xyes") AC_ARG_ENABLE(vanity, @@ -243,25 +247,28 @@ AC_MSG_CHECKING([whether to compile the browser plugins]) AC_ARG_ENABLE([browser-plugins], AS_HELP_STRING([--enable-mozilla],[compile the mozilla plugin]), - [ENABLE_MOZILLA=$enableval], - [ENABLE_MOZILLA=autodetect]) + [],[enable_browser_plugins=autodetect]) # Backward compatibility AC_ARG_ENABLE([mozilla],[],[enable_browser_plugins=$enableval],[enable_browser_plugins=autodetect]) -AC_MSG_RESULT([$ENABLE_MOZILLA]) +AC_MSG_RESULT([$enable_browser_plugins]) -dnl Check which mozilla to use -if test "x$ENABLE_MOZILLA" != "xno" ; then +# Check which mozilla to use + +if test "$enable_browser_plugins" != "no" ; then AC_MSG_CHECKING([which gecko to use]) - AC_ARG_WITH([mozilla], - AS_HELP_STRING([--with-mozilla],[Which gecko engine to use (default: autodetect)])) + AC_ARG_WITH([gecko], + [AS_HELP_STRING([--with-gecko],[Which gecko engine to use (default: autodetect)])]) + + # backward compat + AC_ARG_WITH([mozilla],[],[with_gecko=$withval]) GECKOS="xulrunner firefox mozilla-firefox seamonkey mozilla" - gecko=$with_mozilla + gecko=$with_gecko - if test "x$gecko" = "x"; then + if test -z "$with_gecko"; then dnl Autodetect gecko for g in $GECKOS; do if $PKG_CONFIG --exists $g-xpcom; then @@ -273,16 +280,16 @@ if test "x$ENABLE_MOZILLA" != "xno" ; then AC_MSG_ERROR([Gecko "$gecko" not found]) fi - if test "x$gecko" = "x" -a "x$ENABLE_MOZILLA" = "xautodetect"; then + if test -z "$gecko" -a "$enable_browser_plugins" = "autodetect"; then dnl No gecko found, disable plugin AC_MSG_WARN([No gecko found, disabling plugin]) - ENABLE_MOZILLA=no - elif test "x$gecko" = "x"; then + enable_browser_plugins=no + elif test -z "$gecko"; then AC_MSG_ERROR([No gecko found]) elif ! ( echo "$GECKOS" | egrep "(^| )$gecko(\$| )" > /dev/null); then AC_MSG_ERROR([Unknown gecko "$gecko" specified]) else - ENABLE_MOZILLA=yes + enable_browser_plugins=yes fi AC_MSG_RESULT([$gecko]) @@ -297,7 +304,7 @@ if test "x$ENABLE_MOZILLA" != "xno" ; then MOZILLA=$gecko AC_SUBST([MOZILLA]) - if test "$enable_cxx_warnings" != "no" -a "$GCC" = "yes"; then + if test "$enable_cxx_warnings" != "no" -a "$GXX" = "yes"; then PLUGIN_EXTRA_WARN_CXXFLAGS="-Wno-ctor-dtor-privacy -Wno-non-virtual-dtor -Woverloaded-virtual" else PLUGIN_EXTRA_WARN_CXXFLAGS= @@ -305,23 +312,23 @@ if test "x$ENABLE_MOZILLA" != "xno" ; then AC_SUBST([PLUGIN_EXTRA_WARN_CXXFLAGS]) fi -dnl Check for mozilla modules, but keep the CFLAGS and LIBS in -dnl separate vars -if test "x$ENABLE_MOZILLA" = "xyes" ; then +# Check for mozilla modules, but keep the CFLAGS and LIBS in +# separate vars +if test "$enable_browser_plugins" = "yes" ; then PKG_CHECK_MODULES([MOZILLA_NOT_LINKED], [$MOZILLA-xpcom >= $MOZILLA_VERSION_MIN \ $MOZILLA-plugin],, - [ENABLE_MOZILLA=no]) + [enable_browser_plugins=no]) fi -dnl Check for other required modules, and merge CFLAGS, but not link -dnl flags to avoid linking against -lxpcom -lplds4 -lplc4 -lnspr4 -if test "x$ENABLE_MOZILLA" = "xyes" ; then +# Check for other required modules, and merge CFLAGS, but not link +# flags to avoid linking against -lxpcom -lplds4 -lplc4 -lnspr4 +if test "$enable_browser_plugins" = "yes" ; then PKG_CHECK_MODULES([BROWSER_PLUGIN], [glib-2.0 gnome-vfs-2.0 >= $GNOMEVFS_REQS gnome-vfs-module-2.0 >= $GNOMEVFS_REQS gthread-2.0], - [],[ENABLE_MOZILLA=no]) + [],[enable_browser_plugins=no]) BROWSER_PLUGIN_CFLAGS="$MOZILLA_NOT_LINKED_CFLAGS $BROWSER_PLUGIN_CFLAGS" AC_SUBST([BROWSER_PLUGIN_CFLAGS]) @@ -335,20 +342,20 @@ if test "x$ENABLE_MOZILLA" = "xyes" ; then >= 0.17 to function.])]) fi -if test "x$ENABLE_MOZILLA" = "xyes" ; then +if test "$enable_browser_plugins" = "yes" ; then PKG_CHECK_MODULES([DBUS], [dbus-glib-1 >= $DBUS_REQS],, - [ENABLE_MOZILLA=no]) + [enable_browser_plugins=no]) DBUSLIBDIR="`$PKG_CONFIG dbus-glib-1 --variable=libdir`" DBUSBINDIR="`echo $DBUSLIBDIR | sed -e s/lib/bin/`" AC_PATH_PROG([DBUS_BIND], [dbus-binding-tool], [no], [$DBUSBINDIR:$PATH]) if test "x$DBUS_BIND" = "xno"; then AC_MSG_WARN([dbus-binding-tool not found]) - ENABLE_MOZILLA=no + enable_browser_plugins=no fi fi -dnl Sets some variables, and check for xpidl -if test "x$ENABLE_MOZILLA" = "xyes" ; then +# Sets some variables, and check for xpidl +if test "$enable_browser_plugins" = "yes" ; then MOZILLA_PREFIX="`$PKG_CONFIG $MOZILLA-xpcom --variable=prefix`" MOZILLA_LIBDIR="`$PKG_CONFIG $MOZILLA-xpcom --variable=libdir`" MOZILLA_INCLUDE_ROOT="`$PKG_CONFIG --variable=includedir $MOZILLA-xpcom`" @@ -372,8 +379,8 @@ if test "x$ENABLE_MOZILLA" = "xyes" ; then AC_ARG_VAR([MOZILLA_PLUGINDIR],[Where to install the plugin to]) fi -dnl Search for the idl include directory -if test "x$ENABLE_MOZILLA" = "xyes" ; then +# Search for the idl include directory +if test "$enable_browser_plugins" = "yes" ; then dnl This only works on gecko 1.8 MOZILLA_IDLDIR="`$PKG_CONFIG --variable=idldir $MOZILLA-xpcom`" dnl Fallback for older versions @@ -388,13 +395,71 @@ if test "x$ENABLE_MOZILLA" = "xyes" ; then AC_SUBST([MOZILLA_IDLDIR]) fi -if test "$ENABLE_MOZILLA" = "yes" ; then - AC_DEFINE([HAVE_MOZILLA],[1],[Define if you build the mozilla plugin]) +if test "$enable_browser_plugins" = "yes" ; then + AC_DEFINE([ENABLE_BROWSER_PLUGINS],[1],[Define if you build the mozilla plugin]) +fi + +AM_CONDITIONAL([ENABLE_BROWSER_PLUGINS], [test "$enable_browser_plugins" = "yes"]) + +# check for libxpcomglue_s + +LIBXPCOMGLUE_S= + +if test "$enable_browser_plugins" = "yes"; then + AC_LANG_PUSH([C++]) + __SAVE_CPPFLAGS=$CPPFLAGS + __SAVE_CXXFLAGS=$CXXFLAGS + __SAVE_LIBS=$LIBS + __SAVE_LDFLAGS=$LDFLAGS + + CPPFLAGS="$CPPFLAGS -I$MOZILLA_INCLUDE_ROOT" + CXXFLAGS="$CXXFLAGS $MOZILLA_NOT_LINKED_CFLAGS" + LIBS="$LIBS -L$MOZILLA_LIBDIR -lxpcom -lxpcomglue_s" + LDFLAGS="$LDFLAGS -Wl,--rpath -Wl,$MOZILLA_LIBDIR" + + AC_MSG_CHECKING([for libxpcomglue_s]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include <mozilla-config.h> +#include <nsStringAPI.h> + ]],[[ +nsCString s; +const char *t = s.get (); + ]])], + [have_libxpcomglue_s=yes], + [have_libxpcomglue_s=no]) + AC_MSG_RESULT([$have_libxpcomglue_s]) + + CPPFLAGS=$__SAVE_CPPFLAGS + CXXFLAGS=$__SAVE_CXXFLAGS + LIBS=$__SAVE_LIBS + LDFLAGS=$__SAVE_LDFLAGS + AC_LANG_POP([C++]) + + if test "$have_libxpcomglue_s" = "yes"; then + LIBXPCOMGLUE_S="-L$MOZILLA_LIBDIR -lxpcomglue_s" + else + AC_MSG_WARN([libxpcomglue_s not available; plugins may not be portable]) + fi +fi + +AC_SUBST([LIBXPCOMGLUE_S]) + +# check for some additional xpcom headers + +if test "$enable_browser_plugins" = "yes"; then + AC_LANG_PUSH([C++]) + __SAVE_CPPFLAGS=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $MOZILLA_NOT_LINKED_CFLAGS -I$MOZILLA_INCLUDE_ROOT/xpcom" + + AC_CHECK_HEADERS([nsTArray.h]) + + CPPFLAGS=$__SAVE_CPPFLAGS + AC_LANG_POP([C++]) fi -AM_CONDITIONAL([HAVE_MOZILLA], [test "x$ENABLE_MOZILLA" = "xyes"]) +# check which plugins to enable -if test "$ENABLE_MOZILLA" = "yes"; then +if test "$enable_browser_plugins" = "yes"; then AC_MSG_CHECKING([whether to enable the basic browser plugin]) AC_ARG_ENABLE([basic-plugin], diff --git a/data/mozilla-viewer.glade b/data/mozilla-viewer.glade index 59ecaa4ec..3034148e9 100644 --- a/data/mozilla-viewer.glade +++ b/data/mozilla-viewer.glade @@ -4,8 +4,7 @@ <glade-interface> <widget class="GtkWindow" id="window"> - <property name="visible">True</property> - <property name="title" translatable="yes">window1</property> + <property name="title"></property> <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">False</property> @@ -20,13 +19,13 @@ <property name="urgency_hint">False</property> <child> - <widget class="GtkVBox" id="vbox1"> + <widget class="GtkVBox" id="content_box"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">0</property> <child> - <widget class="GtkHBox" id="hbox4"> + <widget class="GtkHBox" id="video_box"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">0</property> @@ -53,7 +52,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> + <property name="focus_on_click">False</property> <child> <widget class="GtkAlignment" id="alignment1"> @@ -146,7 +145,7 @@ </child> <child> - <widget class="Custom" id="emb_statusbar"> + <widget class="Custom" id="statusbar"> <property name="visible">True</property> <property name="creation_function">totem_statusbar_create</property> <property name="int1">0</property> diff --git a/po/POTFILES.in b/po/POTFILES.in index 9c8999a13..436fd62b4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -16,7 +16,6 @@ src/bacon-video-widget-properties.c src/bacon-volume.c src/totem-interface.c src/totem-menu.c -src/totem-mozilla-viewer.c src/totem-options.c src/totem-playlist.c src/totem-preferences.c @@ -37,4 +36,4 @@ src/backend/bacon-video-widget-xine.c src/backend/video-utils.c src/plparse/totem-disc.c src/plparse/totem-pl-parser.c - +browser-plugin/totem-plugin-viewer.c diff --git a/src/Makefile.am b/src/Makefile.am index c68d4118f..93746349c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,5 @@ SUBDIRS = backend plparse - if TOTEM_VANITY VANITY=vanity else @@ -19,10 +18,6 @@ bin_PROGRAMS = totem totem-video-thumbnailer totem-video-indexer $(VANITY) libexec_PROGRAMS = noinst_PROGRAMS = list_v4l disc-test $(TOTEM_PROPERTIES_PAGE_TEST) -if HAVE_MOZILLA -libexec_PROGRAMS += totem-mozilla-viewer -endif - noinst_LTLIBRARIES = \ libbaconpropertiespage.la \ libbaconmessageconnection.la \ @@ -102,7 +97,7 @@ totem_video_thumbnailer_LDADD = \ $(XTEST_LIBS) $(X_LIBS) nautilusdir = $(libdir)/nautilus/extensions-1.0/ -libtotem_properties_page_la_CFLAGS = -I$(top_srcdir) +libtotem_properties_page_la_CFLAGS = -I$(top_srcdir) $(WARN_CFLAGS) libtotem_properties_page_la_SOURCES = \ totem-properties-main.c \ totem-properties-view.c \ @@ -114,7 +109,7 @@ libtotem_properties_page_la_LIBADD = \ $(XTEST_LIBS) $(X_LIBS) libtotem_properties_page_la_LDFLAGS = $(modules_flags) -test_properties_page_CFLAGS = -I$(top_srcdir) +test_properties_page_CFLAGS = -I$(top_srcdir) $(WARN_CFLAGS) test_properties_page_SOURCES = \ totem-properties-main.c \ totem-properties-view.c \ @@ -129,27 +124,6 @@ test_properties_page_LDADD = \ BUILT_SOURCES = -if HAVE_MOZILLA - -BUILT_SOURCES += \ - totem-mozilla-interface.h - -totem-mozilla-interface.h: totem-mozilla-interface.xml - $(DBUS_BIND) --prefix=totem_embedded --mode=glib-server \ - --output=totem-mozilla-interface.h $< - -totem_mozilla_viewer_SOURCES = \ - totem-mozilla-viewer.c \ - totem-mozilla-options.h - -totem_mozilla_viewer_LDADD = \ - backend/libbaconvideowidget.la libbaconpropertiespage.la \ - libtotem_player.la plparse/libtotem-plparser.la \ - $(EXTRA_GNOME_LIBS) $(XVIDMODE_LIBS) \ - $(DBUS_LIBS) $(NVTV_LIBS) \ - $(XTEST_LIBS) $(X_LIBS) -endif - vanity_SOURCES = \ $(BACON_V4L_SELECTION) \ totem-screenshot.c totem-screenshot.h \ @@ -182,11 +156,9 @@ INCLUDES = \ -I$(top_builddir)/src/plparse \ -I$(top_builddir)/src/backend \ $(EXTRA_GNOME_CFLAGS) \ + $(WARN_CFLAGS) \ -DDBUS_API_SUBJECT_TO_CHANGE \ $(DBUS_CFLAGS) \ - $(MOZILLA_CFLAGS) \ - $(MOZILLA_SUBDIR_INCLUDES) \ - $(MOZILLA_XPCOM_CFLAGS) \ $(NVTV_CFLAGS) \ $(NAUTILUS_CFLAGS) \ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ @@ -199,6 +171,4 @@ INCLUDES = \ CLEANFILES = *.bak *.gladep core* *.orig *~ $(desktop_DATA) $(BUILT_SOURCES) EXTRA_DIST = \ $(BACON_V4L_SELECTION) \ - vanity.c \ - totem-mozilla-viewer.c \ - totem-mozilla-interface.xml + vanity.c diff --git a/src/backend/bvw-test.c b/src/backend/bvw-test.c index 012ba7fcb..0ece0fbf6 100644 --- a/src/backend/bvw-test.c +++ b/src/backend/bvw-test.c @@ -16,6 +16,11 @@ static void test_xine_set_mrl(char *path) bacon_video_widget_open (BACON_VIDEO_WIDGET (xine), mrl, NULL); } +static void on_redirect (GtkWidget *bvw, const char *mrl, gpointer data) +{ + g_message ("Redirect to: %s", mrl); +} + static void on_eos_event(GtkWidget *widget, gpointer user_data) { bacon_video_widget_stop (BACON_VIDEO_WIDGET (xine)); @@ -59,6 +64,7 @@ int main(int argc, char *argv[]) gtk_widget_realize (xine); g_signal_connect(G_OBJECT (xine),"eos",G_CALLBACK (on_eos_event),NULL); + g_signal_connect (G_OBJECT(xine), "got-redirect", G_CALLBACK (on_redirect), NULL); gtk_widget_show(win); gtk_widget_show(xine); diff --git a/src/plparse/totem-pl-parser.c b/src/plparse/totem-pl-parser.c index 5628d9b9c..c4446dcff 100644 --- a/src/plparse/totem-pl-parser.c +++ b/src/plparse/totem-pl-parser.c @@ -2674,17 +2674,17 @@ totem_pl_parser_can_parse_from_data (const char *data, for (i = 0; i < G_N_ELEMENTS(special_types); i++) { if (strcmp (special_types[i].mimetype, mimetype) == 0) { - D(g_message ("Is special type '%s'\n", mimetype)); + D(g_message ("Is special type '%s'", mimetype)); return TRUE; } } for (i = 0; i < G_N_ELEMENTS(dual_types); i++) { if (strcmp (dual_types[i].mimetype, mimetype) == 0) { - D(g_message ("Should be dual type '%s', making sure now\n", mimetype)); + D(g_message ("Should be dual type '%s', making sure now", mimetype)); if (dual_types[i].iden != NULL) { gboolean retval = (* dual_types[i].iden) (data, len); - D(g_message ("%s dual type '%s'\n", + D(g_message ("%s dual type '%s'", retval ? "Is" : "Is not", mimetype)); return retval; } diff --git a/src/totem-mozilla-interface.xml b/src/totem-mozilla-interface.xml deleted file mode 100644 index e7696bd53..000000000 --- a/src/totem-mozilla-interface.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" ?> - -<node name="/"> - <interface name="org.totem.MozillaPluginInterface"> - <method name="Play"/> - <method name="Pause"/> - <method name="Stop"/> - <method name="SetLocalFile"> - <arg type="s"/> - </method> - <signal name="StopSendingData"/> - </interface> -</node> diff --git a/src/totem-mozilla-viewer.c b/src/totem-mozilla-viewer.c deleted file mode 100644 index 20b5977a5..000000000 --- a/src/totem-mozilla-viewer.c +++ /dev/null @@ -1,1077 +0,0 @@ -/* Totem Mozilla Embedded Viewer - * - * Copyright (C) <2004-2006> Bastien Nocera <hadess@hadess.net> - * Copyright (C) <2002> David A. Schleef <ds@schleef.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#include <gtk/gtk.h> -#include <glib/gi18n.h> -#include <stdlib.h> -#include <string.h> - -#include <gdk/gdkx.h> -#include <glade/glade.h> -#include <gconf/gconf-client.h> -#include <totem-pl-parser.h> - -#include <dbus/dbus-glib.h> -#include <sys/types.h> -#include <unistd.h> - -#include <libgnomevfs/gnome-vfs.h> -#include <libgnomevfs/gnome-vfs-mime-handlers.h> - -#include "bacon-video-widget.h" -#include "totem-interface.h" -#include "totem-mozilla-options.h" -#include "totem-statusbar.h" -#include "bacon-volume.h" -#include "video-utils.h" - -GtkWidget *totem_statusbar_create (void); -GtkWidget *totem_volume_create (void); - -#define IS_FD_STREAM (strcmp(emb->filename, "fd://0") == 0) - -#define VOLUME_DOWN_OFFSET -8 -#define VOLUME_UP_OFFSET 8 - -/* For newer D-Bus version */ -#ifndef DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT -#define DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT 0 -#endif - -typedef enum { - STATE_PLAYING, - STATE_PAUSED, - STATE_STOPPED -} TotemStates; - -#define TOTEM_TYPE_EMBEDDED (totem_embedded_get_type ()) -typedef struct { - GObjectClass parent_class; - - void (*stop_sending_data) (GObject *emb); -} TotemEmbeddedClass; - -typedef struct _TotemEmbedded { - GObject parent; - - GtkWidget *window; - GladeXML *menuxml, *xml; - GtkWidget *about; - TotemStatusbar *statusbar; - int width, height; - const char *orig_filename; - const char *mimetype; - char *filename, *href, *target; - BaconVideoWidget *bvw; - TotemStates state; - GdkCursor *cursor; - - /* Playlist */ - GList *playlist, *current; - GMainLoop *loop; - int num_items; - - /* Open menu item */ - GnomeVFSMimeApplication *app; - GtkWidget *menu_item; - - /* Seek bits */ - GtkAdjustment *seekadj; - GtkWidget *seek; - - guint is_playlist : 1; - guint embedded_done : 1; - guint controller_hidden : 1; - guint hidden : 1; - guint repeat : 1; - guint seeking : 1; - guint noautostart : 1; -} TotemEmbedded; - -GType totem_embedded_get_type (void); - -G_DEFINE_TYPE (TotemEmbedded, totem_embedded, G_TYPE_OBJECT); -static void totem_embedded_init (TotemEmbedded *emb) { } - -gboolean totem_embedded_play (TotemEmbedded *emb, GError **err); -gboolean totem_embedded_pause (TotemEmbedded *emb, GError **err); -gboolean totem_embedded_stop (TotemEmbedded *emb, GError **err); -gboolean totem_embedded_set_local_file (TotemEmbedded *emb, const char *url, GError **err); - -static void totem_embedded_set_menu (TotemEmbedded *emb, gboolean enable); -static void on_open1_activate (GtkButton *button, TotemEmbedded *emb); - -#include "totem-mozilla-interface.h" - -enum { - STOP_SENDING_DATA, - LAST_SIGNAL -}; -static int totem_emb_table_signals[LAST_SIGNAL] = { 0 }; - -static void totem_embedded_class_init (TotemEmbeddedClass *klass) -{ - GObjectClass *object_class; - - object_class = (GObjectClass *) klass; - - totem_emb_table_signals[STOP_SENDING_DATA] = - g_signal_new ("stop-sending-data", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (TotemEmbeddedClass, stop_sending_data), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -totem_embedded_exit (TotemEmbedded *emb) -{ - //FIXME what happens when embedded, and we can't go on? - exit (1); -} - -static void -totem_embedded_error_and_exit (char *title, char *reason, TotemEmbedded *emb) -{ - totem_interface_error_blocking (title, reason, - GTK_WINDOW (emb->window)); - totem_embedded_exit (emb); -} - -static void -totem_embedded_emit_stop_sending_data (TotemEmbedded *emb) -{ - g_return_if_fail (emb->filename != NULL); - if (IS_FD_STREAM) { - g_signal_emit (G_OBJECT (emb), - totem_emb_table_signals[STOP_SENDING_DATA], - 0, NULL); - } -} - -static void -totem_embedded_set_state (TotemEmbedded *emb, TotemStates state) -{ - char *id = NULL; - GtkWidget *image; - GdkCursor *cursor; - - if (state == emb->state) - return; - - cursor = NULL; - image = glade_xml_get_widget (emb->xml, "emb_pp_button_image"); - - switch (state) { - case STATE_STOPPED: - if (emb->href != NULL) - cursor = emb->cursor; - id = g_strdup_printf ("gtk-media-play-%s", - gtk_widget_get_direction (image) ? "ltr" : "rtl"); - totem_statusbar_set_text (emb->statusbar, _("Stopped")); - totem_statusbar_set_time_and_length (emb->statusbar, 0, 0); - break; - case STATE_PAUSED: - id = g_strdup_printf ("gtk-media-play-%s", - gtk_widget_get_direction (image) ? "ltr" : "rtl"); - totem_statusbar_set_text (emb->statusbar, _("Paused")); - break; - case STATE_PLAYING: - id = g_strdup ("gtk-media-pause"); - totem_statusbar_set_text (emb->statusbar, _("Playing")); - break; - default: - break; - } - - gtk_image_set_from_icon_name (GTK_IMAGE (image), id, GTK_ICON_SIZE_MENU); - g_free (id); - if (emb->hidden == FALSE && cursor != NULL) - gdk_window_set_cursor (GTK_WIDGET (emb->bvw)->window, cursor); - - emb->state = state; -} - -static void -totem_embedded_set_pp_state (TotemEmbedded *emb, gboolean state) -{ - GtkWidget *item; - - item = glade_xml_get_widget (emb->xml, "pp_button"); - gtk_widget_set_sensitive (item, state); -} - -static gboolean -totem_embedded_open (TotemEmbedded *emb) -{ - GError *err = NULL; - gboolean retval; - - g_message ("totem_embedded_open '%s'", emb->filename); - - retval = bacon_video_widget_open (emb->bvw, emb->filename, &err); - if (retval == FALSE) - { - char *msg, *disp; - - totem_embedded_emit_stop_sending_data (emb); - - totem_embedded_set_state (emb, STATE_STOPPED); - - //FIXME if emb->filename is fd://0 or stdin:/// - //we should use a better name than that - disp = g_strdup (emb->filename); - //disp = gnome_vfs_unescape_string_for_display (totem->mrl); - msg = g_strdup_printf(_("Totem could not play '%s'."), disp); - g_free (disp); - - g_message ("error: %s", err->message); - totem_interface_error_blocking (msg, err->message, - GTK_WINDOW (emb->window)); - g_free (msg); - g_error_free (err); - - totem_embedded_set_pp_state (emb, FALSE); - } else { - totem_embedded_set_state (emb, STATE_PAUSED); - totem_embedded_set_pp_state (emb, TRUE); - } - - if (IS_FD_STREAM && emb->num_items > 1) { - totem_embedded_set_menu (emb, FALSE); - } else { - totem_embedded_set_menu (emb, TRUE); - } - - return retval; -} - -gboolean -totem_embedded_play (TotemEmbedded *emb, GError **err) -{ - if (bacon_video_widget_play (emb->bvw, NULL)) - totem_embedded_set_state (emb, STATE_PLAYING); - return TRUE; -} - -gboolean -totem_embedded_pause (TotemEmbedded *emb, GError **err) -{ - bacon_video_widget_pause (emb->bvw); - totem_embedded_set_state (emb, STATE_PAUSED); - return TRUE; -} - -gboolean -totem_embedded_stop (TotemEmbedded *emb, GError **err) -{ - bacon_video_widget_stop (emb->bvw); - totem_embedded_set_state (emb, STATE_STOPPED); - return TRUE; -} - -gboolean -totem_embedded_set_local_file (TotemEmbedded *emb, - const char *path, GError **err) -{ - g_message ("Setting the current path to %s", path); - - g_free (emb->filename); - emb->filename = g_filename_to_uri (path, NULL, NULL); - - return TRUE; -} - -static void -totem_embedded_set_menu (TotemEmbedded *emb, gboolean enable) -{ - GtkWidget *menu, *item, *image; - GtkWidget *copy; - char *label; - - copy = glade_xml_get_widget (emb->menuxml, "copy_location1"); - gtk_widget_set_sensitive (copy, enable); - - if (emb->menu_item != NULL) { - gtk_widget_destroy (emb->menu_item); - emb->menu_item = NULL; - } - if (emb->app != NULL) { - gnome_vfs_mime_application_free (emb->app); - emb->app = NULL; - } - - if (enable == FALSE) - return; - - if (IS_FD_STREAM) { - emb->app = gnome_vfs_mime_get_default_application_for_uri - (emb->orig_filename, emb->mimetype); - } else { - emb->app = gnome_vfs_mime_get_default_application_for_uri - (emb->filename, emb->mimetype); - } - - if (emb->app == NULL) - return; - - /* translators: this is: - * Open With ApplicationName - * as in nautilus' right-click menu */ - label = g_strdup_printf ("_Open with \"%s\"", emb->app->name); - item = gtk_image_menu_item_new_with_mnemonic (label); - g_free (label); - image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image); - g_signal_connect (G_OBJECT (item), "activate", - G_CALLBACK (on_open1_activate), emb); - gtk_widget_show (item); - emb->menu_item = item; - - menu = glade_xml_get_widget (emb->menuxml, "menu"); - gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item); -} - -static void -on_open1_activate (GtkButton *button, TotemEmbedded *emb) -{ - GList *l = NULL; - - g_return_if_fail (emb->app != NULL); - - if (IS_FD_STREAM) { - l = g_list_prepend (l, (gpointer) emb->orig_filename); - } else if (emb->href != NULL) { - l = g_list_prepend (l, emb->href); - } else { - l = g_list_prepend (l, emb->filename); - } - - if (gnome_vfs_mime_application_launch (emb->app, l) == GNOME_VFS_OK) { - totem_embedded_stop (emb, NULL); - } - - g_list_free (l); -} - -static void -on_about1_activate (GtkButton *button, TotemEmbedded *emb) -{ - char *backend_version, *description; - - const char *authors[] = - { - "Bastien Nocera <hadess@hadess.net>", - "Ronald Bultje <rbultje@ronald.bitfreak.net>", - NULL - }; - - if (emb->about != NULL) - { - gtk_window_present (GTK_WINDOW (emb->about)); - return; - } - - backend_version = bacon_video_widget_get_backend_name (emb->bvw); - description = g_strdup_printf (_("Browser Plugin using %s"), - backend_version); - - emb->about = g_object_new (GTK_TYPE_ABOUT_DIALOG, - "name", _("Totem Browser Plugin"), - "version", VERSION, - "copyright", _("Copyright \xc2\xa9 2002-2006 Bastien Nocera"), - "comments", description, - "authors", authors, - "translator-credits", _("translator-credits"), - "logo-icon-name", "totem", - NULL); - - g_free (backend_version); - g_free (description); - - totem_interface_set_transient_for (GTK_WINDOW (emb->about), - GTK_WINDOW (emb->window)); - - g_object_add_weak_pointer (G_OBJECT (emb->about), - (gpointer *)&emb->about); - g_signal_connect (G_OBJECT (emb->about), "response", - G_CALLBACK (gtk_widget_destroy), NULL); - - gtk_widget_show (emb->about); -} - -static void -on_copy_location1_activate (GtkButton *button, TotemEmbedded *emb) -{ - GtkClipboard *clip; - const char *filename; - - if (IS_FD_STREAM) { - filename = emb->orig_filename; - } else if (emb->href != NULL) { - filename = emb->href; - } else { - filename = emb->filename; - } - - /* Set both the middle-click and the super-paste buffers */ - clip = gtk_clipboard_get_for_display - (gdk_display_get_default(), GDK_SELECTION_CLIPBOARD); - gtk_clipboard_set_text (clip, filename, -1); - clip = gtk_clipboard_get_for_display - (gdk_display_get_default(), GDK_SELECTION_PRIMARY); - gtk_clipboard_set_text (clip, filename, -1); -} - -static void -on_play_pause (GtkWidget *widget, TotemEmbedded *emb) -{ - if (emb->state == STATE_PLAYING) { - totem_embedded_pause (emb, NULL); - } else { - totem_embedded_play (emb, NULL); - } -} - -static void -on_got_redirect (GtkWidget *bvw, const char *mrl, TotemEmbedded *emb) -{ - gchar *new_filename; - - g_message ("url: %s", emb->orig_filename); - g_message ("redirect: %s", mrl); - - if (emb->orig_filename) - new_filename = totem_resolve_relative_link (emb->orig_filename, mrl); - else - new_filename = totem_resolve_relative_link (emb->filename, mrl); - - g_free (emb->filename); - emb->filename = new_filename; - - bacon_video_widget_close (emb->bvw); - totem_embedded_set_state (emb, STATE_STOPPED); - - if (totem_embedded_open (emb) != FALSE) - totem_embedded_play (emb, NULL); -} - -static gboolean -on_video_button_press_event (BaconVideoWidget *bvw, GdkEventButton *event, - TotemEmbedded *emb) -{ - if (event->type == GDK_BUTTON_PRESS && - event->button == 1 && - emb->href != NULL) - { - if (emb->target != NULL && - !g_ascii_strcasecmp (emb->target, "QuicktimePlayer")) { - gchar *cmd; - GError *err = NULL; - - if (g_file_test ("./totem", - G_FILE_TEST_EXISTS) != FALSE) { - cmd = g_strdup_printf ("./totem %s", - emb->href); - } else { - cmd = g_strdup_printf (BINDIR"/totem %s", - emb->href); - } - if (!g_spawn_command_line_async (cmd, &err)) { - totem_interface_error_blocking ( - _("Failed to start stand-alone movie player"), - err ? err->message : _("Unknown reason"), - GTK_WINDOW (emb->window)); - } - g_free (cmd); - } else { - g_free (emb->filename); - emb->filename = emb->href; - emb->href = NULL; - bacon_video_widget_close (emb->bvw); - totem_embedded_set_state (emb, STATE_STOPPED); - - if (emb->controller_hidden != FALSE) { - GtkWidget *controls; - controls = glade_xml_get_widget (emb->xml, "controls"); - gtk_widget_show (controls); - } - - - if (totem_embedded_open (emb) != FALSE) - totem_embedded_play (emb, NULL); - - return TRUE; - } - } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { - GtkMenu *menu; - - menu = GTK_MENU (glade_xml_get_widget (emb->menuxml, "menu")); - gtk_menu_popup (menu, NULL, NULL, NULL, NULL, - event->button, event->time); - - return TRUE; - } - - return FALSE; -} - -static void -on_eos_event (GtkWidget *bvw, TotemEmbedded *emb) -{ - totem_embedded_set_state (emb, STATE_STOPPED); - gtk_adjustment_set_value (emb->seekadj, 0); - - /* No playlist if we have fd://0, right? */ - if (IS_FD_STREAM) { - totem_embedded_set_pp_state (emb, FALSE); - } else if (emb->num_items == 1) { - if (g_str_has_prefix (emb->filename, "file://") != FALSE) { - if (bacon_video_widget_is_seekable (emb->bvw) != FALSE) { - bacon_video_widget_pause (emb->bvw); - bacon_video_widget_seek (emb->bvw, 0.0, NULL); - } else { - bacon_video_widget_close (emb->bvw); - totem_embedded_open (emb); - } - } else { - bacon_video_widget_close (emb->bvw); - totem_embedded_open (emb); - } - if (emb->repeat != FALSE && emb->noautostart == FALSE) - totem_embedded_play (emb, NULL); - } else { - /* Multiple items on the playlist */ - gboolean eop = FALSE, res; - - if (emb->current->next == NULL) { - emb->current = emb->playlist; - eop = TRUE; - } else { - emb->current = emb->current->next; - } - - g_free (emb->filename); - emb->filename = g_strdup (emb->current->data); - bacon_video_widget_close (emb->bvw); - res = totem_embedded_open (emb); - if (res != FALSE && - ((eop != FALSE && emb->repeat != FALSE) - || (eop == FALSE))) { - totem_embedded_play (emb, NULL); - } - } -} - -static void -on_error_event (BaconVideoWidget *bvw, char *message, - gboolean playback_stopped, gboolean fatal, TotemEmbedded *emb) -{ - if (playback_stopped) - totem_embedded_set_state (emb, STATE_STOPPED); - - if (fatal == FALSE) { - totem_interface_error (_("An error occurred"), message, - GTK_WINDOW (emb->window)); - } else { - totem_embedded_error_and_exit (_("An error occurred"), - message, emb); - } -} - -static void -cb_vol (GtkWidget *val, TotemEmbedded *emb) -{ - bacon_video_widget_set_volume (emb->bvw, - bacon_volume_button_get_value (BACON_VOLUME_BUTTON (val))); -} - -static gboolean -on_volume_scroll_event (GtkWidget *win, GdkEventScroll *event, TotemEmbedded *emb) -{ - GtkWidget *vbut; - int vol, offset; - - switch (event->direction) { - case GDK_SCROLL_UP: - offset = VOLUME_UP_OFFSET; - break; - case GDK_SCROLL_DOWN: - offset = VOLUME_DOWN_OFFSET; - break; - default: - return FALSE; - } - - vbut = glade_xml_get_widget (emb->xml, "volume_button"); - vol = bacon_volume_button_get_value (BACON_VOLUME_BUTTON (vbut)); - bacon_volume_button_set_value (BACON_VOLUME_BUTTON (vbut), - vol + offset); - - return FALSE; -} - -static void -on_tick (GtkWidget *bvw, - gint64 current_time, - gint64 stream_length, - float current_position, - gboolean seekable, - TotemEmbedded *emb) -{ - if (emb->state != STATE_STOPPED) { - gtk_widget_set_sensitive (emb->seek, seekable); - if (emb->seeking == FALSE) - gtk_adjustment_set_value (emb->seekadj, - current_position * 65535); - if (stream_length == 0) { - totem_statusbar_set_time_and_length (emb->statusbar, - (int) (current_time / 1000), -1); - } else { - totem_statusbar_set_time_and_length (emb->statusbar, - (int) (current_time / 1000), - (int) (stream_length / 1000)); - } - } -} - -static gboolean -on_seek_start (GtkWidget *widget, GdkEventButton *event, TotemEmbedded *emb) -{ - emb->seeking = TRUE; - - return FALSE; -} - -static gboolean -cb_on_seek (GtkWidget *widget, GdkEventButton *event, TotemEmbedded *emb) -{ - bacon_video_widget_seek (emb->bvw, - gtk_range_get_value (GTK_RANGE (widget)) / 65535, NULL); - emb->seeking = FALSE; - - return FALSE; -} - -static void -totem_embedded_add_children (TotemEmbedded *emb) -{ - GtkWidget *child, *container, *pp_button, *vbut; - BvwUseType type = BVW_USE_TYPE_VIDEO; - GError *err = NULL; - GConfClient *gc; - int volume; - - emb->xml = totem_interface_load_with_root ("mozilla-viewer.glade", - "vbox1", _("Plugin"), TRUE, - GTK_WINDOW (emb->window)); - emb->menuxml = totem_interface_load_with_root ("mozilla-viewer.glade", - "menu", _("Menu"), TRUE, - GTK_WINDOW (emb->window)); - - if (emb->xml == NULL || emb->menuxml == NULL) - totem_embedded_exit (emb); - - child = glade_xml_get_widget (emb->xml, "vbox1"); - gtk_container_add (GTK_CONTAINER (emb->window), child); - - if (emb->hidden) - type = BVW_USE_TYPE_AUDIO; - - emb->bvw = BACON_VIDEO_WIDGET (bacon_video_widget_new - (-1, -1, type, &err)); - - if (emb->bvw == NULL) - { - totem_embedded_error_and_exit (_("The Totem plugin could not startup."), err != NULL ? err->message : _("No reason."), emb); - if (err != NULL) - g_error_free (err); - } - - g_signal_connect (G_OBJECT(emb->bvw), "got-redirect", - G_CALLBACK (on_got_redirect), emb); - g_signal_connect (G_OBJECT (emb->bvw), "eos", - G_CALLBACK (on_eos_event), emb); - g_signal_connect (G_OBJECT (emb->bvw), "error", - G_CALLBACK (on_error_event), emb); - g_signal_connect (G_OBJECT(emb->bvw), "button-press-event", - G_CALLBACK (on_video_button_press_event), emb); - g_signal_connect (G_OBJECT(emb->bvw), "tick", - G_CALLBACK (on_tick), emb); - - container = glade_xml_get_widget (emb->xml, "hbox4"); - gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (emb->bvw)); - if (type == BVW_USE_TYPE_VIDEO) { - gtk_widget_realize (GTK_WIDGET (emb->bvw)); - gtk_widget_show (GTK_WIDGET (emb->bvw)); - } - - emb->seek = glade_xml_get_widget (emb->xml, "time_hscale"); - emb->seekadj = gtk_range_get_adjustment (GTK_RANGE (emb->seek)); - g_signal_connect (emb->seek, "button-press-event", - G_CALLBACK (on_seek_start), emb); - g_signal_connect (emb->seek, "button-release-event", - G_CALLBACK (cb_on_seek), emb); - - pp_button = glade_xml_get_widget (emb->xml, "pp_button"); - g_signal_connect (G_OBJECT (pp_button), "clicked", - G_CALLBACK (on_play_pause), emb); - - gc = gconf_client_get_default (); - volume = gconf_client_get_int (gc, GCONF_PREFIX"/volume", NULL); - g_object_unref (G_OBJECT (gc)); - - bacon_video_widget_set_volume (emb->bvw, volume); - vbut = glade_xml_get_widget (emb->xml, "volume_button"); - bacon_volume_button_set_value (BACON_VOLUME_BUTTON (vbut), volume); - g_signal_connect (G_OBJECT (vbut), "value-changed", - G_CALLBACK (cb_vol), emb); - gtk_widget_add_events (vbut, GDK_SCROLL_MASK); - g_signal_connect (G_OBJECT (vbut), "scroll_event", - G_CALLBACK (on_volume_scroll_event), emb); - - emb->statusbar = TOTEM_STATUSBAR (glade_xml_get_widget - (emb->xml, "emb_statusbar")); - - gtk_widget_realize (emb->window); - gtk_widget_set_size_request (emb->window, emb->width, emb->height); - - if (emb->controller_hidden != FALSE) { - child = glade_xml_get_widget (emb->xml, "controls"); - gtk_widget_hide (child); - } - - /* popup */ - child = glade_xml_get_widget (emb->menuxml, "about1"); - g_signal_connect (G_OBJECT (child), "activate", - G_CALLBACK (on_about1_activate), emb); - child = glade_xml_get_widget (emb->menuxml, "copy_location1"); - g_signal_connect (G_OBJECT (child), "activate", - G_CALLBACK (on_copy_location1_activate), emb); - child = glade_xml_get_widget (emb->menuxml, "preferences1"); - gtk_widget_hide (child); -} - -static void -totem_embedded_create_cursor (TotemEmbedded *emb) -{ - GtkWidget *label; - GdkPixbuf *icon; - - label = gtk_label_new (""); - icon = gtk_widget_render_icon (label, GTK_STOCK_MEDIA_PLAY, - GTK_ICON_SIZE_BUTTON, NULL); - gtk_widget_destroy (label); - emb->cursor = gdk_cursor_new_from_pixbuf (gdk_display_get_default (), - icon, - gdk_pixbuf_get_width (icon) / 2, - gdk_pixbuf_get_height (icon) / 2); - gdk_pixbuf_unref (icon); -} - -static void -entry_added (TotemPlParser *parser, const char *uri, const char *title, - const char *genre, gpointer data) -{ - TotemEmbedded *emb = (TotemEmbedded *) data; - - g_print ("added URI '%s' with title '%s' genre '%s'\n", uri, - title ? title : "empty", genre); - - //FIXME need new struct to hold that - emb->playlist = g_list_prepend (emb->playlist, g_strdup (uri)); - emb->num_items++; -} - -static gboolean -totem_embedded_push_parser (gpointer data) -{ - TotemPlParser *parser = totem_pl_parser_new (); - TotemEmbedded *emb = (TotemEmbedded *) data; - TotemPlParserResult res; - - parser = totem_pl_parser_new (); - g_object_set (G_OBJECT (parser), "force", TRUE, NULL); - g_object_set (G_OBJECT (parser), "disable-unsafe", TRUE, NULL); - g_signal_connect (G_OBJECT (parser), "entry", G_CALLBACK (entry_added), emb); - res = totem_pl_parser_parse (parser, emb->filename, FALSE); - g_object_unref (parser); - - if (res != TOTEM_PL_PARSER_RESULT_SUCCESS) { - //FIXME show a proper error message - switch (res) { - case TOTEM_PL_PARSER_RESULT_UNHANDLED: - g_print ("url '%s' unhandled\n", emb->filename); - break; - case TOTEM_PL_PARSER_RESULT_ERROR: - g_print ("error handling url '%s'\n", emb->filename); - break; - case TOTEM_PL_PARSER_RESULT_IGNORED: - g_print ("ignored url '%s'\n", emb->filename); - break; - default: - g_assert_not_reached (); - ;; - } - } - - /* Check if we have anything in the playlist now */ - if (emb->playlist == NULL) { - g_message ("NO PLAYLIST"); - totem_embedded_error_and_exit ("Can't parse that", - "no files", - emb); - //FIXME error out - return FALSE; - } - - emb->playlist = g_list_reverse (emb->playlist); - g_free (emb->filename); - emb->filename = g_strdup (emb->playlist->data); - emb->current = emb->playlist; - - g_main_loop_quit (emb->loop); - - return FALSE; -} - -static void embedded (GtkPlug *plug, TotemEmbedded *emb) -{ - emb->embedded_done = TRUE; -} - -static int arg_width = -1; -static int arg_height = -1; -static char *arg_url = NULL; -static char *arg_href = NULL; -static char *arg_target = NULL; -static char *arg_mime_type = NULL; -static char **arg_remaining = NULL; -static Window xid = 0; -static gboolean arg_no_controls = FALSE; -static gboolean arg_statusbar = FALSE; -static gboolean arg_hidden = FALSE; -static gboolean arg_is_playlist = FALSE; -static gboolean arg_repeat = FALSE; -static gboolean arg_no_autostart = FALSE; - -GtkWidget * -totem_volume_create (void) -{ - GtkWidget *widget; - - widget = bacon_volume_button_new (GTK_ICON_SIZE_MENU, - 0, 100, -1); - gtk_widget_show (widget); - - return widget; -} - -GtkWidget * -totem_statusbar_create (void) -{ - GtkWidget *widget; - - widget = totem_statusbar_new (); - totem_statusbar_set_has_resize_grip (TOTEM_STATUSBAR (widget), FALSE); - if (arg_statusbar != FALSE) - gtk_widget_show (widget); - - return widget; -} - -static gboolean -parse_xid (const gchar *option_name, - const gchar *value, - gpointer data, - GError **error) -{ - xid = (Window) g_ascii_strtoull (value, NULL, 10); - - return TRUE; -} - -static GOptionEntry option_entries [] = -{ - { TOTEM_OPTION_WIDTH, 0, 0, G_OPTION_ARG_INT, &arg_width, NULL, NULL }, - { TOTEM_OPTION_HEIGHT, 0, 0, G_OPTION_ARG_INT, &arg_height, NULL, NULL }, - { TOTEM_OPTION_URL, 0, 0, G_OPTION_ARG_STRING, &arg_url, NULL, NULL }, - { TOTEM_OPTION_HREF, 0, 0, G_OPTION_ARG_STRING, &arg_href, NULL, NULL }, - { TOTEM_OPTION_XID, 0, 0, G_OPTION_ARG_CALLBACK, parse_xid, NULL, NULL }, - { TOTEM_OPTION_TARGET, 0, 0, G_OPTION_ARG_STRING, &arg_target, NULL, NULL }, - { TOTEM_OPTION_MIMETYPE, 0, 0, G_OPTION_ARG_STRING, &arg_mime_type, NULL, NULL }, - { TOTEM_OPTION_CONTROLS_HIDDEN, 0, 0, G_OPTION_ARG_NONE, &arg_no_controls, NULL, NULL }, - { TOTEM_OPTION_STATUSBAR, 0, 0, G_OPTION_ARG_NONE, &arg_statusbar, NULL, NULL }, - { TOTEM_OPTION_HIDDEN, 0, 0, G_OPTION_ARG_NONE, &arg_hidden, NULL, NULL }, - { TOTEM_OPTION_PLAYLIST, 0, 0, G_OPTION_ARG_NONE, &arg_is_playlist, NULL, NULL }, - { TOTEM_OPTION_REPEAT, 0, 0, G_OPTION_ARG_NONE, &arg_repeat, NULL, NULL }, - { TOTEM_OPTION_NOAUTOSTART, 0, 0, G_OPTION_ARG_NONE, &arg_no_autostart, NULL, NULL }, - { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY /* STRING? */, &arg_remaining, NULL }, - { NULL } -}; - - -int main (int argc, char **argv) -{ - TotemEmbedded *emb; - DBusGProxy *proxy; - DBusGConnection *conn; - guint res; - gchar *svcname; - GError *e = NULL; - - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - -#ifdef GNOME_ENABLE_DEBUG - { - int i; - g_print ("Viewer args: "); - for (i = 0; i < argc; i++) - g_print ("%s ", argv[i]); - g_print ("\n"); - } -#endif - - if (XInitThreads () == 0) - { - gtk_init (&argc, &argv); - totem_embedded_error_and_exit (_("Could not initialise the thread-safe libraries."), _("Verify your system installation. The Totem plugin will now exit."), NULL); - } - - g_thread_init (NULL); - - if (!gtk_init_with_args (&argc, &argv, NULL, option_entries, GETTEXT_PACKAGE, &e)) - { - g_print ("%s\n", e->message); - g_error_free (e); - exit (1); - } - - /* FIXME: are there enough checks? */ - if (arg_remaining == NULL) { - return 1; - } - - bacon_video_widget_init_backend (NULL, NULL); - gnome_vfs_init (); - - dbus_g_object_type_install_info (TOTEM_TYPE_EMBEDDED, - &dbus_glib_totem_embedded_object_info); - svcname = g_strdup_printf ("org.totem_%d.MozillaPluginService", - getpid()); - if (!(conn = dbus_g_bus_get (DBUS_BUS_SESSION, &e)) || - !(proxy = dbus_g_proxy_new_for_name (conn, "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus")) || - !dbus_g_proxy_call (proxy, "RequestName", &e, - G_TYPE_STRING, svcname, - G_TYPE_UINT, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT, - G_TYPE_INVALID, - G_TYPE_UINT, &res, - G_TYPE_INVALID)) { - g_print ("Failed to get DBUS connection for %s: %s\n", - svcname, e->message); - return 1; - } - g_free (svcname); - - emb = g_object_new (TOTEM_TYPE_EMBEDDED, NULL); - - emb->state = STATE_STOPPED; - emb->width = arg_width; - emb->height = arg_height; - emb->controller_hidden = arg_no_controls; - emb->orig_filename = arg_url; - emb->href = arg_href; - emb->filename = arg_remaining ? arg_remaining[0] : NULL; - emb->target = arg_target; - if (arg_mime_type != NULL) { - emb->mimetype = arg_mime_type; - } else { - emb->mimetype = g_strdup (gnome_vfs_get_mime_type_for_name (emb->filename)); - } - emb->hidden = arg_hidden; - emb->is_playlist = arg_is_playlist; - emb->repeat = arg_repeat; - emb->noautostart = arg_no_autostart; - - dbus_g_connection_register_g_object (conn, "/TotemEmbedded", - G_OBJECT (emb)); - - /* XEMBED or stand-alone */ - if (xid != 0) { - GtkWidget *window; - - /* The miraculous XEMBED protocol */ - window = gtk_plug_new ((GdkNativeWindow) xid); - gtk_signal_connect (GTK_OBJECT(window), "embedded", - G_CALLBACK (embedded), NULL); - gtk_widget_realize (window); - - emb->window = window; - } else { - /* Stand-alone version */ - emb->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - } - - /* Hidden or not as well? */ - totem_embedded_add_children (emb); - totem_embedded_create_cursor (emb); - if (emb->hidden == FALSE) - gtk_widget_show (emb->window); - - /* wait until we're embedded if we're to be, or shown */ - if (xid != 0) { - while (emb->embedded_done == FALSE && gtk_events_pending ()) - gtk_main_iteration (); - } else { - while (gtk_events_pending ()) - gtk_main_iteration (); - } - - /* Do we have a playlist we need to setup ourselves? */ - if (emb->is_playlist != FALSE) { - g_idle_add (totem_embedded_push_parser, emb); - emb->loop = g_main_loop_new (NULL, FALSE); - g_main_loop_run (emb->loop); - g_main_loop_unref (emb->loop); - } else { - emb->playlist = g_list_prepend (emb->playlist, - g_strdup (emb->filename)); - emb->num_items++; - } - - if (totem_embedded_open (emb) != FALSE - && emb->noautostart == FALSE) { - totem_embedded_play (emb, NULL); - } - - gtk_main (); - - return 0; -} - |