/*************************************************************************** plugin.c A modified gxine mozilla plugin, to start kaffeine instead of gxine :-) ------------------- begin : Wen Oct 30 2003 revision : $Revision: 1.1.1.1 $ last modified : $Date: 2004/05/12 08:12:52 $ copyright : (C) 2003-2004 by J. Kofler email : kaffeine@gmx.net ***************************************************************************/ /* * Copyright (C) 2000-2004 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * * Id: plugin.c,v 1.22 2003/04/08 22:07:47 guenter Exp * * xine plugin for mozilla * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define XP_UNIX #include "npapi.h" #include "plugin.h" #include "plugin-exports.h" /* define LOG to write debug messages to /tmp/kaffeine_plugin.log */ /* #define LOG */ #define STATUS_STR_SIZE 1024 static char IsInitialised=0; /* global */ typedef struct { char *url; int kaffeine_started; } plugin_global_t; static plugin_global_t globals; /* per-instance */ typedef struct { Display *display; Screen *screen; Window window; Widget top_level; int width,height; int autostart; int imageWindow; char status_str[STATUS_STR_SIZE]; Pixel black, white; int is_visible; } plugin_instance_t; static void xprintf (const char *format, ...) { #ifdef LOG static FILE *log_fd=NULL; va_list argp; if (!log_fd) { log_fd = fopen ("/tmp/kaffeine_plugin.log", "a+"); if (log_fd) { setvbuf (log_fd, NULL, _IONBF, 0); fprintf (log_fd, "\n---------------------------------------------\n\n"); } } va_start (argp, format); if (log_fd) vfprintf (log_fd, format, argp); va_end (argp); #endif } PLUGIN_EXPORT char *NPP_GetMIMEDescription(void) { xprintf("NPP_GetMIMEDescription:\n"); return "video/mpeg: mpeg, mpg, mpe: MPEG animation;" "video/x-mpeg: mpeg, mpg, mpe: MPEG animation;" "audio/mpeg2: mp2: MPEG audio;" "audio/x-mpeg2: mp2: MPEG audio;" "audio/mpeg3: mp3: MPEG audio;" "audio/x-mpeg3: mp3: MPEG audio;" "audio/mpeg: mpa,abs,mpega: MPEG audio;" "audio/x-mpeg: mpa,abs,mpega: MPEG audio;" "video/quicktime: mov,qt: Quicktime animation;" "video/x-quicktime: mov,qt: Quicktime animation;" "video/msvideo: avi: AVI animation;" "video/x-msvideo: avi: AVI animation;" "application/x-mplayer2: asf,asx,asp: mplayer2;" "video/x-ms-asf-plugin: asf,asx,asp: mms animation;" "audio/x-pn-realaudio-plugin: rpm: Real audio;" "audio/x-ogg: ogg,ogm: OGG Media;" "audio/x-scpls: pls: MPEG audio;" "audio/x-ms-wma: wma: Microsoft Media Audio;" "video/x-ms-wmv: wmv: Microsoft Media Video;" "audio/x-mpegurl: m3u: Streaming-MPEG-Audio;" ; } PLUGIN_EXPORT NPError NPP_GetValue(void *future, NPPVariable variable, void *value){ NPError err = NPERR_NO_ERROR; xprintf("NPP_GetValue: variable=%d\n", variable); switch (variable) { case NPPVpluginNameString: *((char **)value) = "Kaffeine Starter Plugin"; break; case NPPVpluginDescriptionString: *((char **)value) = "Will start external Kaffeine Media Player for embedded media streams."; break; default: err = NPERR_GENERIC_ERROR; } return err; } PLUGIN_EXPORT NPError NPP_Initialize(void) { xprintf("NPP_Initialize:\n"); if(!IsInitialised){ IsInitialised=1; globals.url = NULL; globals.kaffeine_started = 0; } return NPERR_NO_ERROR; } PLUGIN_EXPORT void * NPP_GetJavaClass() { xprintf("NPP_GetJavaClass:\n"); return NULL; } PLUGIN_EXPORT void NPP_Shutdown(void) { xprintf("NPP_Shutdown:\n"); } static void print_status (plugin_instance_t *this, const char *format, ...) { va_list argp; va_start (argp, format); vsnprintf (this->status_str, STATUS_STR_SIZE, format, argp); va_end (argp); #if 0 paint_it (this); #endif } /* fork2() -- like fork, but the new process is immediately orphaned * (won't leave a zombie when it exits) * Returns 1 to the parent, not any meaningful pid. * The parent cannot wait() for the new process (it's unrelated). */ /* This version assumes that you *haven't* caught or ignored SIGCHLD. */ /* If you have, then you should just be using fork() instead anyway. */ static int fork2() { pid_t pid; int status; sigset_t set,oset; sigfillset(& set); xprintf (">>>>>>>>Forking<<<<<<<<,\n"); sigprocmask(SIG_SETMASK,&set,&oset); if (!(pid = fork())) { xprintf ("child\n"); switch (fork()) { case 0: xprintf ("child 2\n"); sigprocmask(SIG_SETMASK,&oset,&set); return 0; case -1: xprintf ("fork 2 failed!\n"); _exit(errno); /* assumes all errnos are <256 */ default: xprintf ("parent 2\n"); _exit(0); } } xprintf ("parent, child pid =%d\n", pid); if (pid < 0 || waitpid(pid,&status,0) < 0) { xprintf ("waitpid failed! (pid==%d)\n", pid); sigprocmask(SIG_SETMASK,&oset,&set); return -1; } sigprocmask(SIG_SETMASK,&oset,&set); xprintf ("waitpid done\n"); if (WIFEXITED(status)) if (WEXITSTATUS(status) == 0) { xprintf ("fork 2 succeeded\n"); return 1; } else errno = WEXITSTATUS(status); else errno = EINTR; /* well, sort of :-) */ xprintf ("parent done\n"); return -1; } static void launch_kaffeine(plugin_instance_t *this) { if (!globals.url) { xprintf ("launch_kaffeine: no url!\n"); return; } if (!fork2()) { xprintf ("launch_kaffeine: url = %s\n", globals.url); execlp("kaffeine","" , globals.url, NULL); if (execlp("kaffeine", NULL) == -1) { perror("Error while launching Kaffeine"); _exit(0); } } xprintf ("Kaffeine launched.\n"); globals.kaffeine_started = 1; } static void got_url (const char *url_) { if (strstr (url_, ":/")) globals.url = strdup (url_); else xprintf ("got_url: ignoring this url (%s) because it is a relative one.\n", url_); } PLUGIN_EXPORT NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) { plugin_instance_t* this; xprintf("NPP_New:\n"); if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; instance->pdata = NPN_MemAlloc(sizeof(plugin_instance_t)); this = (plugin_instance_t*) instance->pdata; globals.url = NULL; this->autostart = 0; this->imageWindow = 1; if (this != NULL) { int i; /* parse args */ for (i=0; iautostart = !strcasecmp (argv[i], "true"); xprintf ("got autostart %d\n", this->autostart); } else if (!strcasecmp (argn[i], "controls")) { this->imageWindow = !strcasecmp (argv[i], "imagewindow"); if (!this->imageWindow) xprintf("This is no ImageWindow!\n"); } } if ( globals.url && !globals.kaffeine_started && this->imageWindow ) launch_kaffeine(this); xprintf ("plugin: NPP_New done\n"); return NPERR_NO_ERROR; } else { xprintf ("plugin: out of memory :(\n"); return NPERR_OUT_OF_MEMORY_ERROR; } } PLUGIN_EXPORT NPError NPP_SetWindow(NPP instance, NPWindow* window) { plugin_instance_t* this; Widget hello, box; xprintf("NPP_SetWindow: 42\n"); if (instance == NULL) { xprintf("NPERR_INVALID_INSTANCE_ERROR\n"); return NPERR_INVALID_INSTANCE_ERROR; } if (window == NULL) { xprintf("window == NULL, NPERR_NO_ERROR\n"); return NPERR_NO_ERROR; } this = (plugin_instance_t*) instance->pdata; this->display = ((NPSetWindowCallbackStruct *) window->ws_info)->display; this->window = (Window) window->window; this->width = window->width; this->height = window->height; this->top_level = XtWindowToWidget (this->display, this->window); this->screen = XtScreen (this->top_level); xprintf("x=%lu, y=%lu, w=%lu, h=%lu\n", window->x, window->y, window->width, window->height); xprintf("window = %lu NPERR_NO_ERROR\n", this->window); this->black = BlackPixelOfScreen (this->screen); this->white = WhitePixelOfScreen (this->screen); XResizeWindow(this->display, this->window, this->width, this->height); /* XSetWindowBackground (this, this->window, this->black); */ XSync (this->display, FALSE); box = XtVaCreateManagedWidget ("form", formWidgetClass, this->top_level, XtNbackground, this->black, XtNwidth, this->width, XtNheight, this->height, NULL); if (this->imageWindow) { hello = XtVaCreateManagedWidget ("Kaffeine Starter Plugin", labelWidgetClass, box, XtNbackground, this->black, XtNforeground, this->white, XtNwidth, this->width, XtNheight, this->height, NULL); } XtRealizeWidget (box); xprintf("NPP_SetWindow: done.\n"); return NPERR_NO_ERROR; } PLUGIN_EXPORT NPError NPP_Destroy(NPP instance, NPSavedData** save) { plugin_instance_t* this; xprintf("NPP_Destroy:\n"); if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; this = (plugin_instance_t*) instance->pdata; if (this != NULL) { NPN_MemFree(instance->pdata); instance->pdata = NULL; } globals.kaffeine_started = FALSE; xprintf("NPP_Destroy: closed.\n"); return NPERR_NO_ERROR; } PLUGIN_EXPORT NPError NPP_NewStream (NPP instance, NPMIMEType type, NPStream *stream, NPBool seekable, uint16 *stype) { /* NPByteRange range; */ plugin_instance_t *this; xprintf("NPP_NewStream:\n"); if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; this = (plugin_instance_t*) instance->pdata; xprintf("NPP_NewStream: url is %s \n", stream->url); got_url (stream->url); if (!globals.kaffeine_started) { /* * now start kaffeine as an orphaned child */ launch_kaffeine(this); xprintf ("NPP_NewStream: Kaffeine started successfully\n"); } xprintf ("NPP_NewStream: done\n"); return NPERR_NO_ERROR; } /* PLUGIN DEVELOPERS: * These next 2 functions are directly relevant in a plug-in which * handles the data in a streaming manner. If you want zero bytes * because no buffer space is YET available, return 0. As long as * the stream has not been written to the plugin, Navigator will * continue trying to send bytes. If the plugin doesn't want them, * just return some large number from NPP_WriteReady(), and * ignore them in NPP_Write(). For a NP_ASFILE stream, they are * still called but can safely be ignored using this strategy. */ int32 STREAMBUFSIZE = 0X0FFFFFFF; /* If we are reading from a file in NPAsFile * mode so we can take any size stream in our * write call (since we ignore it) */ PLUGIN_EXPORT int32 NPP_WriteReady(NPP instance, NPStream *stream) { plugin_instance_t* this; xprintf("NPP_WriteReady:\n"); if (instance != NULL) this = (plugin_instance_t*) instance->pdata; return STREAMBUFSIZE; } PLUGIN_EXPORT int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer) { xprintf("NPP_Write:\n"); /* if (instance != NULL) { plugin_instance_t* this = (plugin_instance_t*) instance->pdata; } */ return -1; /* close stream */ } PLUGIN_EXPORT NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) { plugin_instance_t* this; xprintf("NPP_DestroyStream: \n"); if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; this = (plugin_instance_t*) instance->pdata; return NPERR_NO_ERROR; } PLUGIN_EXPORT void NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname) { plugin_instance_t* this; xprintf("NPP_StreamAsFile:\n"); if (instance != NULL) this = (plugin_instance_t*) instance->pdata; } PLUGIN_EXPORT void NPP_Print(NPP instance, NPPrint* printInfo) { xprintf("NPP_Print:\n"); if(printInfo == NULL) return; xprintf("NPP_Print: Not implemented. \n"); } PLUGIN_EXPORT int16 NPP_HandleEvent(NPP instance, void* ev) { xprintf("NPP_HandleEvent\n"); return 1; }