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