Blender  V2.59
UnixShell.c
Go to the documentation of this file.
00001 /*
00002  * $Id: UnixShell.c 36276 2011-04-21 15:53:30Z campbellbarton $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): Enrico Fracasso
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  * NS api template, adapted to link to our own internals.
00029  */
00030 
00031 #define UNIXSH_VERSION "$Id: UnixShell.c 36276 2011-04-21 15:53:30Z campbellbarton $"
00032 #define MOZ_X11 1
00033 
00034 /* -*- Mode: C; tab-width: 8; c-set-style: bsd -*- */
00035 
00036 /* UnixShell.c was adapted from the template in the Netscape API. */
00037 
00038 /* System: */     
00039 #include <string.h>
00040 #include <stdlib.h>
00041 #include <unistd.h>
00042 
00043 /* All nsapi stuff. nsapi now needs FILE, so include stdio as well. */
00044 #include <stdio.h>
00045 #include "npapi.h"
00046 
00047 /* Native hooks: */
00048 #include "npapi.h"
00049 
00050 /* Threading the NSPR way: */
00051 #include "prthread.h"
00052 #include "prlock.h"
00053 
00054 #include "blender_plugin_types.h"
00055 
00056 #include <signal.h>
00057 
00058 /* --------------------------------------------------------------------- */
00059 
00061 #if defined(DEBUG)
00062 #define NZC_GENERATE_LOG
00063 #endif
00064 
00065 int32 STREAMBUFSIZE;
00066 
00068 static void
00069 log_entry(char* msg);
00070 
00071 
00072 void
00073 execute_blenderplayer(BlenderPluginInstance*);
00074 
00075 /* --------------------------------------------------------------------- */
00076 /* Implementations:                                                      */
00077 /* --------------------------------------------------------------------- */
00078 
00079 /* NPP_GetMIMEDescription() and NPP_GetValue() are called to determine
00080  * the mime types supported by this plugin. */
00081 char*
00082 NPP_GetMIMEDescription( void )
00083 {
00084         log_entry("NPP_GetMIMEDescription");
00085         return("application/x-blender-plugin:blend:Blender 3D web plugin");
00086 }
00087 
00088 NPError
00089 NPP_GetValue(
00090         NPP instance,
00091         NPPVariable variable,
00092         void *value
00093         )
00094 {
00095         NPError err = NPERR_NO_ERROR;
00096         
00097         log_entry("NPP_GetValue");
00098 
00099         switch (variable) {
00100         case NPPVpluginNeedsXEmbed:
00101                 log_entry("NPP_GetValue::NPPVpluginNeedsXEmbed");
00102                 *((PRBool *)value) = PR_TRUE;
00103                 break;
00104         case NPPVpluginNameString:
00105                 log_entry("NPP_GetValue::NPPVpluginNameString");
00106                 *((char **)value) = "Blender";
00107                 break;
00108         case NPPVpluginDescriptionString:
00109                 log_entry("NPP_GetValue::NPPVpluginDescriptionString");
00110                 *((char **)value) = "Player for interactive 3D content";
00111                 break;
00112         case NPPVpluginWindowBool:
00113                 log_entry("NPP_GetValue::NPPVpluginWindowBool");
00114                 *((PRBool *)value) = PR_FALSE; //not windowless
00115                 break;
00116         case NPPVpluginTransparentBool:
00117                 log_entry("NPP_GetValue::NPPVpluginTransparentBool");
00118                 *((PRBool *)value) = PR_FALSE; // not trasparent
00119                 break;
00120         default:
00121                 err = NPERR_GENERIC_ERROR;
00122         }
00123         return err;
00124 }
00125 
00126 /* --------------------------------------------------------------------- */
00127 /* Mozilla: NPP_Initialize() is called when
00128  * starting the browser, and then every time the plugin is started*/
00129 NPError
00130 NPP_Initialize(void)
00131 {
00132         log_entry("NPP_Initialize");
00133         return NPERR_NO_ERROR;
00134 }
00135 
00136 /* --------------------------------------------------------------------- */
00137 
00138 void
00139 NPP_Shutdown(void)
00140 {
00141         log_entry("NPP_Shutdown");
00142 }
00143 
00144 
00145 NPError 
00146 NPP_New(
00147         NPMIMEType pluginType,
00148         NPP instance,
00149         uint16 mode,
00150         int16 argc,
00151         char* argn[],
00152         char* argv[],
00153         NPSavedData* saved
00154         )
00155 {
00156         BlenderPluginInstance* This  = NULL;
00157         int i = 0;
00158         int retval = 0;
00159 
00160         log_entry("NPP_New");
00161         
00162         if (instance == NULL)
00163                 return NPERR_INVALID_INSTANCE_ERROR;
00164         
00165         instance->pdata = NPN_MemAlloc(sizeof(BlenderPluginInstance));
00166         if (instance->pdata == 0)
00167                 return NPERR_OUT_OF_MEMORY_ERROR;
00168         
00169         This = (BlenderPluginInstance*) instance->pdata;
00170         This->browser_instance = instance;
00171         This->pID = 0;
00172         This->blend_file = 0;
00173         This->temp_mail_file_name = 0;
00174         This->main_file_store = 0;
00175         This->display = NULL;
00176         This->window = 0;
00177 
00178         /* Parse the options from the file. Should I do this in the
00179          * implementation file maybe? Now we do a lot with
00180          * instance-specific data. */
00181         /*
00182         while (i <argc ) {
00183                 if (!strcmp(argn[i],"src")) {
00184                         The blend file to load. 
00185                         int url_len = strlen(argv[i]);
00186                         if ((url_len > 0) && (url_len < 4096) ) {
00187                                 This->blend_file = NPN_MemAlloc(url_len + 1);
00188                                 if (This->blend_file == 0)
00189                                         return NPERR_OUT_OF_MEMORY_ERROR;
00190                                 strcpy(This->blend_file, argv[i]);
00191                                 
00192                                 retval = NPN_GetURL(This->browser_instance,
00193                                                     This->blend_file,
00194                                                     NULL);
00195                                 if (retval != NPERR_NO_ERROR) {
00196                                         log_entry("Cannot read animation");
00197                                         NPN_Status(instance, "Cannot read animation file");
00198                                         This->blend_file = NULL;
00199                                         return NPERR_NO_ERROR;
00200                                 } else
00201                                         log_entry("Animation loaded"); 
00202                         }
00203                 }               
00204                 i++;
00205         }*/
00206                 
00207         if (This != NULL) {
00208                 return NPERR_NO_ERROR;
00209         } else
00210                 return NPERR_OUT_OF_MEMORY_ERROR;
00211 }
00212 
00213 
00214 NPError 
00215 NPP_Destroy( NPP instance, NPSavedData** save )
00216 {
00217         BlenderPluginInstance* This;
00218 
00219         log_entry("NPP_Destroy");
00220 
00221         if (instance == NULL)
00222                 return NPERR_INVALID_INSTANCE_ERROR;
00223 
00224         This = (BlenderPluginInstance*) instance->pdata;
00225         printf("NPP_Destroy ID:  0x%x %d\n", This->window, This->window);
00226 
00227         if (This != NULL) {
00228 
00229                 if (This->pID != 0) {
00230 #ifdef WITH_PRIVSEP
00231                         kill(This->pID, SIGTERM);
00232 #else 
00233                         kill(This->pID, SIGKILL); //if I have to kill blenderplayer directly I need to send SIGKILL
00234 #endif
00235                         wait(This->pID);
00236                         unlink(This->temp_mail_file_name);
00237                 }
00238 
00239                 // sometimes FF doesn't delete it's own window...
00240                 //printf("%s \n", NPN_UserAgent(instance));
00241                 /*if (This->display != NULL && This->window != 0)
00242                         XDestroyWindow(This->display, This->window);
00243                 */
00244                 if (This->blend_file) NPN_MemFree(This->blend_file);
00245                 if (This->temp_mail_file_name) NPN_MemFree(This->temp_mail_file_name);
00246                 if (This->main_file_store) NPN_MemFree(This->main_file_store);
00247                 NPN_MemFree(instance->pdata);
00248                 instance->pdata = NULL;
00249         }       
00250 
00251         return NPERR_NO_ERROR;
00252 }
00253 
00254 
00255 
00256 NPError 
00257 NPP_SetWindow( NPP instance,NPWindow* window ) 
00258 {
00259         BlenderPluginInstance* This;
00260 
00261         log_entry("NPP_SetWindow");
00262 
00263         if (instance == NULL)
00264                 return NPERR_INVALID_INSTANCE_ERROR;
00265 
00266         /* window handle */ 
00267         if ((window == NULL) || (window->window == NULL)) {
00268                 return NPERR_NO_ERROR; /* mmmmmm  */
00269         }
00270         
00271         if (window->ws_info == NULL)
00272                 return NPERR_NO_ERROR; /* mmmmmm  */
00273 
00274         This = (BlenderPluginInstance*) instance->pdata;
00275 
00276         if (This) {
00277                 This->window = (Window) window->window;
00278 
00279                 NPSetWindowCallbackStruct* window_info = window->ws_info;
00280                 This->display = window_info->display;
00281 
00282                 printf("ID window 0x%x %d\n", window->window, window->window);
00283                 return NPERR_NO_ERROR;
00284         } else {
00285                 return NPERR_INVALID_INSTANCE_ERROR;
00286         }
00287 }
00288 
00289 
00290 NPError 
00291 NPP_NewStream(
00292         NPP instance,
00293         NPMIMEType type,
00294         NPStream *stream, 
00295         NPBool seekable,
00296         uint16 *stype
00297         )
00298 {
00299         //NPByteRange range;
00300         BlenderPluginInstance* This;
00301 
00302         log_entry("NPP_NewStream");
00303         
00304         if (instance == NULL)
00305                 return NPERR_INVALID_INSTANCE_ERROR;
00306 
00307         This = (BlenderPluginInstance*) instance->pdata;
00308 
00309         if (!This) 
00310                 return NPERR_INVALID_INSTANCE_ERROR;
00311 
00312         printf("Loading main file %s (%s)\n", stream->url, type);
00313         if ( strcmp(type,"text/html") == 0 ) // original HTML file 
00314                 return NPERR_NO_ERROR;
00315         
00316         This->stream_total = stream->end;
00317         This->stream_retrieved = 0;
00318         This->main_file_store = NPN_MemAlloc(stream->end*sizeof(unsigned char));
00319         if (!This->main_file_store) {
00320                 fprintf(stderr, "Blender plugin: Out of memory! "
00321                         "Cannot get chunk for loading animation.\n");
00322                 return NPERR_OUT_OF_MEMORY_ERROR;
00323         }
00324 
00325         This->main_file_stream = stream;
00326 
00327         return NPERR_NO_ERROR;
00328                 
00329 }
00330 
00331 
00332 /* PLUGIN DEVELOPERS:
00333  *      These next 2 functions are directly relevant in a plug-in which
00334  *      handles the data in a streaming manner. If you want zero bytes
00335  *      because no buffer space is YET available, return 0. As long as
00336  *      the stream has not been written to the plugin, Navigator will
00337  *      continue trying to send bytes.  If the plugin doesn't want them,
00338  *      just return some large number from NPP_WriteReady(), and
00339  *      ignore them in NPP_Write().  For a NP_ASFILE stream, they are
00340  *      still called but can safely be ignored using this strategy.
00341  */
00342 
00343 int32 STREAMBUFSIZE = 0X0FFFFFFF; /* If we are reading from a file in NPAsFile
00344                                    * mode so we can take any size stream in our
00345                                    * write call (since we ignore it) */
00346 
00347 int32 
00348 NPP_WriteReady(
00349         NPP instance,
00350         NPStream *stream
00351         )
00352 {
00353         BlenderPluginInstance* This = NULL;
00354         int acceptable = 0;
00355         
00356         log_entry("NPP_WriteReady");
00357 
00358         if (instance == NULL)   
00359                 return NPERR_INVALID_INSTANCE_ERROR;
00360 
00361         This = (BlenderPluginInstance*) instance->pdata;
00362 
00363         if (This == NULL)       
00364                 return NPERR_INVALID_INSTANCE_ERROR;
00365 
00366         /* Check whether buffers already exist: */
00367 
00368         if ((This->main_file_stream && This->main_file_store)) {
00369                 acceptable = STREAMBUFSIZE;
00370         }
00371         
00372         
00373         return acceptable;
00374 }
00375 
00376 
00377 int32 
00378 NPP_Write(
00379         NPP instance,
00380         NPStream *stream,
00381         int32 offset,
00382         int32 len,
00383         void *buffer
00384         )
00385 {
00386         BlenderPluginInstance* This = NULL;
00387         int accepted = 0;
00388         
00389         log_entry("NPP_Write");
00390 
00391         if (instance == NULL)   
00392                 return NPERR_INVALID_INSTANCE_ERROR;
00393         
00394         This = (BlenderPluginInstance*) instance->pdata;
00395 
00396         if (This == NULL)       
00397                 return NPERR_INVALID_INSTANCE_ERROR;
00398 
00399         
00400         if (stream == This->main_file_stream) {
00401                 log_entry("NPP_Write: loading main_file_stream"); 
00402                 memcpy(((unsigned char*)This->main_file_store) + This->stream_retrieved, buffer, len);
00403                 accepted = len;
00404                 This->stream_retrieved += len;
00405                 if (This->stream_retrieved >= This->stream_total) {
00406                         log_entry("NPP_Write: main_file_stream loaded"); 
00407                         execute_blenderplayer(This);
00408                 }
00409         } else {
00410                 /* the stream ref wasn't set yet..*/
00411                 log_entry("NPP_Write: not main stream"); 
00412                 log_entry(stream->url);
00413 
00414                 accepted = len;
00415         }
00416 
00417         return accepted;
00418 }
00419 
00420 
00421 
00422 NPError 
00423 NPP_DestroyStream(
00424         NPP instance,
00425         NPStream *stream,
00426         NPError reason
00427         )
00428 {
00429         BlenderPluginInstance* This = NULL;
00430 
00431         log_entry("NPP_DestroyStream");
00432 
00433         if (instance == NULL)
00434                 return NPERR_INVALID_INSTANCE_ERROR;
00435         This = (BlenderPluginInstance*) instance->pdata;
00436 
00437         if (This) {
00438                 if (reason != NPRES_DONE) {
00439                         if (stream == This->main_file_stream) {                         
00440                                 // stream destroyed by NPP_Destroy
00441                                 NPN_Status(instance, "Cannot read animation file");
00442                                 //main_file_failed(This->application);
00443                         }
00444                 }
00445                 return NPERR_NO_ERROR;
00446         } else {
00447                 return NPERR_INVALID_INSTANCE_ERROR;
00448         }
00449 
00450 }
00451 
00452 
00453 /* Not supposed to be called anymore... Anyway, we don't need the
00454  * results. Some Moz implementations will call this one regardless the
00455  * desired transfer mode! */
00456 void 
00457 NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname )
00458 {
00459 /*      log_entry("NPP_StreamAsFile"); */
00460 }
00461 
00462 
00463 void 
00464 NPP_Print(NPP instance, NPPrint* printInfo ) 
00465 {
00466         
00467         log_entry("NPP_Print");
00468         if(printInfo == NULL)
00469                 return;
00470         if (instance != NULL) {
00471                 if (printInfo->mode == NP_FULL) {
00472                         printInfo->print.fullPrint.pluginPrinted = FALSE;
00473                 }
00474                 else {  /* If not fullscreen, we must be embedded */
00475                 }
00476         }
00477 }
00478 
00479 
00480 void
00481 execute_blenderplayer(BlenderPluginInstance* instance){
00482 
00483         char file_name[] = "/tmp/blender.XXXXXX";
00484         int fd = mkstemp(file_name);
00485 
00486         ssize_t real_size = write(fd, instance->main_file_store, instance->stream_retrieved);
00487         close(fd);
00488 
00489         instance->temp_mail_file_name = NPN_MemAlloc(strlen(file_name) + 1);
00490         strcpy(instance->temp_mail_file_name, file_name);
00491 
00492         instance->pID = fork();
00493         //XSelectInput(This->display , This->window, SubstructureNotifyMask);
00494         //XSync(This->display, FALSE);
00495         
00496 
00497 #if defined(WITH_APPARMOR)
00498         const char* executable = "blenderplayer-web"; 
00499 #elif defined(WITH_PRIVSEP)
00500         const char* executable = "blenderplayer-wrapper";
00501 #else   
00502         const char* executable = "blenderplayer";
00503 #endif
00504 
00505         if (instance->pID == 0) {              // child
00506                 char window_id[50];
00507                 sprintf(window_id, "%d", instance->window);
00508                 //exit(0);
00509 #ifdef WITH_PRIVSEP
00510                 execlp(executable, executable, file_name, window_id, (char*)NULL);
00511 #else 
00512                 execlp(executable, executable, "-i", window_id, file_name, (char*)NULL);
00513 #endif
00514         
00515         } else if (instance->pID < 0) {           // failed to fork
00516                 printf("Failed to fork!!!\n");                                  
00517         }
00518 
00519         /*XEvent e;
00520         int started = 0;
00521         while(!started) {
00522                 XNextEvent(This->display, &e);
00523                 printf("Event type %d\n", e.type);                                      
00524                 if (e.type == MapNotify) {
00525                         started = 1;
00526                         XCreateWindowEvent event =  e.xcreatewindow;
00527                         printf("Created window x:%d, y: %d, h: %d, w: %d\n", event.x, event.y, event.height, event.width);
00528                 }
00529         }*/
00530 
00531 }
00532 
00533 
00534 /* --------------------------------------------------------------------- */
00535 
00536 static void
00537 log_entry(char* msg)
00538 {
00539 #ifdef NZC_GENERATE_LOG 
00540         FILE* fp = fopen("/tmp/plugin_log","a");
00541         if (!fp) return;
00542         fprintf(fp, "--> Unixshell:: %s\n",
00543                 msg); 
00544         fflush(fp);
00545         fclose (fp);
00546 #endif
00547 }
00548 
00549 /* --------------------------------------------------------------------- */