diff options
Diffstat (limited to 'src/npplayer.c')
-rw-r--r-- | src/npplayer.c | 1545 |
1 files changed, 1545 insertions, 0 deletions
diff --git a/src/npplayer.c b/src/npplayer.c new file mode 100644 index 0000000..e7f67bb --- /dev/null +++ b/src/npplayer.c @@ -0,0 +1,1545 @@ +/* +* Copyright (C) 2007 Koos Vriezen <[email protected]> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser 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 +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* gcc -o knpplayer `pkg-config --libs --cflags gtk+-x11-2.0` `pkg-config --libs --cflags dbus-glib-1` `pkg-config --libs gthread-2.0` npplayer.c + +http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/ +http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html +*/ + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/time.h> +#include <fcntl.h> + +#include <glib/gprintf.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> + +#define DBUS_API_SUBJECT_TO_CHANGE +#include <dbus/dbus.h> +#include <dbus/dbus-glib.h> + +#define XP_UNIX +#define MOZ_X11 +#include "moz-sdk/npupp.h" + +typedef const char* (* NP_LOADDS NP_GetMIMEDescriptionUPP)(); +typedef NPError (* NP_GetValueUPP)(void *inst, NPPVariable var, void *value); +typedef NPError (* NP_InitializeUPP)(NPNetscapeFuncs*, NPPluginFuncs*); +typedef NPError (* NP_ShutdownUPP)(void); + +static gchar *plugin; +static gchar *object_url; +static gchar *mimetype; + +static DBusConnection *dbus_connection; +static char *service_name; +static gchar *callback_service; +static gchar *callback_path; +static GModule *library; +static GtkWidget *xembed; +static Window socket_id; +static Window parent_id; +static int top_w, top_h; +static int update_dimension_timer; +static int stdin_read_watch; + +static NPPluginFuncs np_funcs; /* plugin functions */ +static NPP npp; /* single instance of the plugin */ +static NPWindow np_window; +static NPObject *js_window; +static NPObject *scriptable_peer; +static NPSavedData *saved_data; +static NPClass js_class; +static GTree *stream_list; +static gpointer current_stream_id; +static uint32_t stream_chunk_size; +static char stream_buf[32 * 1024]; +static unsigned int stream_buf_pos; +static int stream_id_counter; +static GTree *identifiers; +static int js_obj_counter; +typedef struct _StreamInfo { + NPStream np_stream; + /*unsigned int stream_buf_pos;*/ + unsigned int stream_pos; + unsigned int total; + unsigned int reason; + char *url; + char *mimetype; + char *target; + bool notify; + bool called_plugin; + bool destroyed; +} StreamInfo; +struct JsObject; +typedef struct _JsObject { + NPObject npobject; + struct _JsObject * parent; + char * name; +} JsObject; + +static NP_GetMIMEDescriptionUPP npGetMIMEDescription; +static NP_GetValueUPP npGetValue; +static NP_InitializeUPP npInitialize; +static NP_ShutdownUPP npShutdown; + +static void callFunction(int stream, const char *func, int first_arg_type, ...); +static void readStdin (gpointer d, gint src, GdkInputCondition cond); +static char * evaluate (const char *script); + +/*----------------%<---------------------------------------------------------*/ + +static void print (const char * format, ...) { + va_list vl; + va_start (vl, format); + vprintf (format, vl); + va_end (vl); + fflush (stdout); +} + +/*----------------%<---------------------------------------------------------*/ + +static gint streamCompare (gconstpointer a, gconstpointer b) { + return (long)a - (long)b; +} + +static void freeStream (StreamInfo *si) { + if (!g_tree_remove (stream_list, si->np_stream.ndata)) + print ("WARNING freeStream not in tree\n"); + g_free (si->url); + if (si->mimetype) + g_free (si->mimetype); + if (si->target) + g_free (si->target); + free (si); +} + +static gboolean requestStream (void * p) { + StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p); + if (si) { + if (!callback_service) + current_stream_id = p; + if (!stdin_read_watch) + stdin_read_watch = gdk_input_add (0, GDK_INPUT_READ,readStdin,NULL); + if (si->target) + callFunction ((int)(long)p, "getUrl", + DBUS_TYPE_STRING, &si->url, + DBUS_TYPE_STRING, &si->target, DBUS_TYPE_INVALID); + else + callFunction ((int)(long)p, "getUrl", + DBUS_TYPE_STRING, &si->url, DBUS_TYPE_INVALID); + } else { + print ("requestStream %d not found", (long) p); + } + return 0; /* single shot */ +} + +static gboolean destroyStream (void * p) { + StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p); + print ("FIXME destroyStream\n"); + if (si) + callFunction ((int)(long)p, "destroy", DBUS_TYPE_INVALID); + return 0; /* single shot */ +} + +static void removeStream (void * p) { + StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p); + + if (si) { + print ("removeStream %d rec:%d reason %d\n", (long) p, si->stream_pos, si->reason); + if (!si->destroyed) { + if (si->called_plugin && !si->target) { + si->np_stream.end = si->total; + np_funcs.destroystream (npp, &si->np_stream, si->reason); + } + if (si->notify) + np_funcs.urlnotify (npp, + si->url, si->reason, si->np_stream.notifyData); + } + freeStream (si); + } +} + +static int32_t writeStream (gpointer p, char *buf, uint32_t count) { + int32_t sz = -1; + StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p); + /*print ("writeStream found %d count %d\n", !!si, count);*/ + if (si) { + if (si->reason > NPERR_NO_ERROR) { + sz = count; /* stream closed, skip remainings */ + } else { + if (!si->called_plugin) { + uint16 stype = NP_NORMAL; + NPError err = np_funcs.newstream (npp, si->mimetype + ? si->mimetype + : "text/plain", + &si->np_stream, 0, &stype); + if (err != NPERR_NO_ERROR) { + g_printerr ("newstream error %d\n", err); + destroyStream (p); + return count; /* stream not accepted, skip remainings */ + } + print ("newStream %d type:%d\n", (long) p, stype); + si->called_plugin = true; + } + if (count) /* urls with a target returns zero bytes */ + sz = np_funcs.writeready (npp, &si->np_stream); + if (sz > 0) { + sz = np_funcs.write (npp, &si->np_stream, si->stream_pos, + (int32_t) count > sz ? sz : (int32_t) count, buf); + if (sz < 0) /*FIXME plugin destroys stream here*/ + g_timeout_add (0, destroyStream, p); + } else { + sz = 0; + } + si->stream_pos += sz; + if (si->stream_pos == si->total) { + if (si->stream_pos || !count) + removeStream (p); + else + g_timeout_add (0, destroyStream, p); + } + } + } + return sz; +} + +static StreamInfo *addStream (const char *url, const char *mime, const char *target, void *notify_data, bool notify) { + StreamInfo *si = (StreamInfo *) malloc (sizeof (StreamInfo)); + + memset (si, 0, sizeof (StreamInfo)); + si->url = g_strdup (url); + si->np_stream.url = si->url; + if (mime) + si->mimetype = g_strdup (mime); + if (target) + si->target = g_strdup (target); + si->np_stream.notifyData = notify_data; + si->notify = notify; + si->np_stream.ndata = (void *) (long) (stream_id_counter++); + print ("add stream %d\n", (long) si->np_stream.ndata); + g_tree_insert (stream_list, si->np_stream.ndata, si); + + g_timeout_add (0, requestStream, si->np_stream.ndata); + + return si; +} + +/*----------------%<---------------------------------------------------------*/ + +static void createJsName (JsObject * obj, char **name, uint32_t * len) { + int slen = strlen (obj->name); + if (obj->parent) { + *len += slen + 1; + createJsName (obj->parent, name, len); + } else { + *name = (char *) malloc (*len + slen + 1); + *(*name + *len + slen) = 0; + *len = 0; + } + if (obj->parent) { + *(*name + *len) = '.'; + *len += 1; + } + memcpy (*name + *len, obj->name, slen); + *len += slen; +} + +static char *nsVariant2Str (const NPVariant *value) { + char *str; + switch (value->type) { + case NPVariantType_String: + str = (char *) malloc (value->value.stringValue.utf8length + 3); + sprintf (str, "'%s'", value->value.stringValue.utf8characters); + break; + case NPVariantType_Int32: + str = (char *) malloc (16); + snprintf (str, 15, "%d", value->value.intValue); + break; + case NPVariantType_Double: + str = (char *) malloc (64); + snprintf (str, 63, "%f", value->value.doubleValue); + break; + case NPVariantType_Bool: + str = strdup (value->value.boolValue ? "true" : "false"); + break; + case NPVariantType_Null: + str = strdup ("null"); + break; + case NPVariantType_Object: + if (&js_class == value->value.objectValue->_class) { + JsObject *jv = (JsObject *) value->value.objectValue; + char *val; + uint32_t vlen = 0; + createJsName (jv, &val, &vlen); + str = strdup (val); + free (val); + } + break; + default: + str = strdup (""); + break; + } + return str; +} + +/*----------------%<---------------------------------------------------------*/ + +static NPObject * nsCreateObject (NPP instance, NPClass *aClass) { + NPObject *obj; + if (aClass && aClass->allocate) { + obj = aClass->allocate (instance, aClass); + } else { + obj = (NPObject *) malloc (sizeof (NPObject)); + memset (obj, 0, sizeof (NPObject)); + obj->_class = aClass; + /*obj = js_class.allocate (instance, &js_class);/ *add null class*/ + print ("NPN_CreateObject\n"); + } + obj->referenceCount = 1; + return obj; +} + +static NPObject *nsRetainObject (NPObject *npobj) { + /*print( "nsRetainObject %p\n", npobj);*/ + npobj->referenceCount++; + return npobj; +} + +static void nsReleaseObject (NPObject *obj) { + /*print ("NPN_ReleaseObject\n");*/ + if (! (--obj->referenceCount)) + obj->_class->deallocate (obj); +} + +static NPError nsGetURL (NPP instance, const char* url, const char* target) { + (void)instance; + print ("nsGetURL %s %s\n", url, target ? target : ""); + addStream (url, 0L, target, 0L, false); + return NPERR_NO_ERROR; +} + +static NPError nsPostURL (NPP instance, const char *url, + const char *target, uint32 len, const char *buf, NPBool file) { + (void)instance; (void)len; (void)buf; (void)file; + print ("nsPostURL %s %s\n", url, target ? target : ""); + addStream (url, 0L, target, 0L, false); + return NPERR_NO_ERROR; +} + +static NPError nsRequestRead (NPStream *stream, NPByteRange *rangeList) { + (void)stream; (void)rangeList; + print ("nsRequestRead\n"); + return NPERR_NO_ERROR; +} + +static NPError nsNewStream (NPP instance, NPMIMEType type, + const char *target, NPStream **stream) { + (void)instance; (void)type; (void)stream; (void)target; + print ("nsNewStream\n"); + return NPERR_NO_ERROR; +} + +static int32 nsWrite (NPP instance, NPStream* stream, int32 len, void *buf) { + (void)instance; (void)len; (void)buf; (void)stream; + print ("nsWrite\n"); + return 0; +} + +static NPError nsDestroyStream (NPP instance, NPStream *stream, NPError reason) { + StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, stream->ndata); + (void)instance; + print ("nsDestroyStream\n"); + if (si) { + si->reason = reason; + si->destroyed = true; + g_timeout_add (0, destroyStream, stream->ndata); + return NPERR_NO_ERROR; + } + return NPERR_NO_DATA; +} + +static void nsStatus (NPP instance, const char* message) { + (void)instance; + print ("NPN_Status %s\n", message ? message : "-"); +} + +static const char* nsUserAgent (NPP instance) { + (void)instance; + print ("NPN_UserAgent\n"); + return ""; +} + +static void *nsAlloc (uint32 size) { + return malloc (size); +} + +static void nsMemFree (void* ptr) { + free (ptr); +} + +static uint32 nsMemFlush (uint32 size) { + (void)size; + print ("NPN_MemFlush\n"); + return 0; +} + +static void nsReloadPlugins (NPBool reloadPages) { + (void)reloadPages; + print ("NPN_ReloadPlugins\n"); +} + +static JRIEnv* nsGetJavaEnv () { + print ("NPN_GetJavaEnv\n"); + return NULL; +} + +static jref nsGetJavaPeer (NPP instance) { + (void)instance; + print ("NPN_GetJavaPeer\n"); + return NULL; +} + +static NPError nsGetURLNotify (NPP instance, const char* url, const char* target, void *notify) { + (void)instance; + print ("NPN_GetURLNotify %s %s\n", url, target ? target : ""); + addStream (url, 0L, target, notify, true); + return NPERR_NO_ERROR; +} + +static NPError nsPostURLNotify (NPP instance, const char* url, const char* target, uint32 len, const char* buf, NPBool file, void *notify) { + (void)instance; (void)len; (void)buf; (void)file; + print ("NPN_PostURLNotify\n"); + addStream (url, 0L, target, notify, true); + return NPERR_NO_ERROR; +} + +static NPError nsGetValue (NPP instance, NPNVariable variable, void *value) { + print ("NPN_GetValue %d\n", variable & ~NP_ABI_MASK); + switch (variable) { + case NPNVxDisplay: + *(void**)value = (void*)(long) gdk_x11_get_default_xdisplay (); + break; + case NPNVxtAppContext: + *(void**)value = NULL; + break; + case NPNVnetscapeWindow: + print ("NPNVnetscapeWindow\n"); + break; + case NPNVjavascriptEnabledBool: + *(int*)value = 1; + break; + case NPNVasdEnabledBool: + *(int*)value = 0; + break; + case NPNVisOfflineBool: + *(int*)value = 0; + break; + case NPNVserviceManager: + *(int*)value = 0; + break; + case NPNVToolkit: + *(int*)value = NPNVGtk2; + break; + case NPNVSupportsXEmbedBool: + *(int*)value = 1; + break; + case NPNVWindowNPObject: + if (!js_window) { + JsObject *jo = (JsObject*) nsCreateObject (instance, &js_class); + jo->name = g_strdup ("window"); + js_window = (NPObject *) jo; + } + *(NPObject**)value = nsRetainObject (js_window); + break; + case NPNVPluginElementNPObject: { + JsObject * obj = (JsObject *) nsCreateObject (instance, &js_class); + obj->name = g_strdup ("this"); + *(NPObject**)value = (NPObject *) obj; + break; + } + default: + *(int*)value = 0; + print ("unknown value\n"); + return NPERR_GENERIC_ERROR; + } + return NPERR_NO_ERROR; +} + +static NPError nsSetValue (NPP instance, NPPVariable variable, void *value) { + /* NPPVpluginWindowBool */ + (void)instance; (void)value; + print ("NPN_SetValue %d\n", variable & ~NP_ABI_MASK); + return NPERR_NO_ERROR; +} + +static void nsInvalidateRect (NPP instance, NPRect *invalidRect) { + (void)instance; (void)invalidRect; + print ("NPN_InvalidateRect\n"); +} + +static void nsInvalidateRegion (NPP instance, NPRegion invalidRegion) { + (void)instance; (void)invalidRegion; + print ("NPN_InvalidateRegion\n"); +} + +static void nsForceRedraw (NPP instance) { + (void)instance; + print ("NPN_ForceRedraw\n"); +} + +static NPIdentifier nsGetStringIdentifier (const NPUTF8* name) { + /*print ("NPN_GetStringIdentifier %s\n", name);*/ + gpointer id = g_tree_lookup (identifiers, name); + if (!id) { + id = strdup (name); + g_tree_insert (identifiers, id, id); + } + return id; +} + +static void nsGetStringIdentifiers (const NPUTF8** names, int32_t nameCount, + NPIdentifier* ids) { + (void)names; (void)nameCount; (void)ids; + print ("NPN_GetStringIdentifiers\n"); +} + +static NPIdentifier nsGetIntIdentifier (int32_t intid) { + print ("NPN_GetIntIdentifier %d\n", intid); + return (NPIdentifier) (long) intid; +} + +static bool nsIdentifierIsString (NPIdentifier name) { + print ("NPN_IdentifierIsString\n"); + return !!g_tree_lookup (identifiers, name); +} + +static NPUTF8 * nsUTF8FromIdentifier (NPIdentifier name) { + print ("NPN_UTF8FromIdentifier\n"); + char *str = g_tree_lookup (identifiers, name); + if (str) + return strdup (str); + return NULL; +} + +static int32_t nsIntFromIdentifier (NPIdentifier identifier) { + print ("NPN_IntFromIdentifier\n"); + return (int32_t) (long) identifier; +} + +static bool nsInvoke (NPP instance, NPObject * npobj, NPIdentifier method, + const NPVariant *args, uint32_t arg_count, NPVariant *result) { + (void)instance; + /*print ("NPN_Invoke %s\n", id);*/ + return npobj->_class->invoke (npobj, method, args, arg_count, result); +} + +static bool nsInvokeDefault (NPP instance, NPObject * npobj, + const NPVariant * args, uint32_t arg_count, NPVariant * result) { + (void)instance; + return npobj->_class->invokeDefault (npobj,args, arg_count, result); +} + +static bool nsEvaluate (NPP instance, NPObject * npobj, NPString * script, + NPVariant * result) { + char * this_var; + char * this_var_type; + char * this_var_string; + char * jsscript; + (void) npobj; /*FIXME scope, search npobj window*/ + print ("NPN_Evaluate:"); + + /* assign to a js variable */ + this_var = (char *) malloc (64); + sprintf (this_var, "this.__kmplayer__obj_%d", js_obj_counter); + + jsscript = (char *) malloc (strlen (this_var) + script->utf8length + 3); + sprintf (jsscript, "%s=%s;", this_var, script->utf8characters); + this_var_string = evaluate (jsscript); + free (jsscript); + + if (this_var_string) { + /* get type of js this_var */ + jsscript = (char *) malloc (strlen (this_var) + 9); + sprintf (jsscript, "typeof %s;", this_var); + this_var_type = evaluate (jsscript); + free (jsscript); + + if (this_var_type) { + if (!strcasecmp (this_var_type, "undefined")) { + result->type = NPVariantType_Null; + } else if (!strcasecmp (this_var_type, "object")) { + JsObject *jo = (JsObject *)nsCreateObject (instance, &js_class); + js_obj_counter++; + result->type = NPVariantType_Object; + jo->name = g_strdup (this_var); + result->value.objectValue = (NPObject *)jo; + } else { /* FIXME numbers/void/undefined*/ + result->type = NPVariantType_String; + result->value.stringValue.utf8characters = + g_strdup (this_var_string); + result->value.stringValue.utf8length=strlen (this_var_string)+1; + } + g_free (this_var_type); + } + g_free (this_var_string); + } else { + print (" => error\n"); + return false; + } + free (this_var); + + return true; +} + +static bool nsGetProperty (NPP instance, NPObject * npobj, + NPIdentifier property, NPVariant * result) { + (void)instance; + return npobj->_class->getProperty (npobj, property, result); +} + +static bool nsSetProperty (NPP instance, NPObject * npobj, + NPIdentifier property, const NPVariant *value) { + (void)instance; + return npobj->_class->setProperty (npobj, property, value); +} + +static bool nsRemoveProperty (NPP inst, NPObject * npobj, NPIdentifier prop) { + (void)inst; + return npobj->_class->removeProperty (npobj, prop); +} + +static bool nsHasProperty (NPP instance, NPObject * npobj, NPIdentifier prop) { + (void)instance; + return npobj->_class->hasProperty (npobj, prop); +} + +static bool nsHasMethod (NPP instance, NPObject * npobj, NPIdentifier method) { + (void)instance; + return npobj->_class->hasMethod (npobj, method); +} + +static void nsReleaseVariantValue (NPVariant * variant) { + /*print ("NPN_ReleaseVariantValue\n");*/ + switch (variant->type) { + case NPVariantType_String: + if (variant->value.stringValue.utf8characters) + g_free ((char *) variant->value.stringValue.utf8characters); + break; + case NPVariantType_Object: + if (variant->value.objectValue) + nsReleaseObject (variant->value.objectValue); + break; + default: + break; + } + variant->type = NPVariantType_Null; +} + +static void nsSetException (NPObject *npobj, const NPUTF8 *message) { + (void)npobj; + print ("NPN_SetException %s\n", message ? message : "-"); +} + +static bool nsPushPopupsEnabledState (NPP instance, NPBool enabled) { + (void)instance; + print ("NPN_PushPopupsEnabledState %d\n", enabled); + return false; +} + +static bool nsPopPopupsEnabledState (NPP instance) { + (void)instance; + print ("NPN_PopPopupsEnabledState\n"); + return false; +} + +/*----------------%<---------------------------------------------------------*/ + +static NPObject * windowClassAllocate (NPP instance, NPClass *aClass) { + (void)instance; + /*print ("windowClassAllocate\n");*/ + JsObject * jo = (JsObject *) malloc (sizeof (JsObject)); + memset (jo, 0, sizeof (JsObject)); + jo->npobject._class = aClass; + return (NPObject *) jo; +} + +static void windowClassDeallocate (NPObject *npobj) { + JsObject *jo = (JsObject *) npobj; + /*print ("windowClassDeallocate\n");*/ + if (jo->parent) { + nsReleaseObject ((NPObject *) jo->parent); + } else if (jo->name && !strncmp (jo->name, "this.__kmplayer__obj_", 21)) { + char *script = (char *) malloc (strlen (jo->name) + 7); + char *result; + char *counter = strrchr (jo->name, '_'); + sprintf (script, "%s=null;", jo->name); + result = evaluate (script); + free (script); + g_free (result); + if (counter) { + int c = strtol (counter +1, NULL, 10); + if (c == js_obj_counter -1) + js_obj_counter--; /*poor man's variable name reuse */ + } + } + if (jo->name) + g_free (jo->name); + if (npobj == js_window) { + print ("WARNING deleting window object\n"); + js_window = NULL; + } + free (npobj); +} + +static void windowClassInvalidate (NPObject *npobj) { + (void)npobj; + print ("windowClassInvalidate\n"); +} + +static bool windowClassHasMethod (NPObject *npobj, NPIdentifier name) { + (void)npobj; (void)name; + print ("windowClassHasMehtod\n"); + return false; +} + +static bool windowClassInvoke (NPObject *npobj, NPIdentifier method, + const NPVariant *args, uint32_t arg_count, NPVariant *result) { + JsObject * jo = (JsObject *) npobj; + NPString str = { NULL, 0 }; + char buf[512]; + int pos, i; + bool res; + char * id = (char *) g_tree_lookup (identifiers, method); + /*print ("windowClassInvoke\n");*/ + + result->type = NPVariantType_Null; + result->value.objectValue = NULL; + + if (!id) { + print ("Invoke invalid id\n"); + return false; + } + print ("Invoke %s\n", id); + createJsName (jo, (char **)&str.utf8characters, &str.utf8length); + pos = snprintf (buf, sizeof (buf), "%s.%s(", str.utf8characters, id); + free ((char *) str.utf8characters); + for (i = 0; i < arg_count; i++) { + char *arg = nsVariant2Str (args + i); + pos += snprintf (buf + pos, sizeof (buf) - pos, i ? ",%s" : "%s", arg); + free (arg); + } + pos += snprintf (buf + pos, sizeof (buf) - pos, ")"); + + str.utf8characters = buf; + str.utf8length = pos; + res = nsEvaluate (npp, npobj, &str, result); + + return true; +} + +static bool windowClassInvokeDefault (NPObject *npobj, + const NPVariant *args, uint32_t arg_count, NPVariant *result) { + (void)npobj; (void)args; (void)arg_count; (void)result; + print ("windowClassInvokeDefault\n"); + return false; +} + +static bool windowClassHasProperty (NPObject *npobj, NPIdentifier name) { + (void)npobj; (void)name; + print ("windowClassHasProperty\n"); + return false; +} + +static bool windowClassGetProperty (NPObject *npobj, NPIdentifier property, + NPVariant *result) { + char * id = (char *) g_tree_lookup (identifiers, property); + JsObject jo; + NPString fullname = { NULL, 0 }; + bool res; + + print ("GetProperty %s\n", id); + result->type = NPVariantType_Null; + result->value.objectValue = NULL; + + if (!id) + return false; + + if (!strcmp (((JsObject *) npobj)->name, "window") && + !strcmp (id, "top")) { + result->type = NPVariantType_Object; + result->value.objectValue = nsRetainObject (js_window); + return true; + } + + jo.name = id; + jo.parent = (JsObject *) npobj; + createJsName (&jo, (char **)&fullname.utf8characters, &fullname.utf8length); + + res = nsEvaluate (npp, npobj, &fullname, result); + + free ((char *) fullname.utf8characters); + + return res; +} + +static bool windowClassSetProperty (NPObject *npobj, NPIdentifier property, + const NPVariant *value) { + char *id = (char *) g_tree_lookup (identifiers, property); + char *script, *var_name, *var_val, *res; + JsObject jo; + uint32_t len = 0; + + if (!id) + return false; + + jo.name = id; + jo.parent = (JsObject *) npobj; + createJsName (&jo, &var_name, &len); + + var_val = nsVariant2Str (value); + script = (char *) malloc (len + strlen (var_val) + 3); + sprintf (script, "%s=%s;", var_name, var_val); + free (var_name); + free (var_val); + print ("SetProperty %s\n", script); + + res = evaluate (script); + if (res) + g_free (res); + free (script); + + + return true; +} + +static bool windowClassRemoveProperty (NPObject *npobj, NPIdentifier name) { + (void)npobj; (void)name; + print ("windowClassRemoveProperty\n"); + return false; +} + + +/*----------------%<---------------------------------------------------------*/ + +static void shutDownPlugin() { + if (scriptable_peer) { + nsReleaseObject (scriptable_peer); + scriptable_peer = NULL; + } + if (npShutdown) { + if (npp) { + np_funcs.destroy (npp, &saved_data); + free (npp); + npp = 0L; + } + npShutdown(); + npShutdown = 0; + } +} + +static void readStdin (gpointer p, gint src, GdkInputCondition cond) { + char *buf_ptr = stream_buf; + gsize bytes_read = read (src, + stream_buf + stream_buf_pos, + sizeof (stream_buf) - stream_buf_pos); + (void)cond; (void)p; + if (bytes_read > 0) + stream_buf_pos += bytes_read; + + /*print ("readStdin %d\n", bytes_read);*/ + while (buf_ptr < stream_buf + stream_buf_pos) { + uint32_t write_len; + int32_t bytes_written; + + if (callback_service && !stream_chunk_size) { + /* read header info */ + if (stream_buf + stream_buf_pos < buf_ptr + 2 * sizeof (uint32_t)) + break; /* need more data */ + current_stream_id = (gpointer)(long)*(uint32_t*)(buf_ptr); + stream_chunk_size = *((uint32_t *)(buf_ptr + sizeof (uint32_t))); + /*print ("header %d %d\n",(long)current_stream_id, stream_chunk_size);*/ + buf_ptr += 2 * sizeof (uint32_t); + if (stream_chunk_size && stream_buf + stream_buf_pos == buf_ptr) { + stream_buf_pos = 0; + break; /* only read the header for chunk with data */ + } + } + /* feed it to the stream */ + write_len = stream_buf + stream_buf_pos - buf_ptr; + if (callback_service && write_len > stream_chunk_size) + write_len = stream_chunk_size; + bytes_written = writeStream (current_stream_id, buf_ptr, write_len); + if (bytes_written < 0) { + print ("couldn't write to stream %d\n", (long)current_stream_id); + bytes_written = write_len; /* assume stream destroyed, skip */ + } + + /* update chunk status */ + if (bytes_written > 0) { + buf_ptr += bytes_written; + /*print ("update chunk %d %d\n", bytes_written, stream_chunk_size);*/ + stream_chunk_size -= bytes_written; + } else { + /* FIXME if plugin didn't accept the data retry later, suspend stdin reading */ + break; + } + + } + /* update buffer */ + /*print ("buffer written:%d bufpos:%d\n", buf_ptr-stream_buf, stream_buf_pos);*/ + if (stream_buf + stream_buf_pos == buf_ptr) { + stream_buf_pos = 0; + } else { + g_assert (buf_ptr < stream_buf + stream_buf_pos); + stream_buf_pos -= (stream_buf + stream_buf_pos - buf_ptr); + memmove (stream_buf, buf_ptr, stream_buf_pos); + } + if (bytes_read <= 0) { /* eof of stdin, only for 'cat foo | knpplayer' */ + StreamInfo*si=(StreamInfo*)g_tree_lookup(stream_list,current_stream_id); + si->reason = NPRES_DONE; + removeStream (current_stream_id); + if (stdin_read_watch) { + gdk_input_remove (stdin_read_watch); + stdin_read_watch = 0; + } + } +} + +static int initPlugin (const char *plugin_lib) { + NPNetscapeFuncs ns_funcs; + NPError np_err; + char *pname; + + print ("starting %s with %s\n", plugin_lib, object_url); + library = g_module_open (plugin_lib, G_MODULE_BIND_LAZY); + if (!library) { + print ("failed to load %s\n", plugin_lib); + return -1; + } + if (!g_module_symbol (library, + "NP_GetMIMEDescription", (gpointer *)&npGetMIMEDescription)) { + print ("undefined reference to load NP_GetMIMEDescription\n"); + return -1; + } + if (!g_module_symbol (library, + "NP_GetValue", (gpointer *)&npGetValue)) { + print ("undefined reference to load NP_GetValue\n"); + } + if (!g_module_symbol (library, + "NP_Initialize", (gpointer *)&npInitialize)) { + print ("undefined reference to load NP_Initialize\n"); + return -1; + } + if (!g_module_symbol (library, + "NP_Shutdown", (gpointer *)&npShutdown)) { + print ("undefined reference to load NP_Shutdown\n"); + return -1; + } + print ("startup succeeded %s\n", npGetMIMEDescription ()); + memset (&ns_funcs, 0, sizeof (NPNetscapeFuncs)); + ns_funcs.size = sizeof (NPNetscapeFuncs); + ns_funcs.version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; + ns_funcs.geturl = nsGetURL; + ns_funcs.posturl = nsPostURL; + ns_funcs.requestread = nsRequestRead; + ns_funcs.newstream = nsNewStream; + ns_funcs.write = nsWrite; + ns_funcs.destroystream = nsDestroyStream; + ns_funcs.status = nsStatus; + ns_funcs.uagent = nsUserAgent; + ns_funcs.memalloc = nsAlloc; + ns_funcs.memfree = nsMemFree; + ns_funcs.memflush = nsMemFlush; + ns_funcs.reloadplugins = nsReloadPlugins; + ns_funcs.getJavaEnv = nsGetJavaEnv; + ns_funcs.getJavaPeer = nsGetJavaPeer; + ns_funcs.geturlnotify = nsGetURLNotify; + ns_funcs.posturlnotify = nsPostURLNotify; + ns_funcs.getvalue = nsGetValue; + ns_funcs.setvalue = nsSetValue; + ns_funcs.invalidaterect = nsInvalidateRect; + ns_funcs.invalidateregion = nsInvalidateRegion; + ns_funcs.forceredraw = nsForceRedraw; + ns_funcs.getstringidentifier = nsGetStringIdentifier; + ns_funcs.getstringidentifiers = nsGetStringIdentifiers; + ns_funcs.getintidentifier = nsGetIntIdentifier; + ns_funcs.identifierisstring = nsIdentifierIsString; + ns_funcs.utf8fromidentifier = nsUTF8FromIdentifier; + ns_funcs.intfromidentifier = nsIntFromIdentifier; + ns_funcs.createobject = nsCreateObject; + ns_funcs.retainobject = nsRetainObject; + ns_funcs.releaseobject = nsReleaseObject; + ns_funcs.invoke = nsInvoke; + ns_funcs.invokeDefault = nsInvokeDefault; + ns_funcs.evaluate = nsEvaluate; + ns_funcs.getproperty = nsGetProperty; + ns_funcs.setproperty = nsSetProperty; + ns_funcs.removeproperty = nsRemoveProperty; + ns_funcs.hasproperty = nsHasProperty; + ns_funcs.hasmethod = nsHasMethod; + ns_funcs.releasevariantvalue = nsReleaseVariantValue; + ns_funcs.setexception = nsSetException; + ns_funcs.pushpopupsenabledstate = nsPushPopupsEnabledState; + ns_funcs.poppopupsenabledstate = nsPopPopupsEnabledState; + + js_class.structVersion = NP_CLASS_STRUCT_VERSION; + js_class.allocate = windowClassAllocate; + js_class.deallocate = windowClassDeallocate; + js_class.invalidate = windowClassInvalidate; + js_class.hasMethod = windowClassHasMethod; + js_class.invoke = windowClassInvoke; + js_class.invokeDefault = windowClassInvokeDefault; + js_class.hasProperty = windowClassHasProperty; + js_class.getProperty = windowClassGetProperty; + js_class.setProperty = windowClassSetProperty; + js_class.removeProperty = windowClassRemoveProperty; + + np_funcs.size = sizeof (NPPluginFuncs); + + np_err = npInitialize (&ns_funcs, &np_funcs); + if (np_err != NPERR_NO_ERROR) { + print ("NP_Initialize failure %d\n", np_err); + npShutdown = 0; + return -1; + } + np_err = npGetValue (NULL, NPPVpluginNameString, &pname); + if (np_err == NPERR_NO_ERROR) + print ("NP_GetValue Name %s\n", pname); + np_err = npGetValue (NULL, NPPVpluginDescriptionString, &pname); + if (np_err == NPERR_NO_ERROR) + print ("NP_GetValue Description %s\n", pname); + return 0; +} + +static int newPlugin (NPMIMEType mime, int16 argc, char *argn[], char *argv[]) { + NPSetWindowCallbackStruct ws_info; + NPError np_err; + Display *display; + int screen; + int i; + int needs_xembed; + uint32_t width = 0, height = 0; + + for (i = 0; i < argc; i++) { + if (!strcasecmp (argn[i], "width")) + width = strtol (argv[i], 0L, 10); + else if (!strcasecmp (argn[i], "height")) + height = strtol (argv[i], 0L, 10); + } + if (width > 0 && height > 0) + callFunction (-1, "dimension", + DBUS_TYPE_UINT32, &width, DBUS_TYPE_UINT32, &height, + DBUS_TYPE_INVALID); + + npp = (NPP_t*)malloc (sizeof (NPP_t)); + memset (npp, 0, sizeof (NPP_t)); + np_err = np_funcs.newp (mime, npp, NP_EMBED, argc, argn, argv, saved_data); + if (np_err != NPERR_NO_ERROR) { + print ("NPP_New failure %d %p %p\n", np_err, np_funcs, np_funcs.newp); + return -1; + } + if (np_funcs.getvalue) { + char *pname; + void *iid; + np_err = np_funcs.getvalue ((void*)npp, + NPPVpluginNameString, (void*)&pname); + if (np_err == NPERR_NO_ERROR) + print ("plugin name %s\n", pname); + np_err = np_funcs.getvalue ((void*)npp, + NPPVpluginNeedsXEmbed, (void*)&needs_xembed); + if (np_err != NPERR_NO_ERROR || !needs_xembed) { + print ("NPP_GetValue NPPVpluginNeedsXEmbed failure %d\n", np_err); + shutDownPlugin(); + return -1; + } + np_err = np_funcs.getvalue ((void*)npp, + NPPVpluginScriptableIID, (void*)&iid); + np_err = np_funcs.getvalue ((void*)npp, + NPPVpluginScriptableNPObject, (void*)&scriptable_peer); + if (np_err != NPERR_NO_ERROR || !scriptable_peer) + print ("NPP_GetValue no NPPVpluginScriptableNPObject %d\n", np_err); + } + memset (&np_window, 0, sizeof (NPWindow)); + display = gdk_x11_get_default_xdisplay (); + np_window.x = 0; + np_window.y = 0; + np_window.width = 1920; + np_window.height = 1200; + np_window.window = (void*)socket_id; + np_window.type = NPWindowTypeWindow; + ws_info.type = NP_SETWINDOW; + screen = DefaultScreen (display); + ws_info.display = (void*)(long)display; + ws_info.visual = (void*)(long)DefaultVisual (display, screen); + ws_info.colormap = DefaultColormap (display, screen); + ws_info.depth = DefaultDepth (display, screen); + print ("display %u %dx%d\n", socket_id, width, height); + np_window.ws_info = (void*)&ws_info; + + GtkAllocation allocation; + allocation.x = 0; + allocation.y = 0; + allocation.width = np_window.width; + allocation.height = np_window.height; + gtk_widget_size_allocate (xembed, &allocation); + + np_err = np_funcs.setwindow (npp, &np_window); + + return 0; +} + +static gpointer startPlugin (const char *url, const char *mime, + int argc, char *argn[], char *argv[]) { + StreamInfo *si; + if (!npp && (initPlugin (plugin) || newPlugin (mimetype, argc, argn, argv))) + return 0L; + si = addStream (url, mime, 0L, 0L, false); + return si; +} + +/*----------------%<---------------------------------------------------------*/ + +static StreamInfo *getStreamInfo (const char *path, gpointer *stream_id) { + const char *p = strrchr (path, '_'); + *stream_id = p ? (gpointer) strtol (p+1, NULL, 10) : NULL; + return (StreamInfo *) g_tree_lookup (stream_list, *stream_id); +} + +static DBusHandlerResult dbusFilter (DBusConnection * connection, + DBusMessage *msg, void * user_data) { + DBusMessageIter args; + const char *sender = dbus_message_get_sender (msg); + const char *iface = "org.kde.kmplayer.backend"; + (void)user_data; (void)connection; + if (!dbus_message_has_destination (msg, service_name)) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + print ("dbusFilter %s %s\n", sender,dbus_message_get_interface (msg)); + if (dbus_message_is_method_call (msg, iface, "play")) { + DBusMessageIter ait; + char *param = 0; + unsigned int params; + char **argn = NULL; + char **argv = NULL; + int i; + if (!dbus_message_iter_init (msg, &args) || + DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&args)) { + g_printerr ("missing url arg"); + return DBUS_HANDLER_RESULT_HANDLED; + } + dbus_message_iter_get_basic (&args, ¶m); + object_url = g_strdup (param); + if (!dbus_message_iter_next (&args) || + DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&args)) { + g_printerr ("missing mimetype arg"); + return DBUS_HANDLER_RESULT_HANDLED; + } + dbus_message_iter_get_basic (&args, ¶m); + mimetype = g_strdup (param); + if (!dbus_message_iter_next (&args) || + DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&args)) { + g_printerr ("missing plugin arg"); + return DBUS_HANDLER_RESULT_HANDLED; + } + dbus_message_iter_get_basic (&args, ¶m); + plugin = g_strdup (param); + if (!dbus_message_iter_next (&args) || + DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type (&args)) { + g_printerr ("missing param count arg"); + return DBUS_HANDLER_RESULT_HANDLED; + } + dbus_message_iter_get_basic (&args, ¶ms); + if (params > 0 && params < 100) { + argn = (char**) malloc (params * sizeof (char *)); + argv = (char**) malloc (params * sizeof (char *)); + } + if (!dbus_message_iter_next (&args) || + DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type (&args)) { + g_printerr ("missing params array"); + return DBUS_HANDLER_RESULT_HANDLED; + } + dbus_message_iter_recurse (&args, &ait); + for (i = 0; i < params; i++) { + char *key, *value; + DBusMessageIter di; + if (dbus_message_iter_get_arg_type (&ait) != DBUS_TYPE_DICT_ENTRY) + break; + dbus_message_iter_recurse (&ait, &di); + if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&di)) + break; + dbus_message_iter_get_basic (&di, &key); + if (!dbus_message_iter_next (&di) || + DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&di)) + break; + dbus_message_iter_get_basic (&di, &value); + argn[i] = g_strdup (key); + argv[i] = g_strdup (value); + print ("param %d:%s='%s'\n", i + 1, argn[i], value); + if (!dbus_message_iter_next (&ait)) + params = i + 1; + } + print ("play %s %s %s params:%d\n", object_url, + mimetype ? mimetype : "", plugin, i); + startPlugin (object_url, mimetype, i, argn, argv); + } else if (dbus_message_is_method_call (msg, iface, "redirected")) { + char *url = 0; + gpointer stream_id; + StreamInfo *si = getStreamInfo(dbus_message_get_path (msg), &stream_id); + if (si && dbus_message_iter_init (msg, &args) && + DBUS_TYPE_STRING == dbus_message_iter_get_arg_type (&args)) { + dbus_message_iter_get_basic (&args, &url); + free (si->url); + si->url = g_strdup (url); + si->np_stream.url = si->url; + print ("redirect %d (had data %d) to %s\n", (long)stream_id, si->called_plugin, url); + } + } else if (dbus_message_is_method_call (msg, iface, "eof")) { + gpointer stream_id; + StreamInfo *si = getStreamInfo(dbus_message_get_path (msg), &stream_id); + if (si && dbus_message_iter_init (msg, &args) && + DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type (&args)) { + dbus_message_iter_get_basic (&args, &si->total); + if (dbus_message_iter_next (&args) && + DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type (&args)) { + dbus_message_iter_get_basic (&args, &si->reason); + print ("eof %d bytes:%d reason:%d\n", (long)stream_id, si->total, si->reason); + if (si->stream_pos == si->total || si->destroyed) + removeStream (stream_id); + } + } + } else if (dbus_message_is_method_call (msg, iface, "quit")) { + print ("quit\n"); + shutDownPlugin(); + gtk_main_quit(); + } else if (dbus_message_is_method_call (msg, iface, "streamInfo")) { + gpointer stream_id; + StreamInfo *si = getStreamInfo(dbus_message_get_path (msg), &stream_id); + const char *mime; + uint32_t length; + if (si && dbus_message_iter_init (msg, &args) && + DBUS_TYPE_STRING == dbus_message_iter_get_arg_type (&args)) { + dbus_message_iter_get_basic (&args, &mime); + if (*mime) { + if (si->mimetype) + g_free (si->mimetype); + si->mimetype = g_strdup (mime); + } + if (dbus_message_iter_next (&args) && + DBUS_TYPE_UINT32 == dbus_message_iter_get_arg_type (&args)) { + dbus_message_iter_get_basic (&args, &length); + si->np_stream.end = length; + } + print ("streamInfo %d size:%d mime:%s\n", (long)stream_id, length, + mime ? mime : ""); + } + } else { + print ("unknown message\n"); + } + return DBUS_HANDLER_RESULT_HANDLED; +} + +static void callFunction(int stream,const char *func, int first_arg_type, ...) { + char path[64]; + strncpy (path, callback_path, sizeof (path) -1); + if (stream > -1) { + int len = strlen (path); + snprintf (path + len, sizeof (path) - len, "/stream_%d", stream); + } + print ("call %s.%s()\n", path, func); + if (callback_service) { + va_list var_args; + DBusMessage *msg = dbus_message_new_method_call ( + callback_service, + path, + "org.kde.kmplayer.callback", + func); + if (first_arg_type != DBUS_TYPE_INVALID) { + va_start (var_args, first_arg_type); + dbus_message_append_args_valist (msg, first_arg_type, var_args); + va_end (var_args); + } + dbus_message_set_no_reply (msg, TRUE); + dbus_connection_send (dbus_connection, msg, NULL); + dbus_message_unref (msg); + dbus_connection_flush (dbus_connection); + } +} + +static char * evaluate (const char *script) { + char * ret = NULL; + print ("evaluate %s", script); + if (callback_service) { + DBusMessage *rmsg; + DBusMessage *msg = dbus_message_new_method_call ( + callback_service, + callback_path, + "org.kde.kmplayer.callback", + "evaluate"); + dbus_message_append_args ( + msg, DBUS_TYPE_STRING, &script, DBUS_TYPE_INVALID); + rmsg = dbus_connection_send_with_reply_and_block (dbus_connection, + msg, 2000, NULL); + if (rmsg) { + DBusMessageIter it; + if (dbus_message_iter_init (rmsg, &it) && + DBUS_TYPE_STRING == dbus_message_iter_get_arg_type (&it)) { + char * param; + dbus_message_iter_get_basic (&it, ¶m); + ret = g_strdup (param); + } + dbus_message_unref (rmsg); + print (" => %s\n", ret); + } + dbus_message_unref (msg); + } else { + print (" => NA\n"); + } + return ret; +} + +/*----------------%<---------------------------------------------------------*/ + +static void pluginAdded (GtkSocket *socket, gpointer d) { + /*(void)socket;*/ (void)d; + print ("pluginAdded\n"); + if (socket->plug_window) { + gpointer user_data = NULL; + gdk_window_get_user_data (socket->plug_window, &user_data); + if (!user_data) { + /** + * GtkSocket resets plugins XSelectInput in + * _gtk_socket_add_window + * _gtk_socket_windowing_select_plug_window_input + **/ + XSelectInput (gdk_x11_get_default_xdisplay (), + gdk_x11_drawable_get_xid (socket->plug_window), + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + KeymapStateMask | + ButtonMotionMask | + PointerMotionMask | + EnterWindowMask | LeaveWindowMask | + FocusChangeMask | + ExposureMask | + StructureNotifyMask | + SubstructureRedirectMask | + PropertyChangeMask + ); + } + } + callFunction (-1, "plugged", DBUS_TYPE_INVALID); +} + +static void windowCreatedEvent (GtkWidget *w, gpointer d) { + (void)d; + print ("windowCreatedEvent\n"); + socket_id = gtk_socket_get_id (GTK_SOCKET (xembed)); + if (parent_id) { + print ("windowCreatedEvent %p\n", GTK_PLUG (w)->socket_window); + if (!GTK_PLUG (w)->socket_window) + gtk_plug_construct (GTK_PLUG (w), parent_id); + gdk_window_reparent( w->window, + GTK_PLUG (w)->socket_window + ? GTK_PLUG (w)->socket_window + : gdk_window_foreign_new (parent_id), + 0, 0); + gtk_widget_show_all (w); + /*XReparentWindow (gdk_x11_drawable_get_xdisplay (w->window), + gdk_x11_drawable_get_xid (w->window), + parent_id, + 0, 0);*/ + } + if (!callback_service) { + char *argn[] = { "WIDTH", "HEIGHT", "debug", "SRC" }; + char *argv[] = { "440", "330", g_strdup("yes"), g_strdup(object_url) }; + startPlugin (object_url, mimetype, 4, argn, argv); + } +} + +static void embeddedEvent (GtkPlug *plug, gpointer d) { + (void)plug; (void)d; + print ("embeddedEvent\n"); +} + +static gboolean updateDimension (void * p) { + (void)p; + if (np_window.window) { + if (np_window.width != top_w || np_window.height != top_h) { + np_window.width = top_w; + np_window.height = top_h; + np_funcs.setwindow (npp, &np_window); + } + update_dimension_timer = 0; + return 0; /* single shot */ + } else { + return 1; + } +} + +static gboolean configureEvent(GtkWidget *w, GdkEventConfigure *e, gpointer d) { + (void)w; (void)d; + if (e->width != top_w || e->height != top_h) { + top_w = e->width; + top_h = e->height; + if (!update_dimension_timer) + update_dimension_timer = g_timeout_add (100, updateDimension, NULL); + } + return FALSE; +} + +static gboolean windowCloseEvent (GtkWidget *w, GdkEvent *e, gpointer d) { + (void)w; (void)e; (void)d; + shutDownPlugin(); + return FALSE; +} + +static void windowDestroyEvent (GtkWidget *w, gpointer d) { + (void)w; (void)d; + gtk_main_quit(); +} + +static gboolean initPlayer (void * p) { + GtkWidget *window; + GdkColormap *color_map; + GdkColor bg_color; + (void)p; + + window = callback_service + ? gtk_plug_new (parent_id) + : gtk_window_new (GTK_WINDOW_TOPLEVEL); + g_signal_connect (G_OBJECT (window), "delete_event", + G_CALLBACK (windowCloseEvent), NULL); + g_signal_connect (G_OBJECT (window), "destroy", + G_CALLBACK (windowDestroyEvent), NULL); + g_signal_connect_after (G_OBJECT (window), "realize", + GTK_SIGNAL_FUNC (windowCreatedEvent), NULL); + g_signal_connect (G_OBJECT (window), "configure-event", + GTK_SIGNAL_FUNC (configureEvent), NULL); + + xembed = gtk_socket_new(); + g_signal_connect (G_OBJECT (xembed), "plug-added", + GTK_SIGNAL_FUNC (pluginAdded), NULL); + + color_map = gdk_colormap_get_system(); + gdk_colormap_query_color (color_map, 0, &bg_color); + gtk_widget_modify_bg (xembed, GTK_STATE_NORMAL, &bg_color); + + gtk_container_add (GTK_CONTAINER (window), xembed); + + if (!parent_id) { + gtk_widget_set_size_request (window, 440, 330); + gtk_widget_show_all (window); + } else { + g_signal_connect (G_OBJECT (window), "embedded", + GTK_SIGNAL_FUNC (embeddedEvent), NULL); + gtk_widget_set_size_request (window, 1920, 1200); + gtk_widget_realize (window); + } + + if (callback_service && callback_path) { + DBusError dberr; + const char *serv = "type='method_call',interface='org.kde.kmplayer.backend'"; + char myname[64]; + + dbus_error_init (&dberr); + dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &dberr); + if (!dbus_connection) { + g_printerr ("Failed to open connection to bus: %s\n", + dberr.message); + exit (1); + } + g_sprintf (myname, "org.kde.kmplayer.npplayer-%d", getpid ()); + service_name = g_strdup (myname); + print ("using service %s was '%s'\n", service_name, dbus_bus_get_unique_name (dbus_connection)); + dbus_connection_setup_with_g_main (dbus_connection, 0L); + dbus_bus_request_name (dbus_connection, service_name, + DBUS_NAME_FLAG_REPLACE_EXISTING, &dberr); + if (dbus_error_is_set (&dberr)) { + g_printerr ("Failed to register name: %s\n", dberr.message); + dbus_connection_unref (dbus_connection); + return -1; + } + dbus_bus_add_match (dbus_connection, serv, &dberr); + if (dbus_error_is_set (&dberr)) { + g_printerr ("dbus_bus_add_match error: %s\n", dberr.message); + dbus_connection_unref (dbus_connection); + return -1; + } + dbus_connection_add_filter (dbus_connection, dbusFilter, 0L, 0L); + + /* TODO: remove DBUS_BUS_SESSION and create a private connection */ + callFunction (-1, "running", + DBUS_TYPE_STRING, &service_name, DBUS_TYPE_INVALID); + + dbus_connection_flush (dbus_connection); + } + return 0; /* single shot */ +} + +int main (int argc, char **argv) { + int i; + + XInitThreads (); + g_thread_init (NULL); + gtk_init (&argc, &argv); + + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-p") && ++i < argc) { + plugin = g_strdup (argv[i]); + } else if (!strcmp (argv[i], "-cb") && ++i < argc) { + gchar *cb = g_strdup (argv[i]); + gchar *path = strchr(cb, '/'); + if (path) { + callback_path = g_strdup (path); + *path = 0; + } + callback_service = g_strdup (cb); + g_free (cb); + } else if (!strcmp (argv[i], "-m") && ++i < argc) { + mimetype = g_strdup (argv[i]); + } else if (!strcmp (argv [i], "-wid") && ++i < argc) { + parent_id = strtol (argv[i], 0L, 10); + } else + object_url = g_strdup (argv[i]); + } + if (!callback_service && !(object_url && mimetype && plugin)) { + g_fprintf(stderr, "Usage: %s <-m mimetype -p plugin url|-cb service -wid id>\n", argv[0]); + return 1; + } + + identifiers = g_tree_new (strcmp); + stream_list = g_tree_new (streamCompare); + + g_timeout_add (0, initPlayer, NULL); + + fcntl (0, F_SETFL, fcntl (0, F_GETFL) | O_NONBLOCK); + + print ("entering gtk_main\n"); + + gtk_main(); + + if (dbus_connection) + dbus_connection_unref (dbus_connection); + + return 0; +} |