File indexing completed on 2024-04-14 15:01:14

0001 /*
0002 * Copyright (C) 2007  Koos Vriezen <koos.vriezen@gmail.com>
0003 *
0004 * This library is free software; you can redistribute it and/or
0005 * modify it under the terms of the GNU Lesser General Public
0006 * License as published by the Free Software Foundation; either
0007 * version 2 of the License, or (at your option) any later version.
0008 *
0009 * This library is distributed in the hope that it will be useful,
0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012 * Lesser General Public License for more details.
0013 *
0014 * You should have received a copy of the GNU Lesser General Public
0015 * License along with this library; if not, write to the Free Software
0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0017 */
0018 
0019 /* gcc -o knpplayer `pkg-config --libs --cflags gtk+-x11-2.0` `pkg-config --libs --cflags dbus-glib-1` `pkg-config --libs --cflags gthread-2.0` npplayer.c
0020 
0021 http://devedge-temp.mozilla.org/library/manuals/2002/plugin/1.0/
0022 http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html
0023 */
0024 extern "C" {
0025 #include <unistd.h>
0026 #include <string.h>
0027 #include <stdio.h>
0028 #include <stdarg.h>
0029 #include <stdlib.h>
0030 #include <sys/types.h>
0031 #include <sys/time.h>
0032 #include <fcntl.h>
0033 
0034 #include <glib/gprintf.h>
0035 #include <glib.h>
0036 #include <gdk/gdkx.h>
0037 #include <gtk/gtk.h>
0038 
0039 #include <dbus/dbus-glib-lowlevel.h>
0040 #define DBUS_API_SUBJECT_TO_CHANGE
0041 #include <dbus/dbus.h>
0042 #include <dbus/dbus-glib.h>
0043 
0044 #define XP_UNIX
0045 #define MOZ_X11
0046 #include "moz-sdk/npapi.h"
0047 #include "moz-sdk/npruntime.h"
0048 #include "moz-sdk/npfunctions.h"
0049 #include "moz-sdk/prtypes.h"
0050 
0051 #define INITIAL_WINDOW_WIDTH 1920
0052 
0053 typedef const char* (* NP_LOADDS NP_GetMIMEDescriptionUPP)();
0054 typedef NPError (* NP_GetValueUPP)(void *inst, NPPVariable var, void *value);
0055 typedef NPError (* NP_InitializeUPP)(NPNetscapeFuncs*, NPPluginFuncs*);
0056 typedef NPError (* NP_ShutdownUPP)(void);
0057 
0058 static gchar *plugin;
0059 static gchar *object_url;
0060 static gchar *mimetype;
0061 
0062 static DBusConnection *dbus_connection;
0063 static DBusObjectPathVTable stream_vtable;
0064 static char *service_name;
0065 static gchar *callback_service;
0066 static gchar *callback_path;
0067 static GModule *library;
0068 static GtkWidget *xembed;
0069 static Window socket_id;
0070 static Window parent_id;
0071 static int top_w, top_h;
0072 static int update_dimension_timer;
0073 static int stdin_read_watch;
0074 
0075 static NPNetscapeFuncs ns_funcs;
0076 static NPPluginFuncs np_funcs;       /* plugin functions              */
0077 static NPP npp;                      /* single instance of the plugin */
0078 static NPWindow np_window;
0079 static NPSetWindowCallbackStruct ws_info;
0080 static NPObject *js_window;
0081 static NPSavedData *saved_data;
0082 static NPClass js_class;
0083 static GTree *stream_list;
0084 static gpointer current_stream_id;
0085 static uint32_t stream_chunk_size;
0086 static char stream_buf[64 * 1024];
0087 static unsigned int stream_buf_pos;
0088 static int stream_id_counter;
0089 static GTree *identifiers;
0090 typedef struct _StreamInfo {
0091     NPStream np_stream;
0092     /*unsigned int stream_buf_pos;*/
0093     unsigned int stream_pos;
0094     unsigned int total;
0095     unsigned int reason;
0096     unsigned int post_len;
0097     char *url;
0098     char *mimetype;
0099     char *target;
0100     char *post;
0101     char *headers;
0102     bool notify;
0103     bool called_plugin;
0104     bool destroyed;
0105 } StreamInfo;
0106 struct JsObject {
0107     NPObject npobject;
0108     struct JsObject * parent;
0109     char * name;
0110 };
0111 
0112 static NP_GetMIMEDescriptionUPP npGetMIMEDescription;
0113 static NP_GetValueUPP npGetValue;
0114 static NP_InitializeUPP npInitialize;
0115 static NP_ShutdownUPP npShutdown;
0116 
0117 static const char *iface_stream = "org.kde.kmplayer.stream";
0118 static const char *iface_callback = "org.kde.kmplayer.callback";
0119 static void callFunction(int stream, const char *iface, const char *func, int first_arg_type, ...);
0120 static void readStdin (gpointer d, gint src, GdkInputCondition cond);
0121 static char *evaluate (const char *script, bool store);
0122 
0123 static
0124 DBusHandlerResult dbusStreamMessage(DBusConnection *c, DBusMessage *m, void *u);
0125 static void dbusStreamUnregister (DBusConnection *conn, void *user_data);
0126 
0127 /*----------------%<---------------------------------------------------------*/
0128 
0129 static void print (const char * format, ...) {
0130     va_list vl;
0131     va_start (vl, format);
0132     vfprintf (stderr, format, vl);
0133     va_end (vl);
0134     fflush (stderr);
0135 }
0136 
0137 static void *nsAlloc (uint32 size) {
0138     return g_malloc (size);
0139 }
0140 
0141 static void nsMemFree (void* ptr) {
0142     g_free (ptr);
0143 }
0144 
0145 static void createPath (int stream, char *buf, int buf_len) {
0146     strncpy (buf, callback_path, buf_len -1);
0147     buf [buf_len -1] = 0;
0148     if (stream > -1) {
0149         int len = strlen (buf);
0150         snprintf (buf + len, buf_len - len, "/stream_%d", stream);
0151     }
0152 }
0153 
0154 /*----------------%<---------------------------------------------------------*/
0155 
0156 static gint streamCompare (gconstpointer a, gconstpointer b) {
0157     return (long)a - (long)b;
0158 }
0159 
0160 static void freeStream (StreamInfo *si) {
0161     char stream_name[64];
0162     sprintf (stream_name, "/stream_%d", (int)(long) si->np_stream.ndata);
0163     if (!g_tree_remove (stream_list, si->np_stream.ndata))
0164         print ("WARNING freeStream not in tree\n");
0165     else
0166         dbus_connection_unregister_object_path (dbus_connection, stream_name);
0167     g_free (si->url);
0168     if (si->mimetype)
0169         g_free (si->mimetype);
0170     if (si->target)
0171         g_free (si->target);
0172     if (si->post)
0173         nsMemFree (si->post);
0174     if (si->headers)
0175         nsMemFree (si->headers);
0176     nsMemFree (si);
0177 }
0178 
0179 static gboolean requestStream (void * p) {
0180     StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p);
0181     if (si) {
0182         char *path = (char *)nsAlloc (64);
0183         char *target = si->target ? si->target : g_strdup ("");
0184         if (!callback_service)
0185             current_stream_id = p;
0186         if (!stdin_read_watch)
0187             stdin_read_watch = gdk_input_add (0, GDK_INPUT_READ, readStdin, NULL);
0188         createPath ((int)(long)p, path, 64);
0189 
0190         char cb_path[64];
0191         createPath (-1, cb_path, sizeof (cb_path));
0192         print ("call %s.%s()\n", cb_path, "request_stream");
0193         if (callback_service) {
0194             DBusMessageIter it, ait;
0195             DBusMessage *msg = dbus_message_new_method_call (
0196                     callback_service,
0197                     cb_path,
0198                     "org.kde.kmplayer.callback",
0199                     "request_stream");
0200             dbus_message_iter_init_append (msg, &it);
0201             dbus_message_iter_append_basic (&it, DBUS_TYPE_STRING, &path);
0202             dbus_message_iter_append_basic (&it, DBUS_TYPE_STRING, &si->url);
0203             dbus_message_iter_append_basic (&it, DBUS_TYPE_STRING, &target);
0204             dbus_message_iter_open_container (&it, DBUS_TYPE_ARRAY, "y", &ait);
0205             if (si->post_len)
0206                 dbus_message_iter_append_fixed_array (&ait,
0207                         DBUS_TYPE_BYTE, &si->post, si->post_len);
0208             dbus_message_iter_close_container(&it, &ait);
0209             dbus_message_set_no_reply (msg, TRUE);
0210             dbus_connection_send (dbus_connection, msg, NULL);
0211             dbus_message_unref (msg);
0212             dbus_connection_flush (dbus_connection);
0213         }
0214 
0215         nsMemFree (path);
0216         if (!si->target)
0217             g_free (target);
0218     } else {
0219         print ("requestStream %d not found", (long) p);
0220     }
0221     return 0; /* single shot */
0222 }
0223 
0224 static gboolean destroyStream (void * p) {
0225     StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p);
0226     if (si) {
0227         si->destroyed = true;
0228         callFunction ((int)(long)p, iface_stream, "destroy", DBUS_TYPE_INVALID);
0229     }
0230     return 0; /* single shot */
0231 }
0232 
0233 static void removeStream (void * p) {
0234     StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p);
0235 
0236     if (si) {
0237         print ("removeStream %d rec:%d reason %d %dx%d\n", (long) p, si->stream_pos, si->reason, top_w, top_h);
0238         if (!si->destroyed) {
0239             if (si->called_plugin && !si->target) {
0240                 si->np_stream.end = si->total;
0241                 np_funcs.destroystream (npp, &si->np_stream, si->reason);
0242             }
0243             if (si->notify)
0244                 np_funcs.urlnotify (npp,
0245                         si->url, si->reason, si->np_stream.notifyData);
0246         }
0247         freeStream (si);
0248     }
0249 }
0250 
0251 static int32_t writeStream (gpointer p, char *buf, uint32_t count) {
0252     int32_t sz = -1;
0253     StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, p);
0254     /*print ("writeStream found %d count %d\n", !!si, count);*/
0255     if (si) {
0256         if (si->reason > NPERR_NO_ERROR || si->destroyed) {
0257             sz = count; /* stream closed, skip remainings */
0258         } else {
0259             if (!si->called_plugin) {
0260                 uint16 stype = NP_NORMAL;
0261                 NPError err = np_funcs.newstream (npp, si->mimetype
0262                         ?  si->mimetype
0263                         : (char *)"text/plain",
0264                         &si->np_stream, 0, &stype);
0265                 if (err != NPERR_NO_ERROR) {
0266                     g_printerr ("newstream error %d\n", err);
0267                     destroyStream (p);
0268                     return count; /* stream not accepted, skip remainings */
0269                 }
0270                 print ("newStream %d type:%d\n", (long) p, stype);
0271                 si->called_plugin = true;
0272             }
0273             if (count) /* urls with a target returns zero bytes */
0274                 sz = np_funcs.writeready (npp, &si->np_stream);
0275             if (sz > 0) {
0276                 sz = np_funcs.write (npp, &si->np_stream, si->stream_pos,
0277                         (int32_t) count > sz ? sz : (int32_t) count, buf);
0278                 if (sz < 0) { /*FIXME plugin destroys stream here*/
0279                     si->reason = NPERR_INVALID_PLUGIN_ERROR;
0280                     destroyStream ((gpointer)p);
0281                     return count; /* stream not accepted, skip remainings */
0282                 }
0283             } else {
0284                 sz = 0;
0285             }
0286             si->stream_pos += sz;
0287             if (si->stream_pos == si->total) {
0288                 if (si->stream_pos || !count) {
0289                     si->reason = NPRES_DONE;
0290                     removeStream (p);
0291                 } else {
0292                     g_timeout_add (0, destroyStream, p);
0293                 }
0294             }
0295         }
0296     }
0297     return sz;
0298 }
0299 
0300 static StreamInfo *addStream (const char *url, const char *mime, const char *target, int len, const char *post, void *notify_data, bool notify) {
0301     StreamInfo *si = (StreamInfo *) nsAlloc (sizeof (StreamInfo));
0302     char stream_name[64];
0303 
0304     memset (si, 0, sizeof (StreamInfo));
0305     si->url = g_strdup (url);
0306     si->np_stream.url = si->url;
0307     if (mime)
0308         si->mimetype = g_strdup (mime);
0309     if (target)
0310         si->target = g_strdup (target);
0311     if (len && post) {
0312         si->post_len = len;
0313         si->post = (char *) nsAlloc (len);
0314         memcpy (si->post, post, len);
0315     }
0316     si->np_stream.notifyData = notify_data;
0317     si->notify = notify;
0318     si->np_stream.ndata = (void *) (long) (stream_id_counter++);
0319     print ("add stream %d\n", (long) si->np_stream.ndata);
0320     sprintf (stream_name, "/stream_%d", (int)(long) si->np_stream.ndata);
0321     if (!dbus_connection_register_object_path (dbus_connection, stream_name,
0322                 &stream_vtable, si))
0323         g_printerr ("dbus_connection_register_object_path error\n");
0324     g_tree_insert (stream_list, si->np_stream.ndata, si);
0325 
0326     g_timeout_add (0, requestStream, si->np_stream.ndata);
0327 
0328     return si;
0329 }
0330 
0331 /*----------------%<---------------------------------------------------------*/
0332 
0333 static void createJsName (JsObject * obj, char **name, uint32_t * len) {
0334     int slen = strlen (obj->name);
0335     if (obj->parent) {
0336         *len += slen + 1;
0337         createJsName (obj->parent, name, len);
0338     } else {
0339         *name = (char *) nsAlloc (*len + slen + 1);
0340         *(*name + *len + slen) = 0;
0341         *len = 0;
0342     }
0343     if (obj->parent) {
0344         *(*name + *len) = '.';
0345         *len += 1;
0346     }
0347     memcpy (*name + *len, obj->name, slen);
0348     *len += slen;
0349 }
0350 
0351 static char *nsVariant2Str (const NPVariant *value) {
0352     char *str;
0353     switch (value->type) {
0354         case NPVariantType_String:
0355             str = (char *) nsAlloc (value->value.stringValue.utf8length + 3);
0356             str[0] = str[value->value.stringValue.utf8length + 1] = '\'';
0357             strncpy (str + 1, value->value.stringValue.utf8characters,
0358                     value->value.stringValue.utf8length);
0359             str[value->value.stringValue.utf8length + 2] = 0;
0360             break;
0361         case NPVariantType_Int32:
0362             str = (char *) nsAlloc (16);
0363             snprintf (str, 15, "%d", value->value.intValue);
0364             break;
0365         case NPVariantType_Double:
0366             str = (char *) nsAlloc (64);
0367             snprintf (str, 63, "%f", value->value.doubleValue);
0368             break;
0369         case NPVariantType_Bool:
0370             str = strdup (value->value.boolValue ? "true" : "false");
0371             break;
0372         case NPVariantType_Null:
0373             str = strdup ("null");
0374             break;
0375         case NPVariantType_Object:
0376             if (&js_class == value->value.objectValue->_class) {
0377                 JsObject *jv = (JsObject *) value->value.objectValue;
0378                 char *val;
0379                 uint32_t vlen = 0;
0380                 createJsName (jv, &val, &vlen);
0381                 str = strdup (val);
0382                 nsMemFree (val);
0383             } else {
0384                 str = strdup ("null"); /* TODO track plugin objects */
0385             }
0386             break;
0387         default:
0388             str = strdup ("");
0389             break;
0390     }
0391     return str;
0392 }
0393 
0394 /*----------------%<---------------------------------------------------------*/
0395 
0396 static NPObject * nsCreateObject (NPP instance, NPClass *aClass) {
0397     NPObject *obj;
0398     if (aClass && aClass->allocate) {
0399         obj = aClass->allocate (instance, aClass);
0400     } else {
0401         obj = (NPObject *) nsAlloc (sizeof (NPObject));
0402         memset (obj, 0, sizeof (NPObject));
0403         obj->_class = aClass;
0404         /*obj = js_class.allocate (instance, &js_class);/ *add null class*/
0405         print ("NPN_CreateObject\n");
0406     }
0407     obj->referenceCount = 1;
0408     return obj;
0409 }
0410 
0411 static NPObject *nsRetainObject (NPObject *npobj) {
0412     /*print( "nsRetainObject %p\n", npobj);*/
0413     npobj->referenceCount++;
0414     return npobj;
0415 }
0416 
0417 static void nsReleaseObject (NPObject *obj) {
0418     /*print ("NPN_ReleaseObject\n");*/
0419     if (! (--obj->referenceCount))
0420         obj->_class->deallocate (obj);
0421 }
0422 
0423 static NPError nsGetURL (NPP instance, const char* url, const char* target) {
0424     (void)instance;
0425     print ("nsGetURL %s %s\n", url, target ? target : "");
0426     addStream (url, 0L, target, 0, NULL, 0L, false);
0427     return NPERR_NO_ERROR;
0428 }
0429 
0430 static NPError nsPostURL (NPP instance, const char *url,
0431         const char *target, uint32 len, const char *buf, NPBool file) {
0432     (void)instance; (void)file;
0433     print ("nsPostURL %s %s\n", url, target ? target : "");
0434     addStream (url, 0L, target, len, buf, 0L, false);
0435     return NPERR_NO_ERROR;
0436 }
0437 
0438 static NPError nsRequestRead (NPStream *stream, NPByteRange *rangeList) {
0439     (void)stream; (void)rangeList;
0440     print ("nsRequestRead\n");
0441     return NPERR_NO_ERROR;
0442 }
0443 
0444 static NPError nsNewStream (NPP instance, NPMIMEType type,
0445         const char *target, NPStream **stream) {
0446     (void)instance; (void)type; (void)stream; (void)target;
0447     print ("nsNewStream\n");
0448     return NPERR_NO_ERROR;
0449 }
0450 
0451 static int32 nsWrite (NPP instance, NPStream* stream, int32 len, void *buf) {
0452     (void)instance; (void)len; (void)buf; (void)stream;
0453     print ("nsWrite\n");
0454     return 0;
0455 }
0456 
0457 static NPError nsDestroyStream (NPP instance, NPStream *stream, NPError reason) {
0458     StreamInfo *si = (StreamInfo *) g_tree_lookup (stream_list, stream->ndata);
0459     (void)instance;
0460     print ("nsDestroyStream\n");
0461     if (si) {
0462         si->reason = reason;
0463         si->destroyed = true;
0464         g_timeout_add (0, destroyStream, stream->ndata);
0465         return NPERR_NO_ERROR;
0466     }
0467     return NPERR_NO_DATA;
0468 }
0469 
0470 static void nsStatus (NPP instance, const char* message) {
0471     (void)instance;
0472     print ("NPN_Status %s\n", message ? message : "-");
0473 }
0474 
0475 static const char* nsUserAgent (NPP instance) {
0476     (void)instance;
0477     print ("NPN_UserAgent\n");
0478     return "";
0479 }
0480 
0481 static uint32 nsMemFlush (uint32 size) {
0482     (void)size;
0483     print ("NPN_MemFlush\n");
0484     return 0;
0485 }
0486 
0487 static void nsReloadPlugins (NPBool reloadPages) {
0488     (void)reloadPages;
0489     print ("NPN_ReloadPlugins\n");
0490 }
0491 
0492 /*static JRIEnv* nsGetJavaEnv () {
0493     print ("NPN_GetJavaEnv\n");
0494     return NULL;
0495 }*/
0496 
0497 static void *nsGetJavaEnv () {
0498     print ("NPN_GetJavaEnv\n");
0499     return NULL;
0500 }
0501 
0502 /*static jref nsGetJavaPeer (NPP instance) {
0503     (void)instance;
0504     print ("NPN_GetJavaPeer\n");
0505     return NULL;
0506 }*/
0507 
0508 static void *nsGetJavaPeer (NPP instance) {
0509     (void)instance;
0510     print ("NPN_GetJavaPeer\n");
0511     return NULL;
0512 }
0513 
0514 static NPError nsGetURLNotify (NPP instance, const char* url, const char* target, void *notify) {
0515     (void)instance;
0516     print ("NPN_GetURLNotify %s %s\n", url, target ? target : "");
0517     addStream (url, 0L, target, 0, NULL, notify, true);
0518     return NPERR_NO_ERROR;
0519 }
0520 
0521 static NPError nsPostURLNotify (NPP instance, const char* url, const char* target, uint32 len, const char* buf, NPBool file, void *notify) {
0522     (void)instance; (void)file;
0523     print ("NPN_PostURLNotify\n");
0524     addStream (url, 0L, target, len, buf, notify, true);
0525     return NPERR_NO_ERROR;
0526 }
0527 
0528 static NPError nsGetValue (NPP instance, NPNVariable variable, void *value) {
0529     print ("NPN_GetValue %d\n", variable & ~NP_ABI_MASK);
0530     switch (variable) {
0531         case NPNVxDisplay:
0532             *(void**)value = (void*)(long) gdk_x11_get_default_xdisplay ();
0533             break;
0534         case NPNVxtAppContext:
0535             *(void**)value = NULL;
0536             break;
0537         case NPNVnetscapeWindow:
0538             print ("NPNVnetscapeWindow\n");
0539             break;
0540         case NPNVjavascriptEnabledBool:
0541             *(NPBool*)value = 1;
0542             break;
0543         case NPNVasdEnabledBool:
0544             *(NPBool*)value = 0;
0545             break;
0546         case NPNVisOfflineBool:
0547             *(NPBool*)value = 0;
0548             break;
0549         case NPNVserviceManager:
0550             *(void**)value = NULL;
0551             return NPERR_GENERIC_ERROR;
0552             /* *(int*)value = 0;*/
0553             break;
0554         case NPNVToolkit:
0555             *(int*)value = NPNVGtk2;
0556             break;
0557         case NPNVSupportsXEmbedBool:
0558             *(NPBool*)value = 1;
0559             break;
0560         case NPNVSupportsWindowless:
0561             *(NPBool*)value = 0;
0562             break;
0563         case NPNVprivateModeBool:
0564             *(NPBool*)value = 0;
0565             break;
0566         case NPNVWindowNPObject:
0567             if (!js_window) {
0568                 JsObject *jo = (JsObject*) nsCreateObject (instance, &js_class);
0569                 jo->name = g_strdup ("window");
0570                 js_window = (NPObject *) jo;
0571             }
0572             *(NPObject**)value = nsRetainObject (js_window);
0573             break;
0574         case NPNVPluginElementNPObject: {
0575             JsObject * obj = (JsObject *) nsCreateObject (instance, &js_class);
0576             obj->name = g_strdup ("this");
0577             *(NPObject**)value = (NPObject *) obj;
0578             break;
0579         }
0580         default:
0581             print ("unknown value\n");
0582             return NPERR_GENERIC_ERROR;
0583     }
0584     return NPERR_NO_ERROR;
0585 }
0586 
0587 static NPError nsSetValue (NPP instance, NPPVariable variable, void *value) {
0588     /* NPPVpluginWindowBool */
0589     (void)instance; (void)value;
0590     print ("NPN_SetValue %d\n", variable & ~NP_ABI_MASK);
0591     return NPERR_NO_ERROR;
0592 }
0593 
0594 static void nsInvalidateRect (NPP instance, NPRect *invalidRect) {
0595     (void)instance; (void)invalidRect;
0596     print ("NPN_InvalidateRect\n");
0597 }
0598 
0599 static void nsInvalidateRegion (NPP instance, NPRegion invalidRegion) {
0600     (void)instance; (void)invalidRegion;
0601     print ("NPN_InvalidateRegion\n");
0602 }
0603 
0604 static void nsForceRedraw (NPP instance) {
0605     (void)instance;
0606     print ("NPN_ForceRedraw\n");
0607 }
0608 
0609 static NPIdentifier nsGetStringIdentifier (const NPUTF8* name) {
0610     /*print ("NPN_GetStringIdentifier %s\n", name);*/
0611     gpointer id = g_tree_lookup (identifiers, name);
0612     if (!id) {
0613         id = strdup (name);
0614         g_tree_insert (identifiers, id, id);
0615     }
0616     return id;
0617 }
0618 
0619 static void nsGetStringIdentifiers (const NPUTF8** names, int32_t nameCount,
0620         NPIdentifier* ids) {
0621     (void)names; (void)nameCount; (void)ids;
0622     print ("NPN_GetStringIdentifiers\n");
0623 }
0624 
0625 static NPIdentifier nsGetIntIdentifier (int32_t intid) {
0626     print ("NPN_GetIntIdentifier %d\n", intid);
0627     return (NPIdentifier) (long) intid;
0628 }
0629 
0630 static bool nsIdentifierIsString (NPIdentifier name) {
0631     print ("NPN_IdentifierIsString\n");
0632     return !!g_tree_lookup (identifiers, name);
0633 }
0634 
0635 static NPUTF8 * nsUTF8FromIdentifier (NPIdentifier name) {
0636     char *str = (char *)g_tree_lookup (identifiers, name);
0637     print ("NPN_UTF8FromIdentifier %s\n", str ? str : "not found");
0638     if (str)
0639         return strdup (str);
0640     return NULL;
0641 }
0642 
0643 static int32_t nsIntFromIdentifier (NPIdentifier identifier) {
0644     print ("NPN_IntFromIdentifier\n");
0645     return (int32_t) (long) identifier;
0646 }
0647 
0648 static bool nsInvoke (NPP instance, NPObject * npobj, NPIdentifier method,
0649         const NPVariant *args, uint32_t arg_count, NPVariant *result) {
0650     (void)instance;
0651     /*print ("NPN_Invoke %s\n", id);*/
0652     return npobj->_class->invoke (npobj, method, args, arg_count, result);
0653 }
0654 
0655 static bool nsInvokeDefault (NPP instance, NPObject * npobj,
0656         const NPVariant * args, uint32_t arg_count, NPVariant * result) {
0657     (void)instance;
0658     return npobj->_class->invokeDefault (npobj,args, arg_count, result);
0659 }
0660 
0661 static bool str2NPVariant (NPP instance, const char *str, NPVariant *result) {
0662     if (!str || !*str)
0663         return false;
0664     if (!strncmp (str, "o:", 2)) {
0665         JsObject *jo = (JsObject *)nsCreateObject (instance, &js_class);
0666         result->type = NPVariantType_Object;
0667         jo->name = g_strdup (str + 2);
0668         result->value.objectValue = (NPObject *)jo;
0669         print ("object\n");
0670     } else if (!strncmp (str, "s:", 2)) {
0671         result->type = NPVariantType_String;
0672         result->value.stringValue.utf8characters= g_strdup(str+2);
0673         result->value.stringValue.utf8length = strlen (str) - 2;
0674         print ("string %s\n", str + 2);
0675     } else if (!strncmp (str, "u:", 2)) {
0676         result->type = NPVariantType_Null;
0677         print ("null\n");
0678     } else if (!strncmp (str, "n:", 2)) {
0679         char *eptr;
0680         long l = strtol (str + 2, &eptr, 10);
0681         if (*eptr && *eptr == '.') {
0682             result->type = NPVariantType_Double;
0683             result->value.doubleValue = strtod (str + 2, NULL);
0684             print ("double %f\n", result->value.doubleValue);
0685         } else if (eptr != str + 2) {
0686             result->type = NPVariantType_Int32;
0687             result->value.intValue = (int)l;
0688             print ("int32 %d\n", l);
0689         } else {
0690             result->type = NPVariantType_Null;
0691             return false;
0692         }
0693     } else if (!strncmp (str, "b:", 2)) {
0694         result->type = NPVariantType_Bool;
0695         if (!strcasecmp (str + 2, "true")) {
0696             result->value.boolValue = true;
0697         } else {
0698             char *eptr;
0699             long l = strtol (str + 2, &eptr, 10);
0700             result->value.boolValue = eptr != str ? !!l : false;
0701         }
0702         print ("bool %d\n", result->value.boolValue);
0703     } else {
0704         return false;
0705     }
0706     return true;
0707 }
0708 
0709 static bool doEvaluate (NPP instance, NPObject * npobj, NPString * script,
0710         NPVariant * result) {
0711     char *result_string;
0712     bool success = false;
0713     (void) npobj; /*FIXME scope, search npobj window*/
0714 
0715     result_string = evaluate (script->utf8characters, true);
0716 
0717     if (result_string) {
0718         success = str2NPVariant (instance, result_string, result);
0719         g_free (result_string);
0720     }
0721 
0722     return success;
0723 }
0724 
0725 static bool nsEvaluate (NPP instance, NPObject * npobj, NPString * script,
0726         NPVariant * result) {
0727     NPString str;
0728     char *jsscript;
0729     char *escaped;
0730     bool res;
0731 
0732     print ("NPN_Evaluate:");
0733     escaped = g_strescape (script->utf8characters, "");
0734     str.utf8length = strlen (escaped) + 9;
0735     jsscript = (char *) nsAlloc (str.utf8length);
0736     sprintf (jsscript, "eval(\"%s\")", escaped);
0737     str.utf8characters = jsscript;
0738 
0739     res = doEvaluate (instance, npobj, &str, result);
0740 
0741     nsMemFree (jsscript);
0742     g_free (escaped);
0743 
0744     return res;
0745 }
0746 
0747 static bool nsGetProperty (NPP instance, NPObject * npobj,
0748         NPIdentifier property, NPVariant * result) {
0749     (void)instance;
0750     return npobj->_class->getProperty (npobj, property, result);
0751 }
0752 
0753 static bool nsSetProperty (NPP instance, NPObject * npobj,
0754         NPIdentifier property, const NPVariant *value) {
0755     (void)instance;
0756     return npobj->_class->setProperty (npobj, property, value);
0757 }
0758 
0759 static bool nsRemoveProperty (NPP inst, NPObject * npobj, NPIdentifier prop) {
0760     (void)inst;
0761     return npobj->_class->removeProperty (npobj, prop);
0762 }
0763 
0764 static bool nsHasProperty (NPP instance, NPObject * npobj, NPIdentifier prop) {
0765     (void)instance;
0766     return npobj->_class->hasProperty (npobj, prop);
0767 }
0768 
0769 static bool nsHasMethod (NPP instance, NPObject * npobj, NPIdentifier method) {
0770     (void)instance;
0771     return npobj->_class->hasMethod (npobj, method);
0772 }
0773 
0774 static void nsReleaseVariantValue (NPVariant * variant) {
0775     /*print ("NPN_ReleaseVariantValue\n");*/
0776     switch (variant->type) {
0777         case NPVariantType_String:
0778             if (variant->value.stringValue.utf8characters)
0779                 g_free ((char *) variant->value.stringValue.utf8characters);
0780             break;
0781         case NPVariantType_Object:
0782             if (variant->value.objectValue)
0783                 nsReleaseObject (variant->value.objectValue);
0784             break;
0785         default:
0786             break;
0787     }
0788     variant->type = NPVariantType_Null;
0789 }
0790 
0791 static void nsSetException (NPObject *npobj, const NPUTF8 *message) {
0792     (void)npobj;
0793     print ("NPN_SetException %s\n", message ? message : "-");
0794 }
0795 
0796 static bool nsPushPopupsEnabledState (NPP instance, NPBool enabled) {
0797     (void)instance;
0798     print ("NPN_PushPopupsEnabledState %d\n", enabled);
0799     return false;
0800 }
0801 
0802 static bool nsPopPopupsEnabledState (NPP instance) {
0803     (void)instance;
0804     print ("NPN_PopPopupsEnabledState\n");
0805     return false;
0806 }
0807 
0808 static bool nsEnumerate (NPP npp, NPObject *obj, NPIdentifier **identifier, uint32_t *count) {
0809     (void)npp; (void)obj; (void)identifier; (void)count;
0810     print ("NPN_Enumerate\n");
0811     return false;
0812 }
0813 
0814 typedef struct _AsyncCall {
0815     void (*func)(void *);
0816     void *arg;
0817 } AsyncCall;
0818 
0819 static gboolean asyncCall (gpointer data) {
0820     AsyncCall *ac = (AsyncCall *) data;
0821     print ("NPN_PluginThreadAsyncCall %p\n", ac->func);
0822     ac->func (ac->arg);
0823     nsMemFree (ac);
0824     return FALSE;
0825 }
0826 
0827 static void nsPluginThreadAsyncCall (NPP instance, void (*func)(void *), void *userData) {
0828     (void)instance;
0829     if (func) {
0830         AsyncCall *ac = (AsyncCall *) nsAlloc (sizeof (AsyncCall) );
0831         ac->func = func;
0832         ac->arg = userData;
0833         g_idle_add (asyncCall, ac);
0834     }
0835 }
0836 
0837 static bool nsConstruct (NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result) {
0838     (void)npp; (void)obj; (void)args; (void)argCount; (void)result;
0839     print ("NPN_Construct\n");
0840     return FALSE;
0841 }
0842 
0843 static NPError nsGetValueForURL (NPP npp, NPNURLVariable variable, const char *url, char **value, uint32_t *len) {
0844     (void)npp;
0845     print ("NPN_GetValueForURL\n");
0846     switch (variable) {
0847     case NPNURLVCookie:
0848         if (callback_service) {
0849             DBusMessage *rmsg;
0850             DBusMessage *msg = dbus_message_new_method_call (
0851                     callback_service,
0852                     callback_path,
0853                     "org.kde.kmplayer.callback",
0854                     "cookie");
0855             dbus_message_append_args (msg,
0856                     DBUS_TYPE_STRING, &url,
0857                     DBUS_TYPE_INVALID);
0858             rmsg = dbus_connection_send_with_reply_and_block (dbus_connection,
0859                     msg, 2000, NULL);
0860             if (rmsg) {
0861                 DBusMessageIter it;
0862                 if (dbus_message_iter_init (rmsg, &it) &&
0863                       DBUS_TYPE_STRING == dbus_message_iter_get_arg_type (&it)) {
0864                     char *cookies;
0865                     dbus_message_iter_get_basic (&it, &cookies);
0866                     *value = g_strdup (cookies);
0867                     *len = strlen (cookies);
0868                     print ("cookie %s => %s\n", url, cookies);
0869                 }
0870                 dbus_message_unref (rmsg);
0871                 break;
0872             } else {
0873                 print ("cookie no reply\n");
0874             }
0875         }
0876     case NPNURLVProxy:
0877     default:
0878         *value = (char *) nsAlloc (1);
0879         (*value)[0] = 0;
0880         *len = 0;
0881     }
0882     return NPERR_NO_ERROR;
0883 }
0884 
0885 static NPError nsSetValueForURL (NPP npp, NPNURLVariable variable, const char *url, const char *value, uint32_t len) {
0886     (void)npp; (void)variable; (void)url; (void)value; (void)len;
0887     print ("NPN_SetValueForURL\n");
0888     return NPERR_NO_ERROR;
0889 }
0890 
0891 static NPError nsGetAuthenticationInfo (NPP npp, const char *protocol, const char *host, int32_t port, const char *scheme, const char *realm, char **username, uint32_t *ulen, char **password, uint32_t *plen) {
0892     (void)npp;
0893     (void)protocol; (void)host; (void)port; (void)scheme; (void)realm;
0894     print ("NPN_GetAuthenticationInfo\n");
0895     *username = NULL;
0896     *ulen = 0;
0897     *password = NULL;
0898     *plen = 0;
0899     return NPERR_NO_ERROR;
0900 }
0901 
0902 /*----------------%<---------------------------------------------------------*/
0903 
0904 static bool doInvoke (uint32_t obj, const char *func, GSList *arglst,
0905         uint32_t arg_count, char **resultstring) {
0906     NPObject *npobj;
0907     NPVariant result;
0908     NPVariant *args = NULL;
0909 
0910     *resultstring = NULL;
0911     if (!obj && npp) { /*TODO NPObject tracking */
0912         NPError np_err = np_funcs.getvalue (npp,
0913                 NPPVpluginScriptableNPObject, (void*)&npobj);
0914         if (np_err == NPERR_NO_ERROR && npobj) {
0915             NPIdentifier method = nsGetStringIdentifier (func);
0916 
0917             if (nsHasMethod (npp, npobj, method)) {
0918                 GSList *sl;
0919                 int i;
0920                 if (arg_count) {
0921                     args = (NPVariant *) nsAlloc (arg_count * sizeof (NPVariant));
0922                     memset (args, 0, arg_count * sizeof (NPVariant));
0923                     for (sl = arglst, i = 0; sl; sl = sl->next, i++)
0924                         str2NPVariant (npp, (const char *) sl->data, args + i);
0925                 }
0926                 if (nsInvoke (npp, npobj, method, args, arg_count, &result)) {
0927                     *resultstring = nsVariant2Str (&result);
0928                     nsReleaseVariantValue (&result);
0929                     print ("nsInvoke succes %s\n", *resultstring);
0930                 } else {
0931                     print ("nsInvoke failure\n");
0932                 }
0933                 if (args) {
0934                     for (sl = arglst, i = 0; sl; sl = sl->next, i++)
0935                         nsReleaseVariantValue (args + i);
0936                     nsMemFree (args);
0937                 }
0938             }
0939             nsReleaseObject (npobj);
0940         } else {
0941             print("no obj %d\n", obj);
0942         }
0943     }
0944     if (!*resultstring) {
0945         *resultstring = g_strdup ("error");
0946         return false;
0947     }
0948     return true;
0949 }
0950 
0951 static bool doGet (uint32_t obj, const char *prop, char **resultstring) {
0952     NPObject *npobj;
0953     NPVariant result;
0954 
0955     *resultstring = NULL;
0956     if (!obj && npp) { /*TODO NPObject tracking */
0957         NPError np_err = np_funcs.getvalue (npp,
0958                 NPPVpluginScriptableNPObject, (void*)&npobj);
0959         if (np_err == NPERR_NO_ERROR && npobj) {
0960             NPIdentifier identifier = nsGetStringIdentifier (prop);
0961 
0962             if (nsHasMethod (npp, npobj, identifier)) {
0963                 *resultstring = g_strdup ("o:function");
0964             } else if (nsHasMethod (npp, npobj, identifier)) {
0965                 if (nsGetProperty (npp, npobj, identifier, &result)) {
0966                     *resultstring = nsVariant2Str (&result);
0967                     nsReleaseVariantValue (&result);
0968                 }
0969             }
0970             nsReleaseObject (npobj);
0971         }
0972     }
0973     if (!*resultstring) {
0974         *resultstring = g_strdup ("error");
0975         return false;
0976     }
0977     return true;
0978 }
0979 
0980 /*----------------%<---------------------------------------------------------*/
0981 
0982 static NPObject * windowClassAllocate (NPP instance, NPClass *aClass) {
0983     (void)instance;
0984     /*print ("windowClassAllocate\n");*/
0985     JsObject * jo = (JsObject *) nsAlloc (sizeof (JsObject));
0986     memset (jo, 0, sizeof (JsObject));
0987     jo->npobject._class = aClass;
0988     return (NPObject *) jo;
0989 }
0990 
0991 static void windowClassDeallocate (NPObject *npobj) {
0992     JsObject *jo = (JsObject *) npobj;
0993     /*print ("windowClassDeallocate\n");*/
0994     if (jo->parent) {
0995         nsReleaseObject ((NPObject *) jo->parent);
0996     } else if (jo->name && !strncmp (jo->name, "this.__kmplayer__obj_", 21)) {
0997         char *script = (char *) nsAlloc (strlen (jo->name) + 7);
0998         char *result;
0999         sprintf (script, "%s=null;", jo->name);
1000         result = evaluate (script, false);
1001         nsMemFree (script);
1002         g_free (result);
1003     }
1004     if (jo->name)
1005         g_free (jo->name);
1006     if (npobj == js_window) {
1007         print ("WARNING deleting window object\n");
1008         js_window = NULL;
1009     }
1010     nsMemFree (npobj);
1011 }
1012 
1013 static void windowClassInvalidate (NPObject *npobj) {
1014     (void)npobj;
1015     print ("windowClassInvalidate\n");
1016 }
1017 
1018 static bool windowClassHasMethod (NPObject *npobj, NPIdentifier name) {
1019     (void)npobj; (void)name;
1020     print ("windowClassHasMehtod\n");
1021     return false;
1022 }
1023 
1024 static bool windowClassInvoke (NPObject *npobj, NPIdentifier method,
1025         const NPVariant *args, uint32_t arg_count, NPVariant *result) {
1026     JsObject * jo = (JsObject *) npobj;
1027     NPString str = { NULL, 0 };
1028     char buf[4096];
1029     int pos, i;
1030     bool res;
1031     char * id = (char *) g_tree_lookup (identifiers, method);
1032     /*print ("windowClassInvoke\n");*/
1033 
1034     result->type = NPVariantType_Null;
1035     result->value.objectValue = NULL;
1036 
1037     if (!id) {
1038         print ("Invoke invalid id\n");
1039         return false;
1040     }
1041     print ("Invoke %s\n", id);
1042     createJsName (jo, (char **)&str.utf8characters, &str.utf8length);
1043     pos = snprintf (buf, sizeof (buf), "%s.%s(", str.utf8characters, id);
1044     nsMemFree ((char *) str.utf8characters);
1045     for (i = 0; i < (int)arg_count; i++) {
1046         char *arg = nsVariant2Str (args + i);
1047         pos += snprintf (buf + pos,
1048                 sizeof (buf) - pos, i ? ",%s" : "%s", *arg ? arg : "undefined");
1049         nsMemFree (arg);
1050     }
1051     pos += snprintf (buf + pos, sizeof (buf) - pos, ")");
1052 
1053     str.utf8characters = buf;
1054     str.utf8length = pos;
1055     res = doEvaluate (npp, npobj, &str, result);
1056 
1057     return res;
1058 }
1059 
1060 static bool windowClassInvokeDefault (NPObject *npobj,
1061         const NPVariant *args, uint32_t arg_count, NPVariant *result) {
1062     (void)npobj; (void)args; (void)arg_count; (void)result;
1063     print ("windowClassInvokeDefault\n");
1064     return false;
1065 }
1066 
1067 static bool windowClassHasProperty (NPObject *npobj, NPIdentifier name) {
1068     (void)npobj; (void)name;
1069     print ("windowClassHasProperty\n");
1070     return false;
1071 }
1072 
1073 static bool windowClassGetProperty (NPObject *npobj, NPIdentifier property,
1074         NPVariant *result) {
1075     char * id = (char *) g_tree_lookup (identifiers, property);
1076     JsObject jo;
1077     NPString fullname = { NULL, 0 };
1078     bool res;
1079 
1080     print ("GetProperty %s\n", id);
1081     result->type = NPVariantType_Null;
1082     result->value.objectValue = NULL;
1083 
1084     if (!id)
1085         return false;
1086 
1087     if (!strcmp (((JsObject *) npobj)->name, "window") &&
1088                 !strcmp (id, "top")) {
1089         result->type = NPVariantType_Object;
1090         result->value.objectValue = nsRetainObject (js_window);
1091         return true;
1092     }
1093 
1094     jo.name = id;
1095     jo.parent = (JsObject *) npobj;
1096     createJsName (&jo, (char **)&fullname.utf8characters, &fullname.utf8length);
1097 
1098     res = doEvaluate (npp, npobj, &fullname, result);
1099 
1100     nsMemFree ((char *) fullname.utf8characters);
1101 
1102     return res;
1103 }
1104 
1105 static bool windowClassSetProperty (NPObject *npobj, NPIdentifier property,
1106         const NPVariant *value) {
1107     char *id = (char *) g_tree_lookup (identifiers, property);
1108     char *script, *var_name, *var_val, *res;
1109     JsObject jo;
1110     uint32_t len = 0;
1111 
1112     if (!id)
1113         return false;
1114 
1115     jo.name = id;
1116     jo.parent = (JsObject *) npobj;
1117     createJsName (&jo, &var_name, &len);
1118 
1119     var_val = nsVariant2Str (value);
1120     script = (char *) nsAlloc (len + strlen (var_val) + 3);
1121     sprintf (script, "%s=%s;", var_name, var_val);
1122     nsMemFree (var_name);
1123     nsMemFree (var_val);
1124     print ("SetProperty %s\n", script);
1125 
1126     res = evaluate (script, false);
1127     if (res)
1128         g_free (res);
1129     nsMemFree (script);
1130 
1131 
1132     return true;
1133 }
1134 
1135 static bool windowClassRemoveProperty (NPObject *npobj, NPIdentifier name) {
1136     (void)npobj; (void)name;
1137     print ("windowClassRemoveProperty\n");
1138     return false;
1139 }
1140 
1141 
1142 /*----------------%<---------------------------------------------------------*/
1143 
1144 static void shutDownPlugin() {
1145     if (npShutdown) {
1146         if (npp) {
1147             np_funcs.destroy (npp, &saved_data);
1148             nsMemFree (npp);
1149             npp = 0L;
1150         }
1151         npShutdown();
1152         npShutdown = 0;
1153     }
1154 }
1155 
1156 static void readStdin (gpointer p, gint src, GdkInputCondition cond) {
1157     char *buf_ptr = stream_buf;
1158     gsize bytes_read = read (src,
1159             stream_buf + stream_buf_pos,
1160             sizeof (stream_buf) - stream_buf_pos);
1161     (void)cond; (void)p;
1162     if (bytes_read > 0)
1163         stream_buf_pos += bytes_read;
1164 
1165     /*print ("readStdin %d\n", bytes_read);*/
1166     while (buf_ptr < stream_buf + stream_buf_pos) {
1167         uint32_t write_len;
1168         int32_t bytes_written;
1169 
1170         if (callback_service && !stream_chunk_size) {
1171             /* read header info */
1172             if (stream_buf + stream_buf_pos < buf_ptr + 2 * sizeof (uint32_t))
1173                 break; /* need more data */
1174             current_stream_id = (gpointer)(long)*(uint32_t*)(buf_ptr);
1175             stream_chunk_size = *((uint32_t *)(buf_ptr + sizeof (uint32_t)));
1176         /*print ("header %d %d\n",(long)current_stream_id, stream_chunk_size);*/
1177             buf_ptr += 2 * sizeof (uint32_t);
1178             if (stream_chunk_size && stream_buf + stream_buf_pos == buf_ptr) {
1179                 stream_buf_pos = 0;
1180                 break; /* only read the header for chunk with data */
1181             }
1182         }
1183         /* feed it to the stream */
1184         write_len = stream_buf + stream_buf_pos - buf_ptr;
1185         if (callback_service && write_len > stream_chunk_size)
1186             write_len = stream_chunk_size;
1187         bytes_written = writeStream (current_stream_id, buf_ptr, write_len);
1188         if (bytes_written < 0) {
1189             print ("couldn't write to stream %d\n", (long)current_stream_id);
1190             bytes_written = write_len; /* assume stream destroyed, skip */
1191         }
1192 
1193         /* update chunk status */
1194         if (bytes_written > 0) {
1195             buf_ptr += bytes_written;
1196            /*print ("update chunk %d %d\n", bytes_written, stream_chunk_size);*/
1197             stream_chunk_size -= bytes_written;
1198         } else {
1199             /* FIXME if plugin didn't accept the data retry later, suspend stdin reading */
1200             break;
1201         }
1202 
1203     }
1204     /* update buffer */
1205     /*print ("buffer written:%d bufpos:%d\n", buf_ptr-stream_buf, stream_buf_pos);*/
1206     if (stream_buf + stream_buf_pos == buf_ptr) {
1207         stream_buf_pos = 0;
1208     } else {
1209         g_assert (buf_ptr < stream_buf + stream_buf_pos);
1210         stream_buf_pos -= (stream_buf + stream_buf_pos - buf_ptr);
1211         memmove (stream_buf, buf_ptr, stream_buf_pos);
1212     }
1213     if (bytes_read <= 0) { /* eof of stdin, only for 'cat foo | knpplayer' */
1214         StreamInfo*si=(StreamInfo*)g_tree_lookup(stream_list,current_stream_id);
1215         si->reason = NPRES_DONE;
1216         removeStream (current_stream_id);
1217         if (stdin_read_watch) {
1218             gdk_input_remove (stdin_read_watch);
1219             stdin_read_watch = 0;
1220         }
1221     }
1222 }
1223 
1224 static int initPlugin (const char *plugin_lib) {
1225     NPError np_err;
1226     char *pname;
1227 
1228     print ("starting %s\n", plugin_lib);
1229     library = g_module_open (plugin_lib, G_MODULE_BIND_LAZY);
1230     if (!library) {
1231         print ("failed to load %s %s\n", plugin_lib, g_module_error ());
1232         return -1;
1233     }
1234     if (!g_module_symbol (library,
1235                 "NP_GetMIMEDescription", (gpointer *)&npGetMIMEDescription)) {
1236         print ("undefined reference to load NP_GetMIMEDescription\n");
1237         return -1;
1238     }
1239     if (!g_module_symbol (library,
1240                 "NP_GetValue", (gpointer *)&npGetValue)) {
1241         print ("undefined reference to load NP_GetValue\n");
1242     }
1243     if (!g_module_symbol (library,
1244                 "NP_Initialize", (gpointer *)&npInitialize)) {
1245         print ("undefined reference to load NP_Initialize\n");
1246         return -1;
1247     }
1248     if (!g_module_symbol (library,
1249                 "NP_Shutdown", (gpointer *)&npShutdown)) {
1250         print ("undefined reference to load NP_Shutdown\n");
1251         return -1;
1252     }
1253     print ("startup succeeded %s\n", npGetMIMEDescription ());
1254     memset (&ns_funcs, 0, sizeof (NPNetscapeFuncs));
1255     ns_funcs.size = sizeof (NPNetscapeFuncs);
1256     ns_funcs.version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
1257     ns_funcs.geturl = nsGetURL;
1258     ns_funcs.posturl = nsPostURL;
1259     ns_funcs.requestread = nsRequestRead;
1260     ns_funcs.newstream = nsNewStream;
1261     ns_funcs.write = nsWrite;
1262     ns_funcs.destroystream = nsDestroyStream;
1263     ns_funcs.status = nsStatus;
1264     ns_funcs.uagent = nsUserAgent;
1265     ns_funcs.memalloc = nsAlloc;
1266     ns_funcs.memfree = nsMemFree;
1267     ns_funcs.memflush = nsMemFlush;
1268     ns_funcs.reloadplugins = nsReloadPlugins;
1269     ns_funcs.getJavaEnv = nsGetJavaEnv;
1270     ns_funcs.getJavaPeer = nsGetJavaPeer;
1271     ns_funcs.geturlnotify = nsGetURLNotify;
1272     ns_funcs.posturlnotify = nsPostURLNotify;
1273     ns_funcs.getvalue = nsGetValue;
1274     ns_funcs.setvalue = nsSetValue;
1275     ns_funcs.invalidaterect = nsInvalidateRect;
1276     ns_funcs.invalidateregion = nsInvalidateRegion;
1277     ns_funcs.forceredraw = nsForceRedraw;
1278     ns_funcs.getstringidentifier = nsGetStringIdentifier;
1279     ns_funcs.getstringidentifiers = nsGetStringIdentifiers;
1280     ns_funcs.getintidentifier = nsGetIntIdentifier;
1281     ns_funcs.identifierisstring = nsIdentifierIsString;
1282     ns_funcs.utf8fromidentifier = nsUTF8FromIdentifier;
1283     ns_funcs.intfromidentifier = nsIntFromIdentifier;
1284     ns_funcs.createobject = nsCreateObject;
1285     ns_funcs.retainobject = nsRetainObject;
1286     ns_funcs.releaseobject = nsReleaseObject;
1287     ns_funcs.invoke = nsInvoke;
1288     ns_funcs.invokeDefault = nsInvokeDefault;
1289     ns_funcs.evaluate = nsEvaluate;
1290     ns_funcs.getproperty = nsGetProperty;
1291     ns_funcs.setproperty = nsSetProperty;
1292     ns_funcs.removeproperty = nsRemoveProperty;
1293     ns_funcs.hasproperty = nsHasProperty;
1294     ns_funcs.hasmethod = nsHasMethod;
1295     ns_funcs.releasevariantvalue = nsReleaseVariantValue;
1296     ns_funcs.setexception = nsSetException;
1297     ns_funcs.pushpopupsenabledstate = nsPushPopupsEnabledState;
1298     ns_funcs.poppopupsenabledstate = nsPopPopupsEnabledState;
1299     ns_funcs.enumerate = nsEnumerate;
1300     ns_funcs.pluginthreadasynccall = nsPluginThreadAsyncCall;
1301     ns_funcs.construct = nsConstruct;
1302     ns_funcs.getvalueforurl = nsGetValueForURL;
1303     ns_funcs.setvalueforurl = nsSetValueForURL;
1304     ns_funcs.getauthenticationinfo = nsGetAuthenticationInfo;
1305 
1306     js_class.structVersion = NP_CLASS_STRUCT_VERSION;
1307     js_class.allocate = windowClassAllocate;
1308     js_class.deallocate = windowClassDeallocate;
1309     js_class.invalidate = windowClassInvalidate;
1310     js_class.hasMethod = windowClassHasMethod;
1311     js_class.invoke = windowClassInvoke;
1312     js_class.invokeDefault = windowClassInvokeDefault;
1313     js_class.hasProperty = windowClassHasProperty;
1314     js_class.getProperty = windowClassGetProperty;
1315     js_class.setProperty = windowClassSetProperty;
1316     js_class.removeProperty = windowClassRemoveProperty;
1317 
1318     np_funcs.size = sizeof (NPPluginFuncs);
1319 
1320     np_err = npInitialize (&ns_funcs, &np_funcs);
1321     if (np_err != NPERR_NO_ERROR) {
1322         print ("NP_Initialize failure %d\n", np_err);
1323         npShutdown = 0;
1324         return -1;
1325     }
1326     np_err = npGetValue (NULL, NPPVpluginNameString, &pname);
1327     if (np_err == NPERR_NO_ERROR)
1328         print ("NP_GetValue Name %s\n", pname);
1329     np_err = npGetValue (NULL, NPPVpluginDescriptionString, &pname);
1330     if (np_err == NPERR_NO_ERROR)
1331         print ("NP_GetValue Description %s\n", pname);
1332     return 0;
1333 }
1334 
1335 static int newPlugin (NPMIMEType mime, int16 argc, char *argn[], char *argv[]) {
1336     NPError np_err;
1337     Display *display;
1338     int screen;
1339     int i;
1340     int needs_xembed;
1341     uint32_t width = 0, height = 0;
1342 
1343     for (i = 0; i < argc; i++) {
1344         if (!strcasecmp (argn[i], "width"))
1345             width = strtol (argv[i], 0L, 10);
1346         else if (!strcasecmp (argn[i], "height"))
1347             height = strtol (argv[i], 0L, 10);
1348     }
1349     /*if (width > 0 && height > 0)
1350         callFunction (-1, "dimension",
1351                 DBUS_TYPE_INT32, &width, DBUS_TYPE_INT32, &height,
1352                 DBUS_TYPE_INVALID);*/
1353 
1354     npp = (NPP_t*)nsAlloc (sizeof (NPP_t));
1355     memset (npp, 0, sizeof (NPP_t));
1356     np_err = np_funcs.newp (mime, npp, NP_EMBED, argc, argn, argv, saved_data);
1357     if (np_err != NPERR_NO_ERROR) {
1358         print ("NPP_New failure %d %p %p\n", np_err, np_funcs, np_funcs.newp);
1359         return -1;
1360     }
1361     if (np_funcs.getvalue) {
1362         char *pname;
1363         void *iid;
1364         np_err = np_funcs.getvalue (npp,
1365                 NPPVpluginNameString, (void*)&pname);
1366         if (np_err == NPERR_NO_ERROR)
1367             print ("plugin name %s\n", pname);
1368         np_err = np_funcs.getvalue (npp,
1369                 NPPVpluginNeedsXEmbed, (void*)&needs_xembed);
1370         if (np_err != NPERR_NO_ERROR || !needs_xembed) {
1371             print ("NPP_GetValue NPPVpluginNeedsXEmbed failure %d\n", np_err);
1372             shutDownPlugin();
1373             return -1;
1374         }
1375         np_err = np_funcs.getvalue (npp,
1376                 NPPVpluginScriptableIID, (void*)&iid);
1377     }
1378     memset (&np_window, 0, sizeof (NPWindow));
1379     display = gdk_x11_get_default_xdisplay ();
1380     np_window.x = 0;
1381     np_window.y = 0;
1382     np_window.width = INITIAL_WINDOW_WIDTH;
1383     np_window.height = 1200;
1384     np_window.window = (void*)socket_id;
1385     np_window.type = NPWindowTypeWindow;
1386     ws_info.type = NP_SETWINDOW;
1387     screen = DefaultScreen (display);
1388     ws_info.display = display;
1389     ws_info.visual = DefaultVisual (display, screen);
1390     ws_info.colormap = DefaultColormap (display, screen);
1391     ws_info.depth = DefaultDepth (display, screen);
1392     print ("display %u %dx%d\n", socket_id, width, height);
1393     np_window.ws_info = (void*)&ws_info;
1394 
1395     GtkAllocation allocation;
1396     allocation.x = 0;
1397     allocation.y = 0;
1398     allocation.width = np_window.width;
1399     allocation.height = np_window.height;
1400     gtk_widget_size_allocate (xembed, &allocation);
1401 
1402     np_err = np_funcs.setwindow (npp, &np_window);
1403 
1404     return 0;
1405 }
1406 
1407 static bool startPlugin (const char *mime,
1408         int argc, char *argn[], char *argv[]) {
1409     if (!npp && (initPlugin (plugin) || newPlugin (mimetype, argc, argn, argv)))
1410         return false;
1411     return true;
1412 }
1413 
1414 /*----------------%<---------------------------------------------------------*/
1415 
1416 static StreamInfo *getStreamInfo (const char *path, gpointer *stream_id) {
1417     const char *p = strrchr (path, '_');
1418     *stream_id = p ? (gpointer) strtol (p+1, NULL, 10) : NULL;
1419     return (StreamInfo *) g_tree_lookup (stream_list, *stream_id);
1420 }
1421 
1422 static void defaultReply (DBusConnection *conn, DBusMessage *msg) {
1423     if (!dbus_message_get_no_reply (msg)) {
1424         DBusMessage *rmsg = dbus_message_new_method_return (msg);
1425         dbus_connection_send (conn, rmsg, NULL);
1426         dbus_connection_flush (conn);
1427         dbus_message_unref (rmsg);
1428     }
1429 }
1430 
1431 static bool dbusMsgIterGet (DBusMessage *msg, DBusMessageIter *it,
1432         int arg_type, void *p, bool first) {
1433     if (((first && dbus_message_iter_init (msg, it)) ||
1434                 (!first && dbus_message_iter_has_next (it) &&
1435                  dbus_message_iter_next (it))) &&
1436             dbus_message_iter_get_arg_type (it) == arg_type) {
1437         dbus_message_iter_get_basic (it, p);
1438         return true;
1439     }
1440     return false;
1441 }
1442 
1443 static DBusHandlerResult dbusStreamMessage (DBusConnection *conn,
1444         DBusMessage *msg, void *user_data) {
1445     DBusMessageIter args;
1446     const char *iface = "org.kde.kmplayer.backend";
1447     gpointer stream_id;
1448     StreamInfo *si;
1449     (void)user_data;
1450 
1451     print ("dbusStreamMessage %s %s %s\n", dbus_message_get_interface (msg),
1452             dbus_message_get_member (msg), dbus_message_get_signature (msg));
1453     if (dbus_message_is_method_call (msg, iface, "redirected")) {
1454         char *url = 0;
1455         si = getStreamInfo(dbus_message_get_path (msg), &stream_id);
1456         if (dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &url, true)) {
1457             if (si) {
1458                 dbus_message_iter_get_basic (&args, &url);
1459                 nsMemFree (si->url);
1460                 si->url = g_strdup (url);
1461                 si->np_stream.url = si->url;
1462                 print ("redirect %d (had data %d) to %s\n", (long)stream_id, si->called_plugin, url);
1463             } else {
1464                 print ("redirect %d not found\n", (long)stream_id);
1465             }
1466         }
1467         defaultReply (conn, msg);
1468     } else if (dbus_message_is_method_call (msg, iface, "eof")) {
1469         unsigned int total;
1470         if (dbusMsgIterGet (msg, &args, DBUS_TYPE_UINT32, &total, true)) {
1471             unsigned int reason;
1472             if (dbusMsgIterGet (msg, &args, DBUS_TYPE_UINT32, &reason, false)) {
1473                 si = getStreamInfo(dbus_message_get_path (msg), &stream_id);
1474                 if (si) {
1475                     si->total = total;
1476                     si->reason = reason;
1477                     print ("eof %d bytes:%d reason:%d\n", (long)stream_id, si->total, si->reason);
1478                     if (si->stream_pos == si->total || si->destroyed)
1479                         removeStream (stream_id);
1480                 } else {
1481                     print ("stream %d not found\n", stream_id);
1482                 }
1483             }
1484         }
1485         defaultReply (conn, msg);
1486     } else if (dbus_message_is_method_call (msg, iface, "streamInfo")) {
1487         const char *mime;
1488         if (dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &mime, true)) {
1489             const char *headers;
1490             uint32_t length;
1491             si = getStreamInfo(dbus_message_get_path (msg), &stream_id);
1492             if (si && *mime) {
1493                 if (si->mimetype)
1494                     g_free (si->mimetype);
1495                 si->mimetype = g_strdup (mime);
1496             }
1497             if (dbusMsgIterGet (msg, &args, DBUS_TYPE_UINT32, &length, false)) {
1498                 if (si)
1499                     si->np_stream.end = length;
1500                 if (dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &headers, false)) {
1501                     if (si && *headers) {
1502                         si->headers = g_strdup (headers);
1503                         si->np_stream.headers = si->headers;
1504                     }
1505                 }
1506             }
1507             print ("streamInfo %d size:%d mime:%s headers %s\n", (long)stream_id,
1508                     length, mime ? mime : "", headers ? headers : "");
1509         }
1510         defaultReply (conn, msg);
1511     }
1512     return DBUS_HANDLER_RESULT_HANDLED;
1513 }
1514 
1515 static void dbusStreamUnregister (DBusConnection *conn, void *user_data) {
1516     (void)conn; (void)user_data;
1517     print( "dbusStreamUnregister\n");
1518 }
1519 
1520 static void dbusPluginUnregister (DBusConnection *conn, void *user_data) {
1521     (void)conn; (void)user_data;
1522     print( "dbusPluginUnregister\n");
1523 }
1524 
1525 static const char *plugin_inspect =
1526     "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\""
1527     " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
1528     "<node>"
1529     " <interface name=\"org.freedesktop.DBus.Introspectable\">"
1530     "  <method name=\"Introspect\">"
1531     "   <arg name=\"xml_data\" type=\"s\" direction=\"out\"/>"
1532     "  </method>"
1533     " </interface>"
1534     "  <interface name=\"org.kde.kmplayer.backend\">"
1535     "   <method name=\"setup\">"
1536     "    <arg name=\"mimetype\" type=\"s\" direction=\"in\"/>"
1537     "    <arg name=\"plugin\" type=\"s\" direction=\"in\"/>"
1538     "    <arg name=\"arguments\" type=\"a{sv}\" direction=\"in\"/>"
1539     "   </method>"
1540     "   <method name=\"play\">"
1541     "    <arg name=\"url\" type=\"s\" direction=\"in\"/>"
1542     "   </method>"
1543     "  </interface>"
1544     "</node>";
1545 
1546 static DBusHandlerResult dbusPluginMessage (DBusConnection *conn,
1547         DBusMessage *msg, void *user_data) {
1548 
1549     DBusMessageIter args;
1550     const char *iface = "org.kde.kmplayer.backend";
1551     (void) user_data;
1552 
1553     print ("dbusPluginMessage %s %s %s\n", dbus_message_get_interface (msg),
1554             dbus_message_get_member (msg), dbus_message_get_signature (msg));
1555     if (dbus_message_is_method_call (msg,
1556                 "org.freedesktop.DBus.Introspectable", "Introspect")) {
1557         DBusMessage * rmsg = dbus_message_new_method_return (msg);
1558         dbus_message_append_args (rmsg,
1559                 DBUS_TYPE_STRING, &plugin_inspect, DBUS_TYPE_INVALID);
1560         dbus_connection_send (conn, rmsg, NULL);
1561         dbus_connection_flush (conn);
1562         dbus_message_unref (rmsg);
1563     } else if (dbus_message_is_method_call (msg, iface, "setup")) {
1564         DBusMessageIter ait;
1565         char *param = 0;
1566         unsigned int params = 0;
1567         char **argn = NULL;
1568         char **argv = NULL;
1569         GSList *arglst = NULL;
1570         if (!dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &param, true)) {
1571             g_printerr ("missing mimetype arg");
1572             return DBUS_HANDLER_RESULT_HANDLED;
1573         }
1574         mimetype = g_strdup (param);
1575         if (!dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &param, false)) {
1576             g_printerr ("missing plugin arg");
1577             return DBUS_HANDLER_RESULT_HANDLED;
1578         }
1579         plugin = g_strdup (param);
1580         if (!dbus_message_iter_next (&args) ||
1581                 DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type (&args)) {
1582             g_printerr ("missing params array");
1583             return DBUS_HANDLER_RESULT_HANDLED;
1584         }
1585         dbus_message_iter_recurse (&args, &ait);
1586         do {
1587             char *key, *value;
1588             DBusMessageIter di;
1589             int arg_type;
1590             if (dbus_message_iter_get_arg_type (&ait) != DBUS_TYPE_DICT_ENTRY)
1591                 break;
1592             dbus_message_iter_recurse (&ait, &di);
1593             if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&di))
1594                 break;
1595             dbus_message_iter_get_basic (&di, &key);
1596             if (!dbus_message_iter_next (&di))
1597                 break;
1598             arg_type = dbus_message_iter_get_arg_type (&di);
1599             if (DBUS_TYPE_STRING == arg_type) {
1600                 dbus_message_iter_get_basic (&di, &value);
1601             } else if (DBUS_TYPE_VARIANT == arg_type) {
1602                 DBusMessageIter vi;
1603                 dbus_message_iter_recurse (&di, &vi);
1604                 if (DBUS_TYPE_STRING != dbus_message_iter_get_arg_type (&vi))
1605                     break;
1606                 dbus_message_iter_get_basic (&vi, &value);
1607             } else {
1608                 break;
1609             }
1610             arglst = g_slist_append (arglst, g_strdup (key));
1611             arglst = g_slist_append (arglst, g_strdup (value));
1612             params++;
1613             print ("param %d:%s='%s'\n", params, key, value);
1614         } while (dbus_message_iter_has_next (&ait) &&
1615                 dbus_message_iter_next (&ait));
1616         if (params > 0 && params < 100) {
1617             int i;
1618             GSList *sl = arglst;
1619             argn = (char**) nsAlloc (params * sizeof (char *));
1620             argv = (char**) nsAlloc (params * sizeof (char *));
1621             for (i = 0; sl; i++) {
1622                 argn[i] = (gchar *)sl->data;
1623                 sl = sl->next;
1624                 argv[i] = (gchar *)sl->data;
1625                 sl = sl->next;
1626             }
1627             g_slist_free (arglst);
1628         }
1629         print ("setup %s %s params:%d\n",
1630                 mimetype ? mimetype : "", plugin, params);
1631         startPlugin (mimetype, params, argn, argv);
1632         defaultReply (conn, msg);
1633     } else if (dbus_message_is_method_call (msg, iface, "play")) {
1634         char *param = 0;
1635         if (!dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &param, true)) {
1636             g_printerr ("missing url arg");
1637             return DBUS_HANDLER_RESULT_HANDLED;
1638         }
1639         object_url = g_strdup (param);
1640         print ("play %s\n", object_url);
1641         if (mimetype && strncmp (mimetype, "application/x-java", 18))
1642             addStream (object_url, mimetype, 0L, 0, NULL, 0L, false);
1643         defaultReply (conn, msg);
1644     } else if (dbus_message_is_method_call (msg, iface, "get")) {
1645         DBusMessage * rmsg;
1646         uint32_t object;
1647         char *prop;
1648         if (dbusMsgIterGet (msg, &args, DBUS_TYPE_UINT32, &object, true) &&
1649                 dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &prop, false)) {
1650             char *result = NULL;
1651             doGet (object, prop, &result);
1652             print ("get %s => %s\n", prop, result ? result : "NULL");
1653             rmsg = dbus_message_new_method_return (msg);
1654             dbus_message_append_args (rmsg,
1655                     DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID);
1656             dbus_connection_send (conn, rmsg, NULL);
1657             dbus_connection_flush (conn);
1658             dbus_message_unref (rmsg);
1659             g_free (result);
1660         }
1661     } else if (dbus_message_is_method_call (msg, iface, "call")) {
1662         DBusMessage * rmsg;
1663         DBusMessageIter ait;
1664         uint32_t object;
1665         char *func;
1666         GSList *arglst = NULL;
1667         GSList *sl;
1668         uint32_t arg_count = 0;
1669         char *result = NULL;
1670         if (dbusMsgIterGet (msg, &args, DBUS_TYPE_UINT32, &object, true) &&
1671                 dbusMsgIterGet (msg, &args, DBUS_TYPE_STRING, &func, false)) {
1672             if (!dbus_message_iter_next (&args) ||
1673                     DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type (&args)) {
1674                 g_printerr ("missing arguments array");
1675                 return DBUS_HANDLER_RESULT_HANDLED;
1676             }
1677             dbus_message_iter_recurse (&args, &ait);
1678             print ("call %d:%s(", object, func);
1679             do {
1680                 char *arg;
1681                 if (dbus_message_iter_get_arg_type (&ait) != DBUS_TYPE_STRING)
1682                     break;
1683                 dbus_message_iter_get_basic (&ait, &arg);
1684                 print ("%s, ", arg);
1685                 arglst = g_slist_append (arglst, g_strdup (arg));
1686                 arg_count++;
1687             } while (dbus_message_iter_has_next (&ait) &&
1688                     dbus_message_iter_next (&ait));
1689             doInvoke (object, func, arglst, arg_count, &result);
1690             print (") %s\n", result ? result : "NULL");
1691             rmsg = dbus_message_new_method_return (msg);
1692             dbus_message_append_args (rmsg,
1693                     DBUS_TYPE_STRING, &result, DBUS_TYPE_INVALID);
1694             dbus_connection_send (conn, rmsg, NULL);
1695             dbus_connection_flush (conn);
1696             dbus_message_unref (rmsg);
1697             g_free (result);
1698             if (arglst) {
1699                 for (sl = arglst; sl; sl = sl->next)
1700                     g_free ((char *)sl->data);
1701                 g_slist_free (arglst);
1702             }
1703         }
1704     } else if (dbus_message_is_method_call (msg, iface, "quit")) {
1705         print ("quit\n");
1706         shutDownPlugin();
1707         defaultReply (conn, msg);
1708         gtk_main_quit();
1709     }
1710     return DBUS_HANDLER_RESULT_HANDLED;
1711 }
1712 
1713 static void callFunction(int stream, const char *iface, const char *func, int first_arg_type, ...)
1714 {
1715     char path[64];
1716     createPath (stream, path, sizeof (path));
1717     print ("call %s.%s()\n", path, func);
1718     if (callback_service) {
1719         va_list var_args;
1720         DBusMessage *msg = dbus_message_new_method_call (
1721                 callback_service,
1722                 path,
1723                 iface,
1724                 func);
1725         if (first_arg_type != DBUS_TYPE_INVALID) {
1726             va_start (var_args, first_arg_type);
1727             dbus_message_append_args_valist (msg, first_arg_type, var_args);
1728             va_end (var_args);
1729         }
1730         dbus_message_set_no_reply (msg, TRUE);
1731         dbus_connection_send (dbus_connection, msg, NULL);
1732         dbus_message_unref (msg);
1733         dbus_connection_flush (dbus_connection);
1734     }
1735 }
1736 
1737 static char *evaluate (const char *script, bool store) {
1738     char * ret = NULL;
1739     print ("evaluate %s", script);
1740     if (callback_service) {
1741         DBusMessage *rmsg;
1742         DBusMessage *msg = dbus_message_new_method_call (
1743                 callback_service,
1744                 callback_path,
1745                 "org.kde.kmplayer.callback",
1746                 "evaluate");
1747         int bool_val = store;
1748         dbus_message_append_args (msg,
1749                 DBUS_TYPE_STRING, &script,
1750                 DBUS_TYPE_BOOLEAN, &bool_val,
1751                 DBUS_TYPE_INVALID);
1752         rmsg = dbus_connection_send_with_reply_and_block (dbus_connection,
1753                 msg, 2000, NULL);
1754         if (rmsg) {
1755             DBusMessageIter it;
1756             if (dbus_message_iter_init (rmsg, &it) &&
1757                     DBUS_TYPE_STRING == dbus_message_iter_get_arg_type (&it)) {
1758                 char * param;
1759                 dbus_message_iter_get_basic (&it, &param);
1760                 ret = g_strdup (param);
1761             }
1762             dbus_message_unref (rmsg);
1763             print ("  => %s\n", ret);
1764         }
1765         dbus_message_unref (msg);
1766     } else {
1767         print ("  => NA\n");
1768     }
1769     return ret;
1770 }
1771 
1772 /*----------------%<---------------------------------------------------------*/
1773 
1774 static void pluginAdded (GtkSocket *socket, gpointer d) {
1775     /*(void)socket;*/ (void)d;
1776     print ("pluginAdded\n");
1777     if (socket->plug_window) {
1778         gpointer user_data = NULL;
1779         gdk_window_get_user_data (socket->plug_window, &user_data);
1780         if (!user_data) {
1781             /**
1782              * GtkSocket resets plugins XSelectInput in
1783              * _gtk_socket_add_window
1784              *   _gtk_socket_windowing_select_plug_window_input
1785              **/
1786             XSelectInput (gdk_x11_get_default_xdisplay (),
1787                     gdk_x11_drawable_get_xid (socket->plug_window),
1788                     KeyPressMask | KeyReleaseMask |
1789                     ButtonPressMask | ButtonReleaseMask |
1790                     KeymapStateMask |
1791                     ButtonMotionMask |
1792                     PointerMotionMask |
1793                     /*EnterWindowMask | LeaveWindowMask |*/
1794                     FocusChangeMask |
1795                     ExposureMask |
1796                     StructureNotifyMask | SubstructureNotifyMask |
1797                     /*SubstructureRedirectMask |*/
1798                     PropertyChangeMask
1799                     );
1800         }
1801     }
1802     callFunction (-1, iface_callback, "plugged", DBUS_TYPE_INVALID);
1803 }
1804 
1805 static gboolean pluginRemoved (GtkSocket *socket, gpointer d) {
1806     (void)socket; (void)d;
1807     return TRUE;
1808 }
1809 
1810 static void windowCreatedEvent (GtkWidget *w, gpointer d) {
1811     (void)d;
1812     print ("windowCreatedEvent\n");
1813     socket_id = gtk_socket_get_id (GTK_SOCKET (xembed));
1814     if (parent_id) {
1815         print ("windowCreatedEvent %p\n", GTK_PLUG (w)->socket_window);
1816         /*if (!GTK_PLUG (w)->socket_window)
1817             gtk_plug_construct (GTK_PLUG (w), parent_id);
1818         gdk_window_reparent( w->window,
1819                 GTK_PLUG (w)->socket_window
1820                     ? GTK_PLUG (w)->socket_window
1821                     : gdk_window_foreign_new (parent_id),
1822                 0, 0);*/
1823         XReparentWindow (gdk_x11_drawable_get_xdisplay (w->window),
1824                 gdk_x11_drawable_get_xid (w->window),
1825                 parent_id,
1826                 0, 0);
1827         gtk_widget_show_all (w);
1828     }
1829     if (!callback_service) {
1830         char *argn[] = { g_strdup("WIDTH"), g_strdup("HEIGHT"), g_strdup("debug"), g_strdup("SRC") };
1831         char *argv[] = { g_strdup("440"), g_strdup("330"), g_strdup("yes"), g_strdup(object_url) };
1832         if (startPlugin (mimetype, 4, argn, argv))
1833             addStream (object_url, mimetype, 0L, 0, NULL, 0L, false);
1834     }
1835 }
1836 
1837 static void embeddedEvent (GtkPlug *plug, gpointer d) {
1838     (void)plug; (void)d;
1839     print ("embeddedEvent\n");
1840 }
1841 
1842 static gboolean updateDimension (void * p) {
1843     (void)p;
1844     if (np_window.window) {
1845         if ((int)np_window.width != top_w || (int)np_window.height != top_h) {
1846             np_window.width = top_w;
1847             np_window.height = top_h;
1848             np_funcs.setwindow (npp, &np_window);
1849         }
1850         update_dimension_timer = 0;
1851         return 0; /* single shot */
1852     } else {
1853         return 1;
1854     }
1855 }
1856 
1857 static gboolean configureEvent(GtkWidget *w, GdkEventConfigure *e, gpointer d) {
1858     static int first_configure_pre_size;
1859     (void)w; (void)d;
1860     print("configureEvent %dx%d\n", e->width, e->height);
1861     if (!first_configure_pre_size && e->width == INITIAL_WINDOW_WIDTH) {
1862         first_configure_pre_size = 1;
1863         return FALSE;
1864     }
1865     if (e->width != top_w || e->height != top_h) {
1866         top_w = e->width;
1867         top_h = e->height;
1868         if (!update_dimension_timer)
1869             update_dimension_timer = g_timeout_add (100, updateDimension, NULL);
1870     }
1871     return FALSE;
1872 }
1873 
1874 static gboolean windowCloseEvent (GtkWidget *w, GdkEvent *e, gpointer d) {
1875     (void)w; (void)e; (void)d;
1876     shutDownPlugin();
1877     return FALSE;
1878 }
1879 
1880 static void windowDestroyEvent (GtkWidget *w, gpointer d) {
1881     (void)w; (void)d;
1882     gtk_main_quit();
1883 }
1884 
1885 static gboolean initPlayer (void * p) {
1886     static DBusObjectPathVTable pluginVTable;
1887     GtkWidget *window;
1888     GdkColormap *color_map;
1889     GdkColor bg_color;
1890     (void)p;
1891 
1892     window = /*callback_service
1893         ? gtk_plug_new (parent_id)
1894         :*/ gtk_window_new (GTK_WINDOW_TOPLEVEL);
1895     g_signal_connect (G_OBJECT (window), "delete_event",
1896             G_CALLBACK (windowCloseEvent), NULL);
1897     g_signal_connect (G_OBJECT (window), "destroy",
1898             G_CALLBACK (windowDestroyEvent), NULL);
1899     g_signal_connect_after (G_OBJECT (window), "realize",
1900             GTK_SIGNAL_FUNC (windowCreatedEvent), NULL);
1901     g_signal_connect (G_OBJECT (window), "configure-event",
1902             GTK_SIGNAL_FUNC (configureEvent), NULL);
1903 
1904     xembed = gtk_socket_new();
1905     g_signal_connect (G_OBJECT (xembed), "plug-added",
1906             GTK_SIGNAL_FUNC (pluginAdded), NULL);
1907     g_signal_connect (G_OBJECT (xembed), "plug-removed",
1908             GTK_SIGNAL_FUNC (pluginRemoved), NULL);
1909 
1910     color_map = gdk_colormap_get_system();
1911     gdk_colormap_query_color (color_map, 0, &bg_color);
1912     gtk_widget_modify_bg (xembed, GTK_STATE_NORMAL, &bg_color);
1913 
1914     gtk_container_add (GTK_CONTAINER (window), xembed);
1915 
1916     if (!parent_id) {
1917         gtk_widget_set_size_request (window, 440, 330);
1918         gtk_widget_show_all (window);
1919     } else {
1920         g_signal_connect (G_OBJECT (window), "embedded",
1921                 GTK_SIGNAL_FUNC (embeddedEvent), NULL);
1922         gtk_widget_set_size_request (window, INITIAL_WINDOW_WIDTH, 1200);
1923         gtk_widget_realize (window);
1924     }
1925 
1926     if (callback_service && callback_path) {
1927         DBusError dberr;
1928         char myname[64];
1929 
1930         dbus_error_init (&dberr);
1931         dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &dberr);
1932         if (!dbus_connection) {
1933             g_printerr ("Failed to open connection to bus: %s\n",
1934                     dberr.message);
1935             exit (1);
1936         }
1937         g_sprintf (myname, "org.kde.kmplayer.npplayer-%d", getpid ());
1938         service_name = g_strdup (myname);
1939         print ("using service %s was '%s'\n", service_name, dbus_bus_get_unique_name (dbus_connection));
1940         dbus_connection_setup_with_g_main (dbus_connection, 0L);
1941         dbus_bus_request_name (dbus_connection, service_name,
1942                 DBUS_NAME_FLAG_REPLACE_EXISTING, &dberr);
1943         if (dbus_error_is_set (&dberr)) {
1944             g_printerr ("Failed to register name: %s\n", dberr.message);
1945             dbus_connection_unref (dbus_connection);
1946             return -1;
1947         }
1948 
1949         pluginVTable.unregister_function = dbusPluginUnregister;
1950         pluginVTable.message_function = dbusPluginMessage;
1951         if (!dbus_connection_register_object_path (dbus_connection, "/plugin",
1952                     &pluginVTable, NULL))
1953             g_printerr ("dbus_connection_register_object_path error\n");
1954 
1955 
1956         /* TODO: remove DBUS_BUS_SESSION and create a private connection */
1957         //callFunction (-1, "running",
1958         //        DBUS_TYPE_STRING, &service_name, DBUS_TYPE_INVALID);
1959 
1960         dbus_connection_flush (dbus_connection);
1961 
1962         fprintf (stdout, "NPP_DBUS_SRV=%s\n", service_name);
1963         fflush (stdout);
1964     }
1965     return 0; /* single shot */
1966 }
1967 
1968 int main (int argc, char **argv) {
1969     int i;
1970 
1971     g_thread_init (NULL);
1972     gtk_init (&argc, &argv);
1973 
1974     for (i = 1; i < argc; i++) {
1975         if (!strcmp (argv[i], "-p") && ++i < argc) {
1976             plugin = g_strdup (argv[i]);
1977         } else if (!strcmp (argv[i], "-cb") && ++i < argc) {
1978             gchar *cb = g_strdup (argv[i]);
1979             gchar *path = strchr(cb, '/');
1980             if (path) {
1981                 callback_path = g_strdup (path);
1982                 *path = 0;
1983             }
1984             callback_service = g_strdup (cb);
1985             g_free (cb);
1986         } else if (!strcmp (argv[i], "-m") && ++i < argc) {
1987             mimetype = g_strdup (argv[i]);
1988         } else if (!strcmp (argv [i], "-wid") && ++i < argc) {
1989             parent_id = strtol (argv[i], 0L, 10);
1990         } else
1991             object_url = g_strdup (argv[i]);
1992     }
1993     if (!callback_service && !(object_url && mimetype && plugin)) {
1994         g_fprintf(stderr, "Usage: %s <-m mimetype -p plugin url|-cb service -wid id>\n", argv[0]);
1995         return 1;
1996     }
1997 
1998     identifiers = g_tree_new ((GCompareFunc)strcmp);
1999     stream_list = g_tree_new (streamCompare);
2000     stream_vtable.unregister_function = dbusStreamUnregister;
2001     stream_vtable.message_function = dbusStreamMessage;
2002 
2003     g_timeout_add (0, initPlayer, NULL);
2004 
2005     fcntl (0, F_SETFL, fcntl (0, F_GETFL) | O_NONBLOCK);
2006 
2007     print ("entering gtk_main\n");
2008 
2009     gtk_main();
2010 
2011     if (dbus_connection)
2012         dbus_connection_unref (dbus_connection);
2013 
2014     return 0;
2015 }
2016 }