program dgtkflasher;
{$mode delphi}{$H+}
uses
cthreads,
Classes, SysUtils, libc, gtk2, gdk2, glib2, math,
MozillaPluginAPI, gdk2x, x;
var
window, button, vbox, socket, fixed: pGtkWidget;
plugin_funcs: TNPPluginFuncs;
mozilla_funcs: TNPNetscapeFuncs;
gNP_GetMIMEDescription: TNPP_GetMIMEDescription;
//gNP_Initialize: TNPP_Initialize;
gNP_Initialize: TNP_InitializeProc;
gNP_Shutdown: TNPP_Shutdown;
gNP_GetValue: TNPP_GetValue;
plugin: TNPP;
type
{ TPCharArgs }
TArrayPtr = array[0..16383] of PChar;
PArrayPtr = ^TArrayPtr;
TPCharArgs = class
private
FCount: Integer;
FData: PArrayPtr;
public
constructor Create;
destructor Destroy;
procedure Add(AValue: string);
property Data: PArrayPtr read FData;
property Count: Integer read FCount;
end;
{ TPCharArgs }
constructor TPCharArgs.Create;
begin
inherited;
GetMem(FData, SizeOf(PChar) * 50);
FCount := 0;
FillChar(FData^, SizeOf(PChar) * 50, 0);
end;
destructor TPCharArgs.Destroy;
var
I: Integer;
begin
for I := 0 to FCount-1 do
Dispose(FData^[I]);
inherited;
end;
procedure TPCharArgs.Add(AValue: string);
begin
GetMem(FData^[FCount], Length(AValue)+1);
StrPLCopy(FData^[FCount], AValue, Length(AValue));
Inc(FCount);
end;
procedure Debug(msg: string; fmt: array of const);
begin
WriteLn(Format(msg, fmt));
end;
{/*==========================================================================*\
* Browser-side functions...
\*==========================================================================*/}
//* Closes and deletes a stream */
function NPN_DestroyStream(instance: PNPP; stream: PNPStream; reason: TNPError): TNPError; cdecl;
begin
Debug('NPN_DestroyStream instance=%p, stream=%p, reason=%d\n',
[instance, stream, reason]);
Result := NPERR_GENERIC_ERROR;
{$IF 0}
CURLStream *s = stream->ndata;
if (!s) {
return NPERR_GENERIC_ERROR;
}
CURLStreamDestroy(s, reason);
return NPERR_NO_ERROR;
{$ENDIF}
end;
///* Forces a repaint message for a windowless plug-in */
procedure NPN_ForceRedraw(instance: PNPP); cdecl;
begin
Debug('NPN_ForceRedraw instance=%p\n', [instance]);
//NOT_IMPLEMENTED();
end;
///* Asks the browser to create a stream for the specified URL */
function NPN_GetURL(instance: PNPP; const url: PChar; const target: PChar): TNPError; cdecl;
begin
Debug('NPN_GetURL instance=%p, url=%s, target=%s\n',
[instance, url, target]);
if (target <> nil) then
begin
//NOT_IMPLEMENTED();
Result := NPERR_INVALID_PARAM;
Exit;
end;
Result := NPERR_GENERIC_ERROR;
{$IF 0}
CURLStream *s = CURLStreamNew(instance, url, False, NULL);
return s ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
{$ENDIF}
end;
///* Requests creation of a new stream for the specified URL */
function NPN_GetURLNotify(instance: PNPP;
const url: Pchar;
const target: PChar;
notifyData: Pointer): TNPError; cdecl;
begin
Debug('NPN_GetURLNotify instance=%p, url=%s, target=%s\n',
[instance, url, target]);
if (target <> nil) then
begin
//NOT_IMPLEMENTED();
Result := NPERR_INVALID_PARAM;
Exit;
end;
Result := NPERR_GENERIC_ERROR;
{$IF 0}
CURLStream *s = CURLStreamNew(instance, url, True, notifyData);
return s ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
{$ENDIF}
end;
///* Allows the plug-in to query the browser for information */
function NPN_GetValue(instance: PNPP; variable: TNPNVariable; value: Pointer): TNPError; cdecl;
begin
Debug('NPN_GetValue instance=%p, variable=%d [%08x]\n',
[instance, variable and $ffff, variable]);
case (variable) of
NPNVSupportsXEmbedBool:
PNPBool(value)^ := true;
{NPNVxDisplay:
Pointer(Pointer(value)^) := x_display;
//*(void **)value = x_display;
NPNVxtAppContext:
Pointer(Pointer(value)^) := XtDisplayToApplicationContext(x_display);}
NPNVToolkit:
PNPNToolkitType(value)^ := NPNVGtk2;
else
begin
Debug('Unhandled variable %d for NPN_GetValue\n',
[variable]);
Result := NPERR_INVALID_PARAM;
Exit;
end;
end;
Result := NPERR_NO_ERROR;
end;
{/*
* Invalidates specified drawing area prior to repainting or refreshing a
* windowless plug-in
*/}
procedure NPN_InvalidateRect(instance: PNPP; invalidRect: PNPRect); cdecl;
begin
Debug('NPN_InvalidateRect top=%d, left=%d, bottom=%d, right=%d\n',
[invalidRect^.top, invalidRect^.left, invalidRect^.bottom,
invalidRect^.right]);
//NOT_IMPLEMENTED();
end;
{/*
* Invalidates specified region prior to repainting or refreshing a
* windowless plug-in.
*/}
procedure NPN_InvalidateRegion(instance: PNPP; invalidRegion: Pointer {TNPRegion}); cdecl;
{var
rect: TXRectangle;}
begin
//XClipBox(invalidRegion, @rect);
Debug('NPN_InvalidateRegion', []);
{Debug('NPN_InvalidateRegion x=%d, y=%d, width=%d, height=%d\n',
[rect.x, rect.y, rect.width, rect.height]);}
//NOT_IMPLEMENTED();
end;
//* Allocates memory from the browser's memory space. */
function NPN_MemAlloc(size: LongWord): Pointer; cdecl;
begin
Debug('NPN_MemAlloc size=%d\n', [size]);
Result := GetMem(size);
end;
//* Requests that the browser free a specified amount of memory. */
function NPN_MemFlush(size: LongWord): LongWord; cdecl;
begin
Debug('NPN_MemFlush size=%d\n', [size]);
Result := 0;
end;
///* Deallocates a block of allocated memory. */
procedure NPN_MemFree(ptr: Pointer); cdecl;
begin
Debug('NPN_MemFree ptr=%p\n', [ptr]);
FreeMem(ptr);
end;
{/*
* Requests the creation of a new data stream produced by the plug-in and
* consumed by the browser.
*/}
type PPNPStream = ^PNPStream;
function NPN_NewStream(instance: PNPP;
_type: TNPMIMEType;
const target: PChar;
stream: PPNPStream): TNPError; cdecl;
begin
Debug('NPN_NewStream instance=%p, type=%s, target=%s\n',
[instance, _type, target]);
//NOT_IMPLEMENTED();
Result := NPERR_GENERIC_ERROR;
end;
///* Posts data to a URL. */
function NPN_PostURL(instance: PNPP;
const url: PChar;
const target: PChar;
len: LongWord;
const buf: PChar;
_file: TNPBool): TNPError; cdecl;
begin
Debug('NPN_PostURL instance=%p, url=%s, target=%s, len=%d, file=%d\n',
[instance, url, target, len,_file]);
if (target <> NULL) then
begin
//NOT_IMPLEMENTED();
Result := NPERR_INVALID_PARAM;
Exit;
end;
Result := NPERR_GENERIC_ERROR;
{$IF 0}
CURLStream *s = CURLStreamNewPost(instance, url, False, NULL, buf,
len, file);
return s ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
{$ENDIF}
end;
///* Posts data to a URL, and receives notification of the result. */
function NPN_PostURLNotify(instance: PNPP;
const url: PChar;
const target: PChar;
len: LongWord;
const buf: PChar;
_file: TNPBool;
notifyData: Pointer): TNPError; cdecl;
begin
Debug('NPN_PostURLNotify instance=%p, url=%s, target=%s, len=%d, file=%d\n',
[instance, url, target, len, _file]);
if (target <> NULL) then
begin
//NOT_IMPLEMENTED();
Result := NPERR_INVALID_PARAM;
Exit;
end;
Result := NPERR_GENERIC_ERROR;
{$IF 0}
CURLStream *s = CURLStreamNewPost(instance, url, True, notifyData, buf,
len, file);
return s ? NPERR_NO_ERROR : NPERR_GENERIC_ERROR;
{$ENDIF}
end;
///* Supposed to flush all plugins and reload. */
procedure NPN_ReloadPlugins(reloadPages: TNPBool); cdecl;
begin
Debug('NPN_ReloadPlugins reloadPages=%d\n', [reloadPages]);
//NOT_IMPLEMENTED();
end;
///* Returns the Java execution environment. */
function NPN_GetJavaEnv(): PJRIEnv; cdecl;
begin
Debug('NPN_GetJavaEnv\n', []);
Result := nil;
end;
//* Returns the Java object associated with the plug-in instance. */
function NPN_GetJavaPeer(instance: PNPP): Tjref; cdecl;
begin
Debug('NPN_GetJavaPeer instance=%p\n', [instance]);
Result := nil;
end;
///* Requests a range of bytes for a seekable stream. */
function NPN_RequestRead(stream: PNPStream; rangeList: PNPByteRange): TNPError; cdecl;
begin
Debug('NPN_RequestRead stream=%p\n', [stream]);
//NOT_IMPLEMENTED();
Result := NPERR_GENERIC_ERROR;
end;
//* Sets various modes of plug-in operation. */
function NPN_SetValue(instance: PNPP; variable: TNPPVariable; value: Pointer): TNPError; cdecl;
begin
Debug('NPN_SetValue instance=%p, variable=%d\n', [instance, variable]);
//NOT_IMPLEMENTED();
Result := NPERR_GENERIC_ERROR;
end;
//* Displays a message on the status line of the browser window. */
procedure NPN_Status(instance: PNPP; const message: PChar); cdecl;
begin
Debug('NPN_Status instance=%p, message=%s\n', [instance, message]);
//NOT_IMPLEMENTED();
end;
///* Returns the browser's user agent field. */
function NPN_UserAgent(instance: PNPP): PChar; cdecl;
begin
Result := 'Mozilla/5.0 (X11; U; Linux i686; en-US) Flasher/0.2';
end;
{/*
* Pushes data into a stream produced by the plug-in and consumed by the
* browser.
*/}
function NPN_Write(instance: PNPP; stream: PNPStream; len: LongWord; buf: Pointer): LongWord; cdecl;
begin
Debug('NPN_Write instance=%p, stream=%p, len=%d\n',
[instance, stream, len]);
//NOT_IMPLEMENTED();
Result := -1;
end;
{/*==========================================================================*\
* Plugin instance creation, window creation, file reading...
\*==========================================================================*/}
///* Create a new plugin instance, passing some very basic arguments */
function CallNew(plugin: PNPP; swf_file: string; width, height: Integer): TNPError;
var
width_s, height_s: string;
//args, values: array[0..4] of string;
args, values: TPCharArgs;
xembed: TNPBool;
err: TNPError;
begin
///* FIXME: Store window state. */
plugin.ndata := nil;
width_s := IntToStr(width);
height_s := IntToStr(height);
args := TPCharArgs.Create;
values := TPCharArgs.Create;
args.Add('SRC');
args.Add('WIDTH');
args.Add('HEIGHT');
args.Add('MENU');
args.Add('LOOP');
values.Add(swf_file);
values.Add(width_s);
values.Add(height_s);
values.Add('FALSE');
values.Add('TRUE');
//SetLength(args, 5);
//SetLength(values, 5);
{args[0] := 'SRC';
args[1] := 'WIDTH';
args[2] := 'HEIGHT';
args[3] := 'MENU';
args[4] := 'LOOP';
values[0] := swf_file;
values[1] := width_s;
values[2] := height_s;
values[3] := 'FALSE';
values[4] := 'TRUE';}
Result := plugin_funcs.New('application/x-shockwave-flash', plugin,
Word(NP_EMBED), args.Count, PArrayPchar(args.Data), PArrayPchar(values.Data), nil);
args.Free;
values.Free;
{xembed := true;
err := plugin_funcs.GetValue(plugin, NPPVpluginNeedsXEmbed, @xembed);
if err <> NPERR_NO_ERROR then
writeln('ouch');}
end;
//* Create a new Xt window and pass it to the plugin. */
function CallSetWindow(plugin: PNPP; width, height: Integer): TNPError;
var
ws_info: TNPSetWindowCallbackStruct;
win: TNPWindow;
draw: PGdkDrawable;
window: TWindow;
{screen: Integer;
attr: TXSetWindowAttributes;
mask: LongWord;
x_root_win: TWindow;
x_win: TWindow;
top_widget: PWidget;
args: array[0..6] of TArg;
n: Integer;
form: PWidget;}
begin
draw := PGdkDrawable(socket^.window);
//draw := PGdkDrawable(fixed^.window);
//draw := gtk_widget_get_parent_window(socket);
window := GDK_WINDOW_XWINDOW(draw);
FillChar(ws_info, sizeof(ws_info), 0);
FillChar(win, sizeof(win), 0);
ws_info._type := NP_SETWINDOW;
ws_info.display := GDK_DRAWABLE_XDISPLAY (draw);
ws_info.visual := GDK_VISUAL_XVISUAL (gdk_drawable_get_visual(draw));
ws_info.colormap := GDK_COLORMAP_XCOLORMAP (gdk_drawable_get_colormap(draw));
ws_info.depth := gdk_drawable_get_depth (draw);
win._type := NPWindowTypeDrawable;
win.x := 0;
win.y := 0;
win.width := width;
win.height := height;
win.ws_info := @ws_info;
//win.window := gtk_socket_get_id(GTK_SOCKET(socket));
win.window := window;
//gtk_widget_realize(window);
//win.window := GDK_WINDOW_XWINDOW(PGdkDrawable(window^.window));
plugin_funcs.setwindow(plugin, @win);
end;
function SearchFileSize(AFilename: string): Integer;
var
S: TSearchRec;
begin
Result := 0;
if FindFirst(AFilename, faAnyFile, S) = 0 then
Result := S.Size;
FindClose(S);
end;
//* Open, read, and write the contents of swf_file to the plugin instance. */
function SendSrcStream(plugin: PNPP; swf_file: string): TNPError;
var
err: TNPError;
stream: TNPStream;
stype: Word;
swf_fd: TFileStream;
write_idx: Integer;
data: array[0..1024*10] of char;
write_max, bytes_read, bytes_written: Integer;
pfn: PChar;
reason: TNPError;
begin
err := NPERR_NO_ERROR;
FillChar(stream, sizeof(stream), 0);
stype := 0;
if not FileExists(swf_file) then
begin
Result := NPERR_FILE_NOT_FOUND;
Exit;
end;
stream.url := PChar(swf_file);
stream._end := SearchFileSize(swf_file);
stream.lastmodified := FileAge(swf_file);
err := plugin_funcs.newstream(plugin,
'application/x-shockwave-flash', @stream,
True, stype);
if (err <> NPERR_NO_ERROR) then
begin
Result := err;
end;
if ((stype = NP_NORMAL) or (stype = NP_ASFILE)) then
begin
swf_fd := TFileStream.Create(swf_file, fmOpenRead);
write_idx := 0;
while (stream._end > 0) do
begin
write_max :=
plugin_funcs.writeready(plugin, @stream);
{Debug("NPP_WriteReady: write_max = %d, end = %d\n",
write_max, stream.end);}
if (write_max <= 0) then
break;
bytes_read := MIN(sizeof(data), stream._end);
bytes_read := swf_fd.Read(data, bytes_read);
//Debug("fread: bytes_read = %d\n", bytes_read);
bytes_written := plugin_funcs.write(plugin,
@stream, write_idx,
bytes_read,
@data);
{Debug("NPP_Write: offset = %d, end = %d, "
"written = %d\n", write_idx, stream.end,
bytes_read);}
if (bytes_written <= 0) then
break;
Inc(write_idx, bytes_written);
Dec(stream._end, bytes_written);
end;
swf_fd.Free;
end;
if ((stype = NP_ASFILE) or (stype = NP_ASFILEONLY)) then
begin
if stream._end = 0 then
pfn := PChar(swf_file)
else
pfn := nil;
plugin_funcs.asfile(plugin, @stream, pfn);
end;
if (stype <> NP_SEEK) then
begin
if stream._end = 0 then
reason := NPRES_DONE
else
reason := NPRES_NETWORK_ERR;
err := plugin_funcs.destroystream(plugin, @stream, reason);
end;
Result := err;
end;
{/*==========================================================================*\
* Play utility, cmdline parsing, main...
\*==========================================================================*/}
procedure LoadFlashPlugin();
var
user_plugin_path: string;
plugin_path: string;
dlobj: Pointer;
begin
//user_plugin_path := '/home/rreale/prog/MozillaPluginPanel/Plugins/flash7/';
user_plugin_path := '/usr/lib/firefox/plugins/';
plugin_path := user_plugin_path + 'libflashplayer.so';
//user_plugin_path := '/home/rreale/.mozilla/plugins/';
//plugin_path := user_plugin_path + 'libdiamondx.so';
dlobj := dlopen(PChar(plugin_path), RTLD_LAZY);
if dlobj = nil then
raise Exception.Create('Unable to load flash plugin');
gNP_GetMIMEDescription := dlsym(dlobj, 'NP_GetMIMEDescription');
gNP_Initialize := dlsym(dlobj, 'NP_Initialize');
gNP_Shutdown := dlsym(dlobj, 'NP_Shutdown');
gNP_GetValue := dlsym(dlobj, 'NP_GetValue');
end;
FUNCTION delete_event( widget : pGtkWidget ; event : pGdkEvent ; data : gpointer ) :
gint; cdecl;
BEGIN
{ Returning FALSE from the function, as we do here, causes the --destroy-- signal to be emitted. }
delete_event := 0;
END;
PROCEDURE destroy( widget : pGtkWidget ; data : gpointer ); cdecl;
BEGIN
gtk_main_quit();
END;
procedure window_size_allocate(widget: PGtkWidget;
allocation: PGtkAllocation;
user_data: gpointer); cdecl;
begin
{gtk_widget_set_size_request(fixed,
allocation.width, allocation.height);}
{gtk_widget_set_size_request(socket,
allocation.width, allocation.height);}
end;
procedure socket_size_request(widget: PGtkWidget;
allocation: PGtkRequisition;
user_data: gpointer); cdecl;
begin
//gtk_widget_get_size_request();
allocation.width := 900;
allocation.height := 600;
{gtk_widget_set_size_request(fixed,
allocation.width, allocation.height);
gtk_widget_set_size_request(socket,
allocation.width, allocation.height);}
end;
procedure socket_size_allocate(widget: PGtkWidget;
allocation: PGtkAllocation;
user_data: gpointer); cdecl;
begin
if (socket <> nil) and
(GTK_WIDGET_REALIZED(socket)) then
CallSetWindow(@plugin, allocation.width, allocation.height);
{gtk_widget_set_size_request(fixed,
allocation.width, allocation.height);}
{gtk_widget_set_size_request(socket,
allocation.width, allocation.height);}
end;
procedure InitializeGtk(width, height: Integer);
begin
window := gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
gtk_widget_set_size_request(window, width, height);
gtk_signal_connect(GTK_OBJECT(window), 'delete_event',
GTK_SIGNAL_FUNC(@delete_event), NIL);
gtk_signal_connect(GTK_OBJECT(window), 'destroy',
GTK_SIGNAL_FUNC(@destroy), NIL);
gtk_signal_connect(GTK_OBJECT(window), 'size-allocate',
GTK_SIGNAL_FUNC(@window_size_allocate), NIL);
{fixed := gtk_fixed_new;
gtk_fixed_set_has_window(GTK_FIXED(fixed), true);
gtk_widget_set_size_request(fixed, width, height);
gtk_container_add(GTK_CONTAINER(window), fixed);}
//gtk_widget_realize(fixed);
//vbox := gtk_vbox_new(true, 5);
{frame := gtk_frame_new('My Frame');
gtk_box_pack_start(GTK_BOX(vbox), frame, true, false, 0);
gtk_widget_show(frame);}
socket := gtk_socket_new;
//gtk_widget_set_size_request(socket, width, height);
{gtk_signal_connect(GTK_OBJECT(socket), 'size-request',
GTK_SIGNAL_FUNC(@socket_size_request), NIL);}
gtk_signal_connect(GTK_OBJECT(socket), 'size-allocate',
GTK_SIGNAL_FUNC(@socket_size_allocate), NIL);
//gtk_container_add(GTK_CONTAINER(fixed), socket);
//gtk_box_pack_start(GTK_BOX(vbox), socket, true, false, 0);
//gtk_widget_realize(socket);
gtk_container_add(GTK_CONTAINER(window), socket);
gtk_widget_show(socket);
//gtk_container_add(GTK_CONTAINER(fixed), vbox);
//gtk_container_add(GTK_CONTAINER(window), vbox);
//gtk_widget_show(vbox);
//gtk_widget_show(fixed);
gtk_widget_show(window);
end;
procedure InitializeFuncs();
begin
FillChar(mozilla_funcs, sizeof(mozilla_funcs), 0);
mozilla_funcs.Size := sizeof(mozilla_funcs);
mozilla_funcs.Version := (NP_VERSION_MAJOR shl 8) + NP_VERSION_MINOR;
mozilla_funcs.GetUrl := @NPN_GetURL;
mozilla_funcs.PostUrl := @NPN_PostURL;
mozilla_funcs.RequestRead := @NPN_RequestRead;
mozilla_funcs.NewStream := @NPN_NewStream;
mozilla_funcs.Write := @NPN_Write;
mozilla_funcs.DestroyStream := @NPN_DestroyStream;
mozilla_funcs.Status := @NPN_Status;
mozilla_funcs.UserAgent := @NPN_UserAgent;
mozilla_funcs.MemAlloc := @NPN_MemAlloc;
mozilla_funcs.MemFlush := @NPN_MemFlush;
mozilla_funcs.MemFlush := @NPN_MemFree;
mozilla_funcs.ReloadPlugins := @NPN_ReloadPlugins;
mozilla_funcs.GetJavaEnv := @NPN_GetJavaEnv;
mozilla_funcs.GetJavaPeer := @NPN_GetJavaPeer;
mozilla_funcs.GetUrlNotify := @NPN_GetURLNotify;
mozilla_funcs.PostUrlNotify := @NPN_PostURLNotify;
mozilla_funcs.GetValue := @NPN_GetValue;
mozilla_funcs.SetValue := @NPN_SetValue;
mozilla_funcs.InvalidateRect := @NPN_InvalidateRect;
FillChar(plugin_funcs, SizeOf(plugin_funcs), 0);
plugin_funcs.size := SizeOf(plugin_funcs);
end;
procedure Error(msg: string; fmt: array of const);
begin
raise Exception.CreateFmt(msg, fmt);
end;
{/*
* Helper to manage create a plugin instance, new window, then writing the
* file contents to the instance.
*/}
function PlaySwf(plugin: PNPP; swf_file: string; width, height: Integer): TNPError;
var
err: TNPError;
begin
err := NPERR_NO_ERROR;
{/*
* Without this, Flash segfaults attempting to dynamically invoke
* gtk_major_mode. Linking GTK+ means that Flash uses GTK's mainloop
* for IO and timeouts, which we don't want.
*
* FIXME: Is there a better way?
*/}
//putenv('FLASH_GTK_LIBRARY=');
err := gNP_Initialize(@mozilla_funcs, @plugin_funcs);
if (err <> NPERR_NO_ERROR) then
Error('NP_Initialize result = %d', [err]);
err := CallNew(plugin, swf_file, width, height);
if (err <> NPERR_NO_ERROR) then
Error('NPP_NewProc result = %d', [err]);
err := CallSetWindow(plugin, width, height);
if (err <> NPERR_NO_ERROR) then
Error('NPP_SetWindow result = %d', [err]);
//Log("Loading: %s\n", swf_file);
err := SendSrcStream(plugin, swf_file);
if (err <> NPERR_NO_ERROR) then
Error('Writing SWF file, result = %d', [err]);
Result := NPERR_NO_ERROR;
end;
var
geometry: string;
fullscren: Boolean = false;
baseurl, swf_file: string;
width: Integer = 700;
height: Integer = 400;
begin
swf_file := 'test.swf';
// must disable floating point exceptions
SetExceptionMask([exInvalidOp, exDenormalized,
exZeroDivide, exOverflow,
exUnderflow, exPrecision]);
gtk_init(@argc, @argv);
LoadFlashPlugin();
InitializeGtk(width, height);
InitializeFuncs();
PlaySwf(@plugin, swf_file, width, height);
gtk_main();
gNP_Shutdown();
end.