You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
14 KiB
583 lines
14 KiB
/***************************************************************************
|
|
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
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <X11/X.h>
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xos.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/StringDefs.h>
|
|
#include <X11/Intrinsic.h>
|
|
#include <X11/Shell.h>
|
|
#include <X11/Xaw/Box.h>
|
|
#include <X11/Xaw/Form.h>
|
|
#include <X11/Xaw/Label.h>
|
|
#include <X11/Xaw/Command.h>
|
|
#include <X11/Xaw/Text.h>
|
|
#include <X11/Xaw/AsciiText.h>
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <unistd.h>
|
|
|
|
|
|
#define XP_UNIX
|
|
|
|
#include "npapi.h"
|
|
#include "plugin.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
|
|
}
|
|
|
|
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;"
|
|
;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
NPError NPP_Initialize(void) {
|
|
|
|
xprintf("NPP_Initialize:\n");
|
|
if(!IsInitialised){
|
|
|
|
IsInitialised=1;
|
|
|
|
globals.url = NULL;
|
|
globals.kaffeine_started = 0;
|
|
|
|
}
|
|
return NPERR_NO_ERROR;
|
|
}
|
|
|
|
|
|
void * NPP_GetJavaClass() {
|
|
xprintf("NPP_GetJavaClass:\n");
|
|
return NULL;
|
|
}
|
|
|
|
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_);
|
|
|
|
}
|
|
|
|
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; i<argc; i++) {
|
|
|
|
xprintf ("plugin: argument '%s'='%s'\n",
|
|
argn[i], argv[i]);
|
|
|
|
if (!strcasecmp (argn[i], "href")) {
|
|
got_url (argv[i]);
|
|
xprintf ("got href url %s\n", globals.url);
|
|
} else if (!strcasecmp (argn[i], "src") && !globals.url) {
|
|
got_url (argv[i]);
|
|
xprintf ("got src url %s\n", globals.url);
|
|
} else if (!strcasecmp (argn[i], "autostart")) {
|
|
this->autostart = !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;
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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) */
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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 */
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
void NPP_Print(NPP instance, NPPrint* printInfo) {
|
|
|
|
xprintf("NPP_Print:\n");
|
|
if(printInfo == NULL)
|
|
return;
|
|
|
|
xprintf("NPP_Print: Not implemented. \n");
|
|
}
|
|
|
|
int16 NPP_HandleEvent(NPP instance, void* ev) {
|
|
|
|
xprintf("NPP_HandleEvent\n");
|
|
|
|
return 1;
|
|
}
|