webbmp.c
上传用户:juruiw
上传日期:2007-01-04
资源大小:101k
文件大小:30k
源码类别:

PlugIns编程

开发平台:

Visual C++

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <malloc.h>
  6. #include <stdarg.h>
  7. #include "npupp.h"
  8. #include "npapi.h"
  9. #include "resource.h"
  10. NPError NPN_GetValue(NPP instance, NPNVariable variable, void *ret_value);
  11. #define DLLFILENAME "npwebbmp.dll"
  12. #define PRGNAME "WebBMP Plug-in"
  13. #define VERS    "1.1.0"
  14. #define FHS 14  /* bitmap file header size ( == sizeof(BITMAPFILEHEADER) )*/
  15. #define ST_NOTLOADED 0
  16. #define ST_LOADED    1
  17. /* plugin-specific data */
  18. typedef struct
  19. {
  20. NPWindow*  fWindow;
  21. uint16     fMode;
  22. HWND       fhWnd;
  23. WNDPROC    fDefaultWindowProc;
  24. NPP        instance;
  25. int windowless;
  26. char url[2048];  /* the url of the stream */
  27. int status;      /* is the image finished loading? (either ST_LOADED or ST_NOTLOADED) */
  28. LPBITMAPINFOHEADER lpdib;     /* the memory block we're storing the image in */
  29. int diballoc;    /* number of bytes allocated for hdib */
  30. int dibused;     /* number of bytes read into the DIB so far (there may be unused bytes in hdib) 
  31.                   * if status==ST_LOADED, this equals the size of the DIB */
  32. int streamend;   /* size of stream, or 0 if unknown */
  33. BITMAPFILEHEADER fileheader;
  34. int imgwidth,imgheight, imgbpp, imgcompr, palettesize;
  35. int prevpainted;
  36. FILETIME lastpaint;  /* used to prevent repainting too often during progressive display */
  37. } PluginInstance;
  38. /* global variables */
  39. extern NPNetscapeFuncs* g_pNavigatorFuncs;
  40. const char* gInstanceLookupString = "pdata";
  41. HMODULE hInst;
  42. HMENU hmenuContext=NULL;
  43. /* forward declarations of functions */
  44. LRESULT CALLBACK DlgProcAbout(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
  45. LRESULT CALLBACK PluginWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
  46. void PaintBitmap(PluginInstance *This, HDC hdc, RECT *rect, int erase);
  47. void ContextMenu(PluginInstance *This, int x, int y, HWND hwnd);
  48. /* ----------------------------------------------------------------------- */
  49. /* safe version of strncpy, which always null-terminates strings */
  50. void Strncpy(char *dest, const char *src, int len)
  51. {
  52.    if(src) {
  53.       strncpy(dest,src,len-1);
  54.       dest[len-1]='';
  55.    }
  56.    else dest[0]='';
  57. }
  58. HWND GetHWND(PluginInstance *This)
  59. {
  60. HWND hwnd;
  61. if(!This->windowless) {
  62. return This->fhWnd;
  63. }
  64. NPN_GetValue(This->instance, NPNVnetscapeWindow, (void*)&hwnd);
  65. return hwnd;
  66. }
  67. void warn(PluginInstance *This, char *fmt, ...)
  68. {
  69. va_list ap;
  70. char buf[2048];
  71. HWND hwnd;
  72. va_start(ap, fmt);
  73. vsprintf(buf,fmt, ap);
  74. va_end(ap);
  75. if(This) hwnd=GetHWND(This);
  76. else hwnd=NULL;
  77. MessageBox(hwnd,buf,"WebBMP Plug-in",MB_OK|MB_ICONWARNING);
  78. }
  79. void WindowlessPaint(PluginInstance *This)
  80. {
  81. HDC hdc;
  82. RECT rect;
  83. if(!This->fWindow) return;
  84. if(!This->fWindow->window) return;
  85. /* This is to work around an problem where Netscape initially
  86.  * makes spurious one or more SetWindow calls with coordinates of (0,0).
  87.  * This isn't a perfect fix, since (0,0) is sometimes the correct
  88.  * coordinates. */
  89. if( !This->prevpainted && This->fMode==NP_EMBED && 
  90. This->fWindow->x==0 && This->fWindow->y==0)
  91. {
  92. return;
  93. }
  94. This->prevpainted=1;
  95. hdc=(HDC)(This->fWindow->window);
  96. rect.left=   This->fWindow->x;
  97. rect.top=    This->fWindow->y;
  98. rect.right=  This->fWindow->width;
  99. rect.bottom= This->fWindow->height;
  100. PaintBitmap(This, hdc, &rect, 1);
  101. }
  102. /* ----------------------------------------------------------------------- */
  103. /* Global initialization */
  104. NPError NPP_Initialize(void)
  105. {
  106. hInst=GetModuleHandle(DLLFILENAME);
  107. if(!hInst) {
  108. warn(NULL,"Cannot load resources. Make sure plug-in file is named %s",DLLFILENAME);
  109. }
  110. hmenuContext=LoadMenu(hInst,"CONTEXTMENU");
  111. return NPERR_NO_ERROR;
  112. }
  113. /* Global shutdown */
  114. void NPP_Shutdown(void)
  115. {
  116. if(hmenuContext) DestroyMenu(hmenuContext);
  117. }
  118. /* Once-per-instance initialization */
  119. NPError NPP_New(NPMIMEType pluginType,NPP instance,uint16 mode,
  120. int16 argc,char* argn[],char* argv[],NPSavedData* saved)
  121. {
  122. PluginInstance* This;
  123. int i;
  124. if (instance == NULL) {
  125. return NPERR_INVALID_INSTANCE_ERROR;
  126. }
  127. /* instance->pdata = NPN_MemAlloc(sizeof(PluginInstance)); */
  128. instance->pdata = malloc(sizeof(PluginInstance));
  129. This = (PluginInstance*) instance->pdata;
  130. if (This == NULL) {
  131.     return NPERR_OUT_OF_MEMORY_ERROR;
  132. }
  133. This->windowless=0;
  134. /* examine the <embed> tag argumente */
  135. for(i=0;i<argc;i++) {
  136. if(!_stricmp(argn[i],"windowless")) {
  137. if(atoi(argv[i])==1) This->windowless=1;
  138. }
  139. }
  140. /* record some info for later lookup */
  141. This->fWindow = NULL;
  142. This->fMode = mode;
  143. This->fhWnd = NULL;
  144. This->fDefaultWindowProc = NULL;
  145. This->instance = instance;  /* save the instance id for reverse lookups */
  146. This->lpdib=NULL;
  147. This->diballoc=0;
  148. This->dibused=0;
  149. This->status=ST_NOTLOADED;
  150. This->prevpainted=0;
  151. if(This->windowless) {
  152. /* tell the browser we are windowless */
  153. NPN_SetValue(This->instance,NPPVpluginWindowBool,(void*)0);
  154. /* tell the browser if we are opaque(0) or transparent(1) */
  155. NPN_SetValue(This->instance,NPPVpluginTransparentBool,(void*)0);
  156. }
  157. return NPERR_NO_ERROR;
  158. }
  159. NPError NPP_Destroy(NPP instance, NPSavedData** save)
  160. {
  161. PluginInstance* This;
  162. if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;
  163. This = (PluginInstance*) instance->pdata;
  164. if(!This) return NPERR_INVALID_INSTANCE_ERROR;
  165. if( This->fhWnd ) { /* If we have a window, clean it up. */
  166. SetWindowLong( This->fhWnd, GWL_WNDPROC, (LONG)This->fDefaultWindowProc);
  167. This->fDefaultWindowProc = NULL;
  168. This->fhWnd = NULL;
  169. }
  170. if(This->lpdib) {
  171. GlobalFree(This->lpdib);
  172. This->lpdib=NULL;
  173. This->diballoc=0;
  174. }
  175. if (This != NULL) {
  176. if(instance->pdata)
  177. /* NPN_MemFree(instance->pdata);    -- seems to crash IE sometimes... */
  178. free(instance->pdata);
  179. instance->pdata = NULL;
  180. }
  181. return NPERR_NO_ERROR;
  182. }
  183. jref NPP_GetJavaClass(void)
  184. {
  185. return NULL;
  186. }
  187. /* Browser is providing us with a window */
  188. NPError NPP_SetWindow(NPP instance, NPWindow* window)
  189. {
  190. NPError result = NPERR_NO_ERROR;
  191. PluginInstance* This;
  192. if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;
  193. This = (PluginInstance*) instance->pdata;
  194. if(This->windowless) {
  195. This->fhWnd = NULL;
  196. This->fWindow = window;
  197. WindowlessPaint(This);
  198. return NPERR_NO_ERROR;
  199. }
  200. /* if( This->fWindow != NULL )  -- from example in Netscape's API, but not really correct */
  201. if( This->fhWnd != NULL ) {   /* If we already have a window... */
  202. if( (window == NULL) || ( window->window == NULL ) ) {
  203. /* There is now no window to use. get rid of the old
  204.  * one and exit. */
  205. SetWindowLong( This->fhWnd, GWL_WNDPROC, (LONG)This->fDefaultWindowProc);
  206. This->fDefaultWindowProc = NULL;
  207. This->fhWnd = NULL;
  208. This->fWindow=window;
  209. return NPERR_NO_ERROR;
  210. }
  211. else if ( This->fhWnd == (HWND) window->window ) {
  212. /* The new window is the same as the old one. Redraw and get out. */
  213. This->fWindow=window;
  214. InvalidateRect( This->fhWnd, NULL, FALSE );
  215. /* UpdateWindow( This->fhWnd ); */
  216. return NPERR_NO_ERROR;
  217. }
  218. else {
  219. /* Unsubclass the old window, so that we can subclass the new
  220.  * one later. */
  221. SetWindowLong( This->fhWnd, GWL_WNDPROC, (LONG)This->fDefaultWindowProc);
  222. This->fDefaultWindowProc = NULL;
  223. This->fhWnd = NULL;
  224. }
  225. }
  226. else if( (window == NULL) || ( window->window == NULL ) ) {
  227. /* We can just get out of here if there is no current
  228.  * window and there is no new window to use. */
  229. This->fWindow=window;
  230. return NPERR_NO_ERROR;
  231. }
  232. /* Subclass the new window so that we can begin drawing and
  233.  * receiving window messages. */
  234. This->fDefaultWindowProc = (WNDPROC)SetWindowLong( (HWND)window->window, GWL_WNDPROC, (LONG)PluginWindowProc);
  235. This->fhWnd = (HWND) window->window;
  236. SetProp( This->fhWnd, gInstanceLookupString, (HANDLE)This);
  237. This->fWindow = window;
  238. InvalidateRect( This->fhWnd, NULL, TRUE );
  239. UpdateWindow( This->fhWnd );
  240. return result;
  241. }
  242. /*+++++++++++++++++++++++++++++++++++++++++++++++++
  243.  * NPP_NewStream:
  244.  * Notifies an instance of a new data stream and returns an error value. 
  245.  * 
  246.  * NPP_NewStream notifies the instance denoted by instance of the creation of
  247.  * a new stream specifed by stream. The NPStream* pointer is valid until the
  248.  * stream is destroyed. The MIME type of the stream is provided by the
  249.  * parameter type. 
  250.  +++++++++++++++++++++++++++++++++++++++++++++++++*/
  251. NPError  NPP_NewStream(NPP instance,NPMIMEType type,NPStream *stream, 
  252.       NPBool seekable,uint16 *stype) {
  253. PluginInstance* This;
  254. if(instance==NULL)
  255. return NPERR_INVALID_INSTANCE_ERROR;
  256. This = (PluginInstance*) instance->pdata;
  257. if(!This)
  258. return NPERR_GENERIC_ERROR;
  259. /* save the URL for later */
  260. Strncpy(This->url,stream->url,2048);
  261. /* (*stype)=NP_ASFILE; -- request stream to be given as a file */
  262. if(This->lpdib) {
  263. GlobalFree(This->lpdib);
  264. This->lpdib=NULL;
  265. This->diballoc=0;
  266. }
  267. This->status=ST_NOTLOADED;
  268. This->streamend=stream->end;
  269. This->lastpaint.dwLowDateTime = This->lastpaint.dwHighDateTime = 0;
  270. return NPERR_NO_ERROR;
  271. }
  272. int32 NPP_WriteReady(NPP instance, NPStream *stream)
  273. {
  274. /* Number of bytes ready to accept in NPP_Write() */
  275. /* We can handle any amount, so just return some really big number. */
  276. return (int32)0X0FFFFFFF;
  277. }
  278. /*+++++++++++++++++++++++++++++++++++++++++++++++++
  279.  * NPP_Write:
  280.  * Delivers data from a stream and returns the number of bytes written. 
  281.  * 
  282.  * NPP_Write is called after a call to NPP_NewStream in which the plug-in
  283.  * requested a normal-mode stream, in which the data in the stream is delivered
  284.  * progressively over a series of calls to NPP_WriteReady and NPP_Write. The
  285.  * function delivers a buffer buf of len bytes of data from the stream identified
  286.  * by stream to the instance. The parameter offset is the logical position of
  287.  * buf from the beginning of the data in the stream. 
  288.  * 
  289.  * The function returns the number of bytes written (consumed by the instance).
  290.  * A negative return value causes an error on the stream, which will
  291.  * subsequently be destroyed via a call to NPP_DestroyStream. 
  292.  * 
  293.  * Note that a plug-in must consume at least as many bytes as it indicated in the
  294.  * preceeding NPP_WriteReady call. All data consumed must be either processed
  295.  * immediately or copied to memory allocated by the plug-in: the buf parameter
  296.  * is not persistent. 
  297.  +++++++++++++++++++++++++++++++++++++++++++++++++*/
  298. int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
  299. {
  300. PluginInstance* This;
  301. int32 amt;
  302. unsigned char *tmphdr, *tmp1;
  303. void *tmp_from, *tmp_to;
  304. int alloc, n;
  305. if (instance == NULL) return -1;
  306. This = (PluginInstance*) instance->pdata;
  307. if(offset<FHS) {
  308. tmphdr=(unsigned char*)(&This->fileheader);
  309. amt=FHS-offset;
  310. if(amt>len) amt=len;
  311. CopyMemory(&tmphdr[offset],buffer,amt);
  312. }
  313. if(offset+len > FHS) {
  314. if(!This->lpdib) {
  315. /* Allocate a memory block for the whole image, but how big should
  316.  * it be? If the stream size is available from the browser, we'll 
  317.  * use that. Otherwise we'll use the info from the header of the BMP
  318.  * file (it's not as reliable, though). */
  319. if(This->streamend>0) {
  320. alloc=This->streamend - FHS;
  321. }
  322. else {
  323. /* This will normally allocate 14 extra bytes. Some BMP files
  324.  * have a size indicator that's 14 bytes off, so this protects
  325.  * against that. */
  326. alloc=This->fileheader.bfSize  /* - FHS */ ;
  327. }
  328. if(alloc<128) alloc=128;
  329. This->lpdib=(void*)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,alloc);
  330. This->diballoc=alloc;
  331. }
  332. if(!This->lpdib) {
  333. warn(This,"Out of memory");
  334. return -1;
  335. }
  336. if(This->diballoc < offset+len-FHS) {
  337. /* Oops, our memory block isn't big enough for the whole file.
  338.  * --> Bump it up by 128K or so.
  339.  * This will only happen if the BMP had an incorrect file size
  340.  * in the header. Unfortunately that is common. */
  341. This->diballoc=offset+len+131072;
  342. This->lpdib=GlobalReAlloc(This->lpdib,This->diballoc, GMEM_ZEROINIT);
  343. }
  344. if(!This->lpdib) {
  345. warn(This,"Out of memory");
  346. return -1;
  347. }
  348. tmp1=(unsigned char*)(buffer);
  349. if(offset<FHS) {
  350. tmp_from= &tmp1[FHS-offset];
  351. tmp_to= (void*)This->lpdib;
  352. n=len-(FHS-offset);
  353. }
  354. else {
  355. tmp_from=tmp1;
  356. tmp_to= (void*)& ((unsigned char*)This->lpdib)[offset-FHS];
  357. n=len;
  358. }
  359. CopyMemory(tmp_to,tmp_from,n);
  360. }
  361. This->dibused   = offset + len - FHS;
  362. /* progressive display */
  363. if(This->windowless) {
  364. /* WindowlessPaint(This); */
  365. }
  366. else if(This->fhWnd) {
  367. RECT r;
  368. HDC hdc;
  369. FILETIME ft;
  370. /* Since we repaint the whole image every time we do a progressive
  371.  * display update, it can slow things down if we do this too often,
  372.  * especially if the image is being scaled to a different size. To
  373.  * prevent this, we make sure a certain amount of time has elapsed
  374.  * since the last update */
  375. /* FILETIME (dwLoDateTime) is in 100 ns intervals, so 10000 == 1 second
  376.  *
  377.  * The comparison has a small bug in it that is not important here,
  378.  * so don't copy this code without understanding it! */
  379. GetSystemTimeAsFileTime(&ft);
  380. if( (ft.dwHighDateTime > This->lastpaint.dwHighDateTime) ||
  381. (ft.dwLowDateTime > This->lastpaint.dwLowDateTime + 10000))
  382. {
  383. This->lastpaint.dwHighDateTime = ft.dwHighDateTime;
  384. This->lastpaint.dwLowDateTime  = ft.dwLowDateTime;
  385. GetClientRect(This->fhWnd,&r);
  386. hdc=GetDC(This->fhWnd);
  387. PaintBitmap(This,hdc,&r,0);   /* FIXME maybe should be 1 first time? */
  388. ReleaseDC(This->fhWnd,hdc);
  389. }
  390. }
  391. return len; /* The number of bytes accepted -- we always accept them all. */
  392. }
  393. /* DestroyStream gets called after the file has finished loading,
  394.  */
  395. NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
  396. {
  397. PluginInstance* This;
  398. if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;;
  399. This = (PluginInstance*) instance->pdata;
  400. if(reason==NPRES_DONE) {
  401. This->status=ST_LOADED;
  402. if(This->lpdib) {
  403. /* Record some image metrics for convenience. 
  404.  * Except for imgcompr(ession), these are inessential and
  405.  * only used in the About dialog. */
  406. This->imgwidth  = (int)This->lpdib->biWidth;
  407. This->imgheight = (int)This->lpdib->biHeight;
  408. This->imgbpp    = (int)This->lpdib->biBitCount;
  409. This->imgcompr  = (int)This->lpdib->biCompression;
  410. This->palettesize=0;
  411. if(This->imgbpp<=8) {   /* paletted */
  412. if(This->lpdib->biSize>=36) {  /* does header have an entry for palette size? */
  413. This->palettesize=This->lpdib->biClrUsed;
  414. }
  415. if(This->palettesize==0) {  /* zero (or nonexistent) means maximum */
  416. This->palettesize= (1<<This->imgbpp);
  417. }
  418. }
  419. }
  420. /* Now that we have the whole image, draw it. */
  421. if(This->windowless) {
  422. WindowlessPaint(This);
  423. }
  424. else if(This->fhWnd) {
  425. InvalidateRect(This->fhWnd,NULL,FALSE);
  426. UpdateWindow(This->fhWnd);
  427. }
  428. }
  429. return NPERR_NO_ERROR;
  430. }
  431. /* This should not get called, since we aren't using the NP_ASFILE mode */
  432. void NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
  433. {
  434. PluginInstance* This;
  435. if (instance == NULL) return;
  436. This = (PluginInstance*) instance->pdata;
  437. }
  438. /* Print embedded plug-in (via the browser's Print command) */
  439. void NPP_Print(NPP instance, NPPrint* printInfo)
  440. {
  441. PluginInstance* This;
  442. if (instance == NULL) return;
  443. This = (PluginInstance*) instance->pdata;
  444. if(printInfo == NULL) {
  445. /* Though it's not documented, if a Netscape user tries to print a full-page
  446.  * plugin, the browser will call NPP_Print with printInfo set to NULL.
  447.  * At this point you should print your plug-in in whatever manner you
  448.  * choose. */
  449. /* PrintFullPage();  */
  450. return;
  451. }
  452. if (printInfo->mode == NP_FULL) {
  453. /* the plugin is full-page, and the browser is giving it a chance
  454.  * to print in the manner of its choosing */
  455. void* platformPrint = printInfo->print.fullPrint.platformPrint;
  456. NPBool printOne =  printInfo->print.fullPrint.printOne;
  457. /* Setting this to FALSE and returning *should* cause the browser to
  458.  * call NPP_Print again, this time with mode=NP_EMBED.
  459.  * However, that doesn't happen with any browser I've ever seen :-(.
  460.  * Instead of the following line, you will probably need to implement
  461.  * printing yourself.  You also might as well set pluginPrinted to TRUE,
  462.  * though every browser I've tested ignores it. */
  463. printInfo->print.fullPrint.pluginPrinted = FALSE;
  464. /*  or */
  465. /*  PrintFullPage();
  466.  *  printInfo->print.fullPrint.pluginPrinted = TRUE;  */
  467. }
  468. else { /* If not fullscreen, we must be embedded */
  469. HDC pdc;
  470. int prevstretchmode;
  471. NPWindow* printWindow;
  472. if(This->status != ST_LOADED) return;
  473. printWindow= &(printInfo->print.embedPrint.window);
  474. /* embedPrint.platformPrint is a Windows device context in disguise */
  475. /* The definition of NPWindow changed between API verion 0.9 and 0.11,
  476.  * increasing in size from 28 to 32 bytes. This normally makes it
  477.  * impossible for version 0.9 browsers to print version 0.11 plugins
  478.  * (because the platformPrint field ends up at the wrong offset) --
  479.  * unless the plugin takes special care to detect this situation.
  480.  * To work around it, if we are compiled with API 0.11 or higher,
  481.  * and the browser is version 0.9 or earlier, we look for the HDC
  482.  * 4 bytes earlier, at offset 28 instead of 32 (of the embedPrint
  483.  * sub-structure).
  484.  */
  485. if(sizeof(NPWindow)>28 &&     /* i.e. is plugin API >= 0.11? */
  486.      HIBYTE(g_pNavigatorFuncs->version)==0 &&
  487.          LOBYTE(g_pNavigatorFuncs->version)<=9) {
  488. char *tmpc;
  489. HDC  *tmph;
  490. tmpc= (char*)&(printInfo->print.embedPrint);
  491. tmph= (HDC*)&tmpc[28];
  492. pdc=  *tmph;
  493. }
  494. else {
  495. pdc= (HDC) (printInfo->print.embedPrint.platformPrint);
  496. }
  497. if(!This->lpdib) return;
  498. prevstretchmode=SetStretchBltMode(pdc,COLORONCOLOR);
  499. StretchDIBits(pdc,
  500. printWindow->x,printWindow->y,
  501.   printWindow->width,printWindow->height, /* dest coords */
  502. 0,0,This->lpdib->biWidth, This->lpdib->biHeight,   /* source coords */
  503. &((BYTE*)(This->lpdib))[This->fileheader.bfOffBits - FHS],
  504. (LPBITMAPINFO)This->lpdib,DIB_RGB_COLORS,SRCCOPY);
  505. if(prevstretchmode) SetStretchBltMode(pdc,prevstretchmode);
  506. }
  507. }
  508. /*+++++++++++++++++++++++++++++++++++++++++++++++++
  509.  * NPP_URLNotify:
  510.  * Notifies the instance of the completion of a URL request. 
  511.  +++++++++++++++++++++++++++++++++++++++++++++++++*/
  512. void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
  513. {
  514. }
  515. /*+++++++++++++++++++++++++++++++++++++++++++++++++
  516.  * NPP_HandleEvent:
  517.  * With normal windowed plugins, this is used only for Macs.
  518.  * With windowless plugins, it is where everything happens.
  519.  
  520.   If the plug-in handles the event, the function should return true. 
  521.   If the plug-in ignores the event, the function returns false. 
  522.  +++++++++++++++++++++++++++++++++++++++++++++++++*/
  523. int16 NPP_HandleEvent(NPP instance, void* event)
  524. {
  525. NPEvent *ev;
  526. PluginInstance* This;
  527. HWND hwnd;
  528. POINT pt;
  529. if (instance == NULL) return 0;
  530. This = (PluginInstance*) instance->pdata;
  531. if(!This->windowless) return 0;
  532. if(This->fWindow==NULL) return 0;
  533. ev= (NPEvent*)event;
  534. switch( (UINT)ev->event ) {
  535. case WM_PAINT:
  536. WindowlessPaint(This);
  537. return 1;
  538. case WM_CONTEXTMENU: case WM_RBUTTONDOWN:
  539. hwnd=GetHWND(This);
  540. /* ContextMenu(This,LOWORD(ev->lParam),HIWORD(ev->lParam),hwnd); */
  541. GetCursorPos(&pt);
  542. ContextMenu(This,pt.x,pt.y,hwnd);
  543. return 1;
  544. case WM_LBUTTONDBLCLK:  /* Netscape crashes (!) if you return 0 on a double-click */
  545. case WM_LBUTTONDOWN:
  546. return 1;
  547. }
  548. return 0;
  549. }
  550. /**********************************************************************/
  551. /* draw bitmap from DIB 
  552.  * rect: I am abusing the definition of RECT, and using rect->right,bottom as
  553.  *       width,height instead of absolute coordinates (it didn't matter until
  554.  *       I added windowless mode).
  555.  * erase: 1==erase background first
  556.  */
  557. void PaintBitmap(PluginInstance *This, HDC hdc, RECT *rect1, int erase) 
  558. {
  559. int prevstretchmode;
  560. HPEN oldpen;
  561. HBRUSH oldbrush;
  562. RECT rect;
  563. /* make a local copy */
  564. rect.left=rect1->left;    rect.top=rect1->top;
  565. rect.right=rect1->right;  rect.bottom=rect1->bottom;
  566. if(erase) {
  567. oldpen=SelectObject(hdc,GetStockObject(NULL_PEN));
  568. oldbrush=SelectObject(hdc,GetStockObject(WHITE_BRUSH));
  569. Rectangle(hdc,rect1->left,rect1->top,
  570. rect1->left+rect1->right+1,rect1->top+rect1->bottom+1);
  571. if(oldpen) SelectObject(hdc,oldpen);
  572. if(oldbrush) SelectObject(hdc,oldbrush);
  573. }
  574. /* We consider displaying it only if it is completely loaded, or if
  575.  * the complete header has been loaded */
  576. if(!This->lpdib) return;
  577. if(This->status!=ST_LOADED) {
  578. /* Is enough loaded to do progressive display? */
  579. if(This->dibused < sizeof(BITMAPINFOHEADER)) return;
  580. /* Progressive display of compressed images would be difficult to do,
  581.  * so we don't do it.  So there! */
  582. if(This->lpdib->biCompression==BI_RLE4 ||
  583.    This->lpdib->biCompression==BI_RLE8) return;
  584. }
  585. /* Show full-screen images their natural size, unless they don't fit
  586.  * in the browser, in which case scale them down (the Right Thing to do
  587.  * would be to create scroll bars, etc.) */
  588. if(This->fMode==NP_FULL) {
  589. int w,h,x,y;
  590. w=This->lpdib->biWidth;
  591. h=This->lpdib->biHeight;
  592. /* reduce size if needed */
  593. if(w>rect.right) {
  594. h= (int)(((double)h * (double)rect.right)/(double)w);
  595. w=  rect.right;
  596. }
  597. if(h>rect.bottom) {
  598. w= (int)(((double)w * (double)rect.bottom)/(double)h);
  599. h= rect.bottom;
  600. }
  601. /* center the image if there's room */
  602. if(w < rect.right)  x=(rect.right-w)/2;   else x=0;
  603. if(h < rect.bottom) y=(rect.bottom-h)/2;  else y=0;
  604. rect.left=x;
  605. rect.right=w;
  606. rect.top=y;
  607. rect.bottom=h;
  608. }
  609. prevstretchmode=SetStretchBltMode(hdc,COLORONCOLOR);
  610. StretchDIBits(hdc,
  611. rect.left,rect.top,rect.right,rect.bottom, /* dest */
  612. 0,0,This->lpdib->biWidth, This->lpdib->biHeight,      /* source  */
  613. &((BYTE*)(This->lpdib))[This->fileheader.bfOffBits - FHS],
  614. (LPBITMAPINFO)This->lpdib,DIB_RGB_COLORS,SRCCOPY);
  615. if(prevstretchmode) SetStretchBltMode(hdc,prevstretchmode);
  616. }
  617. void CopyToClipboard(PluginInstance *This,unsigned char *mem,int size,UINT format)
  618. {
  619. HGLOBAL hClip;
  620. LPVOID lpClip;
  621. if(!mem) return;
  622. if(!OpenClipboard(NULL)) {
  623. warn(This,"Can't open the clipboard");
  624. return;
  625. }
  626. if(EmptyClipboard()) {
  627. hClip=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE,size);
  628. lpClip=GlobalLock(hClip);
  629. if(lpClip) {
  630. CopyMemory(lpClip,mem,size);
  631. GlobalUnlock(hClip);
  632. if(!SetClipboardData(format,hClip)) {
  633. warn(This,"Can't set clipboard data");
  634. }
  635. }
  636. else {
  637. warn(This,"Can't allocate memory for clipboard");
  638. }
  639. }
  640. else {
  641. warn(This,"Can't clear the clipboard");
  642. }
  643. CloseClipboard();
  644. }
  645. int SaveToBMPFile(PluginInstance *This, char *filename)
  646. {
  647. HANDLE hFile;
  648. DWORD written;
  649. if(!This->lpdib) return 0;
  650. hFile=CreateFile(filename,GENERIC_WRITE,0,NULL,
  651. CREATE_ALWAYS,0,NULL);
  652. if(hFile==INVALID_HANDLE_VALUE) {
  653. warn(This,"Could not save file '%s'",filename);
  654. return 0;
  655. }
  656. /* First write our copy of the file header, then the DIB. 
  657.  * That should result in an exact copy of the original file. */
  658. WriteFile(hFile,(LPVOID)&(This->fileheader),FHS,&written,NULL);
  659. WriteFile(hFile,This->lpdib,This->dibused,&written,NULL);
  660. CloseHandle(hFile);
  661. return 1;
  662. }
  663. /* A vitally important feature ;-) */
  664. void SetAsWallpaper(PluginInstance *This, int tiled)
  665. {
  666. char fn[512];
  667. HKEY key;
  668. LONG rv;
  669. if(!This->lpdib || This->status!=ST_LOADED) {
  670. warn(This,"Image not loaded. Can't set as wallpaper");
  671. return;
  672. }
  673. GetWindowsDirectory(fn,256);
  674. strcat(fn,"\WebBMP Wallpaper.bmp");
  675. if(!SaveToBMPFile(This,fn)) return;
  676. rv=RegOpenKeyEx(HKEY_CURRENT_USER,"Control Panel\desktop",
  677. (DWORD)0,KEY_SET_VALUE,&key);
  678. if(rv==ERROR_SUCCESS) {
  679. RegSetValueEx(key,"Wallpaper",0,REG_SZ,(BYTE*)fn,1+strlen(fn));
  680. RegSetValueEx(key,"TileWallpaper",0,REG_SZ,tiled?"1":"0",2);
  681. RegCloseKey(key);
  682. }
  683. SystemParametersInfo(SPI_SETDESKWALLPAPER,(UINT)0,(PVOID)fn,SPIF_SENDCHANGE);
  684. }
  685. /* Try to make a filename from the url. Caller must provide fn[256] buffer.
  686.  * This function attempts to extract a bitmap filename from a URL,
  687.  * but if it doesn't look like it contains an appropriate name,
  688.  * it leaves it blank.
  689.  * (No matter what Microsoft thinks, URLs are *not* filenames!) */
  690. void url2filename(char *fn, char *url)
  691. {
  692. int title,ext,i;
  693. strcpy(fn,"");
  694. ext=0;   /* position of the file extention */
  695. title=0; /* position of the base filename */
  696. for(i=0;url[i];i++) {
  697. if(url[i]=='.') ext=i+1;
  698. if(url[i]=='/') title=i+1;
  699. if(url[i]==':') title=i+1;
  700. if(url[i]=='=') title=i+1;
  701. }
  702. if(!_stricmp(&url[ext],"bmp")) {
  703. Strncpy(fn,&url[title],256);
  704. }
  705. }
  706. /* Write the image to a local file; see SaveToBMPFile() */
  707. void SaveImage(PluginInstance *This)
  708. {
  709. OPENFILENAME ofn;
  710. char fn[MAX_PATH];
  711. if(This->status != ST_LOADED) {
  712. warn(This,"Image not loaded -- can't save");
  713. return;
  714. }
  715. if(strlen(This->url)) {
  716. url2filename(fn,This->url);
  717. }
  718. else {
  719. strcpy(fn,"");
  720. }
  721. ZeroMemory(&ofn,sizeof(OPENFILENAME));
  722. ofn.lStructSize=sizeof(OPENFILENAME);
  723. ofn.hwndOwner=GetHWND(This);
  724. ofn.lpstrFilter="BMP (*.bmp)*.bmp";
  725. ofn.nFilterIndex=1;
  726. ofn.lpstrTitle="Save Image As...";
  727. ofn.lpstrFile=fn;
  728. ofn.nMaxFile=MAX_PATH;
  729. ofn.Flags=OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT;
  730. ofn.lpstrDefExt="bmp";
  731. if(GetSaveFileName(&ofn)) {
  732. SaveToBMPFile(This,ofn.lpstrFile);
  733. }
  734. }
  735. void AboutDialog(PluginInstance *This)
  736. {
  737. DialogBoxParam(hInst,"ABOUTDLG",GetHWND(This),(DLGPROC)DlgProcAbout,(LPARAM)This);
  738. }
  739. void ContextMenu(PluginInstance *This, int x, int y, HWND hwnd)
  740. {
  741. HMENU sub;
  742. int cmd;
  743. sub=GetSubMenu(hmenuContext,0);
  744. cmd=TrackPopupMenuEx(sub, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_NONOTIFY|TPM_RETURNCMD|
  745. TPM_RIGHTBUTTON,x,y,hwnd,NULL);
  746. switch(cmd) {
  747. case ID_SAVEAS:  SaveImage(This); break;
  748. case ID_COPYIMAGE:
  749. if(This->status==ST_LOADED && This->lpdib) {
  750. CopyToClipboard(This,(unsigned char*)This->lpdib,This->dibused,CF_DIB);
  751. }
  752. else {
  753. warn(This,"No image to copy");
  754. }
  755. break;
  756. case ID_COPYURL:
  757. if(strlen(This->url)) {
  758. CopyToClipboard(This,This->url,strlen(This->url)+1,CF_TEXT);
  759. }
  760. else {
  761. warn(This,"No link to copy");
  762. }
  763. break;
  764. case ID_SETASWALLPAPERC: SetAsWallpaper(This,0); break;
  765. case ID_SETASWALLPAPERT: SetAsWallpaper(This,1); break;
  766. case ID_VIEWIMAGE:
  767. if(strlen(This->url)) 
  768. NPN_GetURL(This->instance,This->url,"_self");
  769. break;
  770. case ID_ABOUT:   AboutDialog(This); break;
  771. }
  772. }
  773. /*+++++++++++++++++++++++++++++++++++++++++++++++++
  774.  * PluginWindowProc
  775.  * Handle the Windows window-event loop.
  776.  +++++++++++++++++++++++++++++++++++++++++++++++++*/
  777. LRESULT CALLBACK PluginWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
  778. {
  779. PluginInstance* This = (PluginInstance*) GetProp(hWnd, gInstanceLookupString);
  780. if(!This) return DefWindowProc( hWnd, Msg, wParam, lParam);
  781. switch(Msg) {
  782. case WM_ERASEBKGND:
  783. /* Disable background-erasing. It seems a little unreliable, so we'll
  784.  * do it ourselves in WM_PAINT */
  785. return 1;
  786. case WM_CONTEXTMENU:
  787. ContextMenu(This,LOWORD(lParam),HIWORD(lParam), hWnd);
  788. return 0;
  789. case WM_PAINT:
  790. {
  791. PAINTSTRUCT paintStruct;
  792. HDC hdc;
  793. RECT rect;
  794. hdc = BeginPaint( hWnd, &paintStruct );
  795. GetClientRect(hWnd,&rect);
  796. PaintBitmap(This, hdc, &rect, 1);
  797. EndPaint( hWnd, &paintStruct );
  798. return 0;
  799. }
  800. }
  801. /* Forward unprocessed messages on to their original destination
  802.  * (the window proc we replaced) */
  803. return This->fDefaultWindowProc(hWnd, Msg, wParam, lParam);
  804. }
  805. char *GetCompressionName(int c)
  806. {
  807. switch(c) {
  808. case BI_RGB:
  809. case BI_BITFIELDS:   return "None";
  810. case BI_RLE4:        return "RLE4";
  811. case BI_RLE8:        return "RLE8";
  812. }
  813. return "Unknown";
  814. }
  815. LRESULT CALLBACK DlgProcAbout(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
  816. {
  817. switch(Msg) {
  818. case WM_INITDIALOG:
  819. {
  820. char buf[4096];
  821. char info[1024];
  822. DWORD tabs[1];
  823. PluginInstance *This=(PluginInstance*)lParam;
  824. tabs[0]= 60;
  825. SendDlgItemMessage(hWnd,IDC_IMGINFO,EM_SETTABSTOPS,(WPARAM)1,(LPARAM)tabs);
  826. sprintf(buf,"WebBMP Plug-in, Version %srn%s"
  827. #ifdef _DEBUG
  828. " DEBUG BUILD"
  829. #endif
  830. "rnBy Jason Summers"
  831. ,VERS,__DATE__);
  832. SetDlgItemText(hWnd,IDC_PRGINFO,buf);
  833. sprintf(buf,"URL:t%srn",This->url);
  834. if(This->status==ST_LOADED) {
  835. sprintf(info,"Size:t%d bytesrn",(int)(This->dibused+FHS));
  836. strcat(buf,info);
  837. #ifdef _DEBUG
  838. sprintf(info,"Reported size:t%d bytesrn",(int)This->fileheader.bfSize);
  839. strcat(buf,info);
  840. sprintf(info,"Memory alloc:t%d bytesrn",(int)(This->diballoc+FHS));
  841. strcat(buf,info);
  842. #endif
  843. sprintf(info,"Dimensions:t%d x %d pixelsrn",This->imgwidth,This->imgheight);
  844. strcat(buf,info);
  845. sprintf(info,"Bits/pixel:t%drn",This->imgbpp);
  846. strcat(buf,info);
  847. sprintf(info,"Palette entries:t%drn",This->palettesize);
  848. strcat(buf,info);
  849. sprintf(info,"Compression:t%srn",GetCompressionName((int)This->imgcompr));
  850. strcat(buf,info);
  851. }
  852. sprintf(info,"Browser:t%srn",NPN_UserAgent(This->instance));
  853. strcat(buf,info);
  854. sprintf(info,"Browser API:t%d.%drn",HIBYTE(g_pNavigatorFuncs->version),
  855. LOBYTE(g_pNavigatorFuncs->version));
  856. strcat(buf,info);
  857. sprintf(info,"Plug-in API:t%d.%drn",NP_VERSION_MAJOR,NP_VERSION_MINOR);
  858. strcat(buf,info);
  859. sprintf(info,"Mode:t%srn",This->windowless?" Windowless":" Windowed");
  860. strcat(buf,info);
  861. SetDlgItemText(hWnd,IDC_IMGINFO,buf);
  862. }
  863. return(TRUE);
  864. case WM_CLOSE:
  865. EndDialog(hWnd,0);
  866. return(TRUE);
  867. case WM_COMMAND:
  868. switch(wParam) {
  869. case IDOK:
  870. case IDCANCEL:
  871. EndDialog(hWnd,0);
  872. return(TRUE);
  873. }
  874. }
  875. return(FALSE);
  876. }