|  | 
 
 发表于 2010-7-19 23:21:02
|
显示全部楼层 
| But IMHO, the most convenient and crossplatform way to embed Flash into application written in Pascal is to write a web application. If you want to deploy to the desktop you can use embedded web server (that can be based e.g. on Ararat Synapse examples) and something like Mozilla Prism as a frontend 
 如果你是在浏览器上运行,可以试一下下面的demo:
 
 复制代码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; BEGINgtk_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.
 | 
 |