File indexing completed on 2024-04-28 04:54:14

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