Repository URL to install this package:
|
Version:
3.0.0 ▾
|
{
Free Pascal PTCPas framebuffer library threaded wrapper
Copyright (C) 2010, 2011, 2012, 2013 Nikolay Nikolov (nickysn@users.sourceforge.net)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version
with the following modification:
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,and
to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms
and conditions of the license of that module. An independent module is a
module which is not derived from or based on this library. If you modify
this library, you may extend this exception to your version of the library,
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit ptcwrapper;
{$MODE objfpc}{$H+}
{$IFDEF GO32V2}
{ this unit does not work under go32v2, because DOS does not support threads,
but this hack at least makes it compile, so we don't need to exclude it from
the makefiles for now. }
{$DEFINE NoSyncObjsHack}
{$ENDIF GO32V2}
interface
uses
{$IFDEF unix}cthreads,{$ENDIF}
SysUtils, Classes{$IFNDEF NoSyncObjsHack}, syncobjs{$ENDIF}, ptc, ptceventqueue;
type
{$IFDEF NoSyncObjsHack}
TCriticalSection = class
public
procedure Acquire;
procedure Release;
end;
{$ENDIF NoSyncObjsHack}
TPTCWrapperOpenType = (pwotDefault, pwotFormat, pwotWidthHeightFormat, pwotMode);
TPTCWrapperOpenRequest = record
OpenType: TPTCWrapperOpenType;
Title: string;
SurfaceWidth, SurfaceHeight: Integer;
Width, Height: Integer;
Format: IPTCFormat;
Mode: IPTCMode;
VirtualPages: Integer;
Pages: Integer;
Processed: Boolean;
Success: Boolean;
end;
TPTCWrapperCloseRequest = record
Processed: Boolean;
Success: Boolean;
end;
TPTCWrapperOptionRequest = record
Option: string;
Processed: Boolean;
Result: Boolean;
end;
TPTCWrapperGetModesRequest = record
Processed: Boolean;
Result: TPTCModeList;
end;
TPTCWrapperThread = class(TThread)
private
FConsole: IPTCConsole;
FSurface: array of IPTCSurface;
FPalette: IPTCPalette;
FSurfaceCriticalSection: TCriticalSection;
FNeedsUpdate: Boolean;
FPaletteNeedsUpdate: Boolean;
FCurrentVisualPage: Integer;
FEventQueue: TEventQueue;
FPixels: array of Pointer;
FPaletteData: Pointer;
FOpen: Boolean;
{FError?}
FOpenRequest: TPTCWrapperOpenRequest;
FCloseRequest: TPTCWrapperCloseRequest;
FOptionRequest: TPTCWrapperOptionRequest;
FGetModesRequest: TPTCWrapperGetModesRequest;
protected
procedure Execute; override;
public
constructor Create;
destructor Destroy; override;
procedure Open(const ATitle: string; AVirtualPages: Integer; APages: Integer = 0);
procedure Open(const ATitle: string; AFormat: IPTCFormat; AVirtualPages: Integer; APages: Integer = 0);
procedure Open(const ATitle: string; ASurfaceWidth, ASurfaceHeight, AWidth, AHeight: Integer; AFormat: IPTCFormat; AVirtualPages: Integer; APages: Integer = 0);
procedure Open(const ATitle: string; ASurfaceWidth, ASurfaceHeight: Integer; AMode: IPTCMode; AVirtualPages: Integer; APages: Integer = 0);
procedure Close;
function Option(const AOption: string): Boolean;
function Modes: TPTCModeList;
procedure SetVisualPage(AVisualPage: Integer);
function Lock(AVirtualPage: Integer): Pointer;
procedure Unlock;
function PaletteLock: Pointer;
procedure PaletteUnlock;
function NextEvent(out AEvent: IPTCEvent; AWait: Boolean; const AEventMask: TPTCEventMask): Boolean;
function PeekEvent(AWait: Boolean; const AEventMask: TPTCEventMask): IPTCEvent;
property IsOpen: Boolean read FOpen;
end;
implementation
{$IFDEF NoSyncObjsHack}
procedure TCriticalSection.Acquire;
begin
end;
procedure TCriticalSection.Release;
begin
end;
{$ENDIF NoSyncObjsHack}
constructor TPTCWrapperThread.Create;
begin
FOpen := False;
FNeedsUpdate := False;
FOpenRequest.Processed := True;
FCloseRequest.Processed := True;
FOptionRequest.Processed := True;
FGetModesRequest.Processed := True;
FSurfaceCriticalSection := TCriticalSection.Create;
inherited Create(False);
end;
destructor TPTCWrapperThread.Destroy;
begin
inherited;
end;
procedure TPTCWrapperThread.Execute;
procedure GetEvents;
var
Event: IPTCEvent;
NextEventAvailable: Boolean;
begin
repeat
NextEventAvailable := FConsole.NextEvent(Event, False, PTCAnyEvent);
if NextEventAvailable then
FEventQueue.AddEvent(Event);
until not NextEventAvailable;
end;
procedure ProcessRequests;
var
I: Integer;
begin
if not FOpenRequest.Processed then
begin
for I := Low(FSurface) to High(FSurface) do
FSurface[I] := nil;
with FOpenRequest do
begin
SetLength(FSurface, VirtualPages);
case OpenType of
pwotDefault:
begin
FConsole.Open(Title, Pages);
for I := Low(FSurface) to High(FSurface) do
FSurface[I] := TPTCSurfaceFactory.CreateNew(FConsole.Width, FConsole.Height, FConsole.Format);
end;
pwotFormat:
begin
FConsole.Open(Title, Format, Pages);
for I := Low(FSurface) to High(FSurface) do
FSurface[I] := TPTCSurfaceFactory.CreateNew(FConsole.Width, FConsole.Height, Format);
end;
pwotWidthHeightFormat:
begin
FConsole.Open(Title, Width, Height, Format, Pages);
for I := Low(FSurface) to High(FSurface) do
FSurface[I] := TPTCSurfaceFactory.CreateNew(SurfaceWidth, SurfaceHeight, Format);
end;
pwotMode:
begin
FConsole.Open(Title, Mode, Pages);
for I := Low(FSurface) to High(FSurface) do
FSurface[I] := TPTCSurfaceFactory.CreateNew(SurfaceWidth, SurfaceHeight, Mode.Format);
end;
end;
end;
SetLength(FPixels, Length(FSurface));
for I := Low(FSurface) to High(FSurface) do
begin
FPixels[I] := FSurface[I].Lock;
FSurface[I].Unlock;
end;
FOpen := True;
FOpenRequest.Success := True;
FOpenRequest.Processed := True;
end;
if not FCloseRequest.Processed then
begin
FConsole.Close;
for I := Low(FSurface) to High(FSurface) do
FSurface[I] := nil;
SetLength(FSurface, 0);
SetLength(FPixels, 0);
FOpen := False;
FCloseRequest.Success := True;
FCloseRequest.Processed := True;
end;
if not FOptionRequest.Processed then
begin
FOptionRequest.Result := FConsole.Option(FOptionRequest.Option);
FOptionRequest.Processed := True;
end;
if not FGetModesRequest.Processed then
begin
FGetModesRequest.Result := FConsole.Modes;
FGetModesRequest.Processed := True;
end;
end;
begin
try
FConsole := TPTCConsoleFactory.CreateNew;
FConsole.Option('intercept window close');
FEventQueue := TEventQueue.Create;
FPalette := TPTCPaletteFactory.CreateNew;
FPaletteData := FPalette.Data;
FOpen := False;
while not Terminated do
begin
ThreadSwitch;
Sleep(10);
FSurfaceCriticalSection.Acquire;
try
ProcessRequests;
if FOpen then
begin
GetEvents;
if FNeedsUpdate or FPaletteNeedsUpdate then
begin
if FPaletteNeedsUpdate then
FSurface[FCurrentVisualPage].Palette(FPalette);
FSurface[FCurrentVisualPage].Copy(FConsole);
if FPaletteNeedsUpdate then
FConsole.Palette(FPalette);
FConsole.Update;
FNeedsUpdate := False;
FPaletteNeedsUpdate := False;
end;
end;
finally
FSurfaceCriticalSection.Release;
end;
end;
finally
FOpen := False;
FreeAndNil(FEventQueue);
if Assigned(FConsole) then
FConsole.Close;
SetLength(FSurface, 0);
FConsole := nil;
end;
end;
procedure TPTCWrapperThread.Open(const ATitle: string; AVirtualPages: Integer; APages: Integer = 0);
begin
FSurfaceCriticalSection.Acquire;
try
with FOpenRequest do
begin
OpenType := pwotDefault;
Title := ATitle;
VirtualPages := AVirtualPages;
Pages := APages;
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FOpenRequest.Processed;
end;
procedure TPTCWrapperThread.Open(const ATitle: string; AFormat: IPTCFormat; AVirtualPages: Integer; APages: Integer = 0);
begin
FSurfaceCriticalSection.Acquire;
try
with FOpenRequest do
begin
OpenType := pwotFormat;
Title := ATitle;
Format := AFormat;
VirtualPages := AVirtualPages;
Pages := APages;
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FOpenRequest.Processed;
end;
procedure TPTCWrapperThread.Open(const ATitle: string; ASurfaceWidth, ASurfaceHeight, AWidth, AHeight: Integer; AFormat: IPTCFormat; AVirtualPages: Integer; APages: Integer = 0);
begin
FSurfaceCriticalSection.Acquire;
try
with FOpenRequest do
begin
OpenType := pwotWidthHeightFormat;
Title := ATitle;
SurfaceWidth := ASurfaceWidth;
SurfaceHeight := ASurfaceHeight;
Width := AWidth;
Height := AHeight;
Format := AFormat;
VirtualPages := AVirtualPages;
Pages := APages;
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FOpenRequest.Processed;
end;
procedure TPTCWrapperThread.Open(const ATitle: string; ASurfaceWidth, ASurfaceHeight: Integer; AMode: IPTCMode; AVirtualPages: Integer; APages: Integer = 0);
begin
FSurfaceCriticalSection.Acquire;
try
with FOpenRequest do
begin
OpenType := pwotMode;
Title := ATitle;
SurfaceWidth := ASurfaceWidth;
SurfaceHeight := ASurfaceHeight;
Mode := AMode;
VirtualPages := AVirtualPages;
Pages := APages;
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FOpenRequest.Processed;
end;
procedure TPTCWrapperThread.Close;
begin
FSurfaceCriticalSection.Acquire;
try
with FCloseRequest do
begin
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FCloseRequest.Processed;
end;
function TPTCWrapperThread.Option(const AOption: string): Boolean;
begin
FSurfaceCriticalSection.Acquire;
try
with FOptionRequest do
begin
Option := AOption;
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FOptionRequest.Processed;
Result := FOptionRequest.Result;
end;
function TPTCWrapperThread.Modes: TPTCModeList;
begin
FSurfaceCriticalSection.Acquire;
try
with FGetModesRequest do
begin
Processed := False;
end;
finally
FSurfaceCriticalSection.Release;
end;
repeat
ThreadSwitch;
until FGetModesRequest.Processed;
Result := FGetModesRequest.Result;
end;
procedure TPTCWrapperThread.SetVisualPage(AVisualPage: Integer);
begin
FSurfaceCriticalSection.Acquire;
try
if FCurrentVisualPage <> AVisualPage then
begin
FCurrentVisualPage := AVisualPage;
FNeedsUpdate := True;
FPaletteNeedsUpdate := True; { todo: no need to set this always }
end;
finally
FSurfaceCriticalSection.Release;
end;
end;
function TPTCWrapperThread.Lock(AVirtualPage: Integer): Pointer;
begin
FSurfaceCriticalSection.Acquire;
if AVirtualPage = FCurrentVisualPage then
FNeedsUpdate := True;
Result := FPixels[AVirtualPage];
end;
procedure TPTCWrapperThread.Unlock;
begin
FSurfaceCriticalSection.Release;
end;
function TPTCWrapperThread.PaletteLock: Pointer;
begin
FSurfaceCriticalSection.Acquire;
FPaletteNeedsUpdate := True;
Result := FPaletteData;
end;
procedure TPTCWrapperThread.PaletteUnlock;
begin
FSurfaceCriticalSection.Release;
end;
function TPTCWrapperThread.NextEvent(out AEvent: IPTCEvent; AWait: Boolean; const AEventMask: TPTCEventMask): Boolean;
begin
repeat
ThreadSwitch;
FSurfaceCriticalSection.Acquire;
AEvent := FEventQueue.NextEvent(AEventMask);
FSurfaceCriticalSection.Release;
until (not AWait) or (AEvent <> nil);
Result := AEvent <> nil;
end;
function TPTCWrapperThread.PeekEvent(AWait: Boolean; const AEventMask: TPTCEventMask): IPTCEvent;
begin
repeat
ThreadSwitch;
FSurfaceCriticalSection.Acquire;
Result := FEventQueue.PeekEvent(AEventMask);
FSurfaceCriticalSection.Release;
until (not AWait) or (Result <> nil);
end;
end.