1
// plugin.cpp: Windows "win32" flash player Mozilla plugin, for Gnash.
3
// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
5
// This program is free software; you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation; either version 3 of the License, or
8
// (at your option) any later version.
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
// GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "gnashconfig.h"
26
#define FLASH_MAJOR_VERSION "9"
27
#define FLASH_MINOR_VERSION "0"
28
#define FLASH_REV_NUMBER "82"
30
#define MIME_TYPES_HANDLED "application/x-shockwave-flash"
31
// The name must be this value to get flash movies that check the
32
// plugin version to load.
33
#define PLUGIN_NAME "Shockwave Flash"
34
#define MIME_TYPES_DESCRIPTION MIME_TYPES_HANDLED":swf:"PLUGIN_NAME
36
// Some javascript plugin detectors use the description
37
// to decide the flash version to display. They expect the
38
// form (major version).(minor version) r(revision).
40
#ifndef FLASH_MAJOR_VERSION
41
#define FLASH_MAJOR_VERSION DEFAULT_FLASH_MAJOR_VERSION
43
#ifndef FLASH_MINOR_VERSION
44
#define FLASH_MINOR_VERSION DEFAULT_FLASH_MINOR_VERSION
46
#ifndef FLASH_REV_NUMBER
47
#define FLASH_REV_NUMBER DEFAULT_FLASH_REV_NUMBER
49
#define FLASH_VERSION FLASH_MAJOR_VERSION"."\
50
FLASH_MINOR_VERSION" r"FLASH_REV_NUMBER"."
52
#define PLUGIN_DESCRIPTION \
53
"Shockwave Flash "FLASH_VERSION" Gnash "VERSION", the GNU SWF Player. \
54
Copyright © 2006, 2007, 2008 \
55
<a href=\"http://www.fsf.org\">Free Software Foundation</a>, Inc.<br> \
56
Gnash comes with NO WARRANTY, to the extent permitted by law. \
57
You may redistribute copies of Gnash under the terms of the \
58
<a href=\"http://www.gnu.org/licenses/gpl.html\">GNU General Public \
59
License</a>. For more information about Gnash, see <a \
60
href=\"http://www.gnu.org/software/gnash/\"> \
61
http://www.gnu.org/software/gnash</a>. \
62
Compatible Shockwave Flash "FLASH_VERSION
64
#define _WIN32_WINNT 0x0500
70
#include <boost/cstdint.hpp>
75
static int module_initialized = FALSE;
76
static PRLock* playerLock = NULL;
77
static int instances = 0;
79
char* NPP_GetMIMEDescription(void);
80
static void playerThread(void *arg);
81
static LRESULT CALLBACK PluginWinProc(HWND, UINT, WPARAM, LPARAM);
83
#define DBG(x, ...) __DBG(x, ## __VA_ARGS__)
85
__DBG(const char *fmt, ...)
91
vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
93
OutputDebugString(buf);
96
// general initialization and shutdown
99
NS_PluginInitialize(void)
101
DBG("NS_PluginInitialize\n");
103
playerLock = PR_NewLock();
105
module_initialized = TRUE;
106
return NPERR_NO_ERROR;
110
NS_PluginShutdown(void)
112
DBG("NS_PluginShutdown\n");
113
if (!module_initialized) return;
115
PR_DestroyLock(playerLock);
120
/// \brief Return the MIME Type description for this plugin.
122
NPP_GetMIMEDescription(void)
124
if (!module_initialized) return NULL;
125
return MIME_TYPES_HANDLED;
129
/// \brief Retrieve values from the plugin for the Browser
131
/// This C++ function is called by the browser to get certain
132
/// information is needs from the plugin. This information is the
133
/// plugin name, a description, etc...
135
/// This is actually not used on Win32 (XP_WIN), only on Unix (XP_UNIX).
137
NS_PluginGetValue(NPPVariable aVariable, void *aValue)
139
NPError err = NPERR_NO_ERROR;
141
if (!module_initialized) return NPERR_NO_ERROR;
142
DBG("aVariable = %d\n", aVariable);
144
case NPPVpluginNameString:
145
*static_cast<char **> (aValue) = PLUGIN_NAME;
148
// This becomes the description field you see below the opening
149
// text when you type about:plugins and in
150
// navigator.plugins["Shockwave Flash"].description, used in
151
// many flash version detection scripts.
152
case NPPVpluginDescriptionString:
153
*static_cast<const char **>(aValue) = PLUGIN_DESCRIPTION;
156
case NPPVpluginNeedsXEmbed:
158
*static_cast<PRBool *>(aValue) = PR_TRUE;
160
*static_cast<PRBool *>(aValue) = PR_FALSE;
164
case NPPVpluginTimerInterval:
166
case NPPVpluginKeepLibraryInMemory:
169
err = NPERR_INVALID_PARAM;
176
// construction and destruction of our plugin instance object
178
nsPluginInstanceBase*
179
NS_NewPluginInstance(nsPluginCreateData* aCreateDataStruct)
181
DBG("NS_NewPluginInstance\n");
182
if (!module_initialized) return NULL;
186
instances++; // N.B. This is a threading race condition. FIXME.
188
playerLock = PR_NewLock();
190
if (!aCreateDataStruct) {
193
return new nsPluginInstance(aCreateDataStruct);
197
NS_DestroyPluginInstance(nsPluginInstanceBase* aPlugin)
199
DBG("NS_DestroyPluginInstance\n");
200
if (!module_initialized) return;
202
delete (nsPluginInstance *) aPlugin;
205
PR_DestroyLock(playerLock);
211
// nsPluginInstance class implementation
213
/// \brief Constructor
214
nsPluginInstance::nsPluginInstance(nsPluginCreateData* data) :
215
nsPluginInstanceBase(),
216
_instance(data->instance),
234
DBG("nsPluginInstance::nsPluginInstance\n");
237
/// \brief Destructor
238
nsPluginInstance::~nsPluginInstance()
240
DBG("nsPluginInstance::~nsPluginInstance\n");
242
// Deleting _bmp should free this memory.
246
DeleteObject(_hMemDC);
256
nsPluginInstance::init(NPWindow* aWindow)
258
DBG("nsPluginInstance::init\n");
261
DBG("aWindow == NULL\n");
267
_width = aWindow->width;
268
_height = aWindow->height;
269
_window = (HWND) aWindow->window;
270
// Windows DIB row stride is always a multiple of 4 bytes.
271
_rowstride = /* 24 bits */ 3 * _width;
272
_rowstride += _rowstride % 4;
274
memset(&_bmpInfo, 0, sizeof(BITMAPINFOHEADER));
275
_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
276
_bmpInfo.bmiHeader.biWidth = _width;
277
// Negative height means first row comes first in memory.
278
_bmpInfo.bmiHeader.biHeight = -1 * _height;
279
_bmpInfo.bmiHeader.biPlanes = 1;
280
_bmpInfo.bmiHeader.biBitCount = 24;
281
_bmpInfo.bmiHeader.biCompression = BI_RGB;
282
_bmpInfo.bmiHeader.biSizeImage = 0;
283
_bmpInfo.bmiHeader.biXPelsPerMeter = 0;
284
_bmpInfo.bmiHeader.biYPelsPerMeter = 0;
285
_bmpInfo.bmiHeader.biClrUsed = 0;
286
_bmpInfo.bmiHeader.biClrImportant = 0;
288
HDC hDC = GetDC(_window);
289
_hMemDC = CreateCompatibleDC(hDC);
290
_bmp = CreateDIBSection(_hMemDC, &_bmpInfo,
291
DIB_RGB_COLORS, (void **) &_memaddr, 0, 0);
292
SelectObject(_hMemDC, _bmp);
294
DBG("aWindow->type: %s (%u)\n",
295
(aWindow->type == NPWindowTypeWindow) ? "NPWindowTypeWindow" :
296
(aWindow->type == NPWindowTypeDrawable) ? "NPWindowTypeDrawable" :
300
// subclass window so we can intercept window messages and
301
// do our drawing to it
302
_oldWndProc = SubclassWindow(_window, (WNDPROC) PluginWinProc);
304
// associate window with our nsPluginInstance object so we can access
305
// it in the window procedure
306
SetWindowLong(_window, GWL_USERDATA, (LONG) this);
313
nsPluginInstance::shut(void)
315
DBG("nsPluginInstance::shut\n");
317
DBG("Acquiring playerLock mutex for shutdown.\n");
320
DBG("Releasing playerLock mutex for shutdown.\n");
321
PR_Unlock(playerLock);
324
DBG("Waiting for thread to terminate.\n");
325
PR_JoinThread(_thread);
330
SubclassWindow(_window, _oldWndProc);
332
_initialized = FALSE;
336
nsPluginInstance::NewStream(NPMIMEType type, NPStream *stream,
337
NPBool seekable, boost::uint16_t *stype)
339
DBG("nsPluginInstance::NewStream\n");
340
DBG("stream->url: %s\n", stream->url);
352
return NPERR_NO_ERROR;
356
nsPluginInstance::DestroyStream(NPStream *stream, NPError reason)
358
DBG("nsPluginInstance::DestroyStream\n");
359
DBG("stream->url: %s\n", stream->url);
361
// N.B. We can only support one Gnash VM/thread right now.
363
_thread = PR_CreateThread(PR_USER_THREAD, playerThread, this,
364
PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
367
return NPERR_NO_ERROR;
371
nsPluginInstance::Write(NPStream *stream, int32 offset, int32 len,
374
DBG("nsPluginInstance::Write\n");
375
DBG("stream->url: %s, offset: %ld, len: %ld\n",
376
stream->url, offset, len);
380
playerThread(void *arg)
382
nsPluginInstance *plugin = (nsPluginInstance *) arg;
384
plugin->threadMain();
388
nsPluginInstance::threadMain(void)
390
DBG("nsPluginInstance::threadMain started\n");
391
DBG("URL: %s\n", _url.c_str());
395
// Initialize Gnash core library.
397
DBG("Gnash core initialized.\n");
400
gnash::RcInitFile& rcinit = gnash::RcInitFile::getDefaultInstance();
401
std::string logfilename = std::string(std::getenv("TEMP")) +
402
std::string("\\npgnash.log");
403
rcinit.setDebugLog(logfilename);
404
gnash::LogFile& dbglogfile = gnash::LogFile::getDefaultInstance();
405
dbglogfile.setWriteDisk(true);
406
dbglogfile.setVerbosity(GNASH_DEBUG_LEVEL);
407
DBG("Gnash logging initialized: %s\n", logfilename.c_str());
410
_sound_handler.reset(gnash::media::create_sound_handler_sdl());
411
gnash::set_sound_handler(_sound_handler.get());
412
DBG("Gnash sound initialized.\n");
415
int old_mouse_x = 0, old_mouse_y = 0, old_mouse_buttons = 0;
417
(gnash::render_handler *) gnash::create_render_handler_agg("BGR24");
418
// _memaddr = (unsigned char *) malloc(getMemSize());
419
static_cast<gnash::render_handler_agg_base *>(_render_handler)->init_buffer(
420
getMemAddr(), getMemSize(), _width, _height, _rowstride);
421
gnash::set_render_handler(_render_handler);
422
DBG("Gnash GUI initialized: %ux%u\n", _width, _height);
424
gnash::URL url(_url);
427
gnash::URL::parse_querystring(url.querystring(), vars);
428
for (VariableMap::iterator i = vars.begin(), ie = vars.end(); i != ie; i++) {
429
_flashVars[i->first] = i->second;
432
gnash::set_base_url(url);
434
gnash::movie_definition* md = NULL;
436
md = gnash::create_library_movie(url, _url.c_str(), false);
437
} catch (const gnash::GnashException& err) {
442
* N.B. Can't use the goto here, as C++ complains about "jump to
443
* label 'done' from here crosses initialization of ..." a bunch
444
* of things. Sigh. So, instead, I duplicate the cleanup code
445
* here. TODO: Remove this duplication.
449
PR_Unlock(playerLock);
451
DBG("Clean up Gnash.\n");
454
DBG("nsPluginInstance::threadMain exiting\n");
457
DBG("Movie created: %s\n", _url.c_str());
459
int movie_width = static_cast<int>(md->get_width_pixels());
460
int movie_height = static_cast<int>(md->get_height_pixels());
461
float movie_fps = md->get_frame_rate();
462
DBG("Movie dimensions: %ux%u (%.2f fps)\n",
463
movie_width, movie_height, movie_fps);
465
gnash::SystemClock clock; // use system clock here...
466
gnash::movie_root& root = gnash::VM::init(*md, clock).getRoot();
467
DBG("Gnash VM initialized.\n");
469
// Register this plugin as listener for FsCommands from the core
471
root.registerFSCommandCallback(FSCommand_callback);
473
// Register a static function to handle ActionScript events such
474
// as Mouse.hide, Stage.align etc.
475
// root.registerEventCallback(&staticEventHandlingFunction);
478
DBG("Movie loaded.\n");
480
std::auto_ptr<gnash::movie_instance> mr(md->create_movie_instance());
481
mr->setVariables(_flashVars);
482
root.setRootMovie(mr.release());
483
root.set_display_viewport(0, 0, _width, _height);
484
root.set_background_alpha(1.0f);
485
gnash::movie_instance* mi = root.getRootMovie();
486
DBG("Movie instance created.\n");
488
ShowWindow(_window, SW_SHOW);
491
// DBG("Inside main thread loop.\n");
494
DBG("Main thread shutting down.\n");
498
size_t cur_frame = mi->get_current_frame();
499
// DBG("Got current frame number: %d.\n", cur_frame);
500
size_t tot_frames = mi->get_frame_count();
501
// DBG("Got total frame count: %d.\n", tot_frames);
503
// DBG("Advancing one frame.\n");
505
// DBG("Going to next frame.\n");
506
root.goto_frame(cur_frame + 1);
507
// DBG("Ensuring frame is loaded.\n");
508
root.get_movie_definition()->ensure_frame_loaded(tot_frames);
509
// DBG("Setting play state to PLAY.\n");
510
root.set_play_state(gnash::sprite_instance::PLAY);
512
if (old_mouse_x != mouse_x || old_mouse_y != mouse_y) {
513
old_mouse_x = mouse_x;
514
old_mouse_y = mouse_y;
515
root.notify_mouse_moved(mouse_x, mouse_y);
517
if (old_mouse_buttons != mouse_buttons) {
518
old_mouse_buttons = mouse_buttons;
520
root.notify_mouse_clicked(mouse_buttons > 0, mask);
526
GetClientRect(_window, &rt);
527
InvalidateRect(_window, &rt, FALSE);
530
InvalidatedRanges ranges;
531
ranges.setSnapFactor(1.3f);
532
ranges.setSingleMode(false);
533
root.add_invalidated_bounds(ranges, false);
534
ranges.growBy(40.0f);
535
ranges.combine_ranges();
537
if (!ranges.isNull()) {
538
InvalidateRect(_window, &rt, FALSE);
544
// DBG("Unlocking playerLock mutex.\n");
545
PR_Unlock(playerLock);
546
// DBG("Sleeping.\n");
547
PR_Sleep(PR_INTERVAL_MIN);
548
// DBG("Acquiring playerLock mutex.\n");
553
PR_Unlock(playerLock);
555
DBG("Clean up Gnash.\n");
558
* N.B. As per server/impl.cpp:clear(), all of Gnash's threads aren't
559
* guaranteed to be terminated by this, yet. Therefore, when Firefox
560
* unloads npgnash.dll after calling NS_PluginShutdown(), and there are
561
* still Gnash threads running, they will try and access memory that was
562
* freed as part of the unloading of npgnash.dll, resulting in a process
568
DBG("nsPluginInstance::threadMain exiting\n");
572
nsPluginInstance::getVersion()
574
return NPN_UserAgent(_instance);
578
nsPluginInstance::FSCommand_callback(gnash::sprite_instance* movie, const std::string& command, const std::string& args)
579
// For handling notification callbacks from ActionScript.
581
gnash::log_debug(_("FSCommand_callback(%p): %s %s"), (void*) movie, command, args);
584
static LRESULT CALLBACK
585
PluginWinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
587
// get our plugin instance object
588
nsPluginInstance *plugin =
589
(nsPluginInstance *) GetWindowLong(hWnd, GWL_USERDATA);
595
if (plugin->getMemAddr() == NULL) {
600
HDC hDC = BeginPaint(hWnd, &ps);
603
GetClientRect(hWnd, &rt);
604
int w = rt.right - rt.left;
605
int h = rt.bottom - rt.top;
607
BitBlt(hDC, rt.left, rt.top, w, h,
608
plugin->getMemDC(), 0, 0, SRCCOPY);
615
int x = GET_X_LPARAM(lParam);
616
int y = GET_Y_LPARAM(lParam);
618
plugin->notify_mouse_state(x, y, -1);
624
int x = GET_X_LPARAM(lParam);
625
int y = GET_Y_LPARAM(lParam);
626
int buttons = (msg == WM_LBUTTONDOWN) ? 1 : 0;
628
plugin->notify_mouse_state(x, y, buttons);
632
// dbglogfile << "msg " << msg << endl;
636
return DefWindowProc(hWnd, msg, wParam, lParam);