244 lines
6.7 KiB
ObjectPascal
244 lines
6.7 KiB
ObjectPascal
unit KM_System;
|
|||
|
|
{$I KaM_Remake.inc}
|
||
|
|
interface
|
||
|
|
uses
|
||
|
|
{$IFDEF MSWindows} Windows, {$ENDIF}
|
||
|
|
{$IFDEF Unix} LCLIntf, LCLType, {$ENDIF}
|
||
|
|
KM_ResSprites, KM_ResTypes,
|
||
|
|
KM_Defaults;
|
||
|
|
|
||
|
|
|
||
|
|
type
|
||
|
|
// System related features are encapsulated in this class
|
||
|
|
TKMSystem = class
|
||
|
|
private
|
||
|
|
{$IFNDEF FPC}
|
||
|
|
{$IFDEF MSWindows}
|
||
|
|
fFormMainHandle: HWND;
|
||
|
|
fFlashing: Boolean;
|
||
|
|
{$ENDIF}
|
||
|
|
{$ENDIF}
|
||
|
|
|
||
|
|
function GetCursor: TKMCursorImageType;
|
||
|
|
procedure SetCursor(Value: TKMCursorImageType);
|
||
|
|
public
|
||
|
|
constructor Create(aFormMainHandle: HWND);
|
||
|
|
|
||
|
|
property Cursor: TKMCursorImageType read GetCursor write SetCursor;
|
||
|
|
|
||
|
|
procedure MakeCursors(aSprites: TKMSpritePack);
|
||
|
|
|
||
|
|
procedure FlashingStart;
|
||
|
|
procedure FlashingStop;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
var
|
||
|
|
gSystem: TKMSystem;
|
||
|
|
|
||
|
|
|
||
|
|
implementation
|
||
|
|
uses
|
||
|
|
SysUtils, Math,
|
||
|
|
Forms, Graphics;
|
||
|
|
|
||
|
|
const
|
||
|
|
// Screen.Cursors[0] is used by System default cursor
|
||
|
|
CURSOR_CNT_OFFSET = 1;
|
||
|
|
|
||
|
|
// New custom cursors count
|
||
|
|
CUSTOM_CUR_CNT = 1;
|
||
|
|
|
||
|
|
CUSTOM_CURSORS: array [0..CUSTOM_CUR_CNT-1] of TKMCursorImageType = (kmcAnimatedDirSelector);
|
||
|
|
|
||
|
|
|
||
|
|
{ TKMSystem }
|
||
|
|
constructor TKMSystem.Create(aFormMainHandle: HWND);
|
||
|
|
begin
|
||
|
|
inherited Create;
|
||
|
|
|
||
|
|
{$IFNDEF FPC}
|
||
|
|
{$IFDEF MSWindows}
|
||
|
|
fFormMainHandle := aFormMainHandle;
|
||
|
|
{$ENDIF}
|
||
|
|
{$ENDIF}
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
function TKMSystem.GetCursor: TKMCursorImageType;
|
||
|
|
begin
|
||
|
|
if InRange(Screen.Cursor - CURSOR_CNT_OFFSET, Ord(Low(TKMCursorImageType)), Ord(High(TKMCursorImageType))) then
|
||
|
|
Result := TKMCursorImageType(Screen.Cursor - CURSOR_CNT_OFFSET)
|
||
|
|
else
|
||
|
|
Result := kmcDefault;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMSystem.SetCursor(Value: TKMCursorImageType);
|
||
|
|
begin
|
||
|
|
if SKIP_LOADING_CURSOR then Exit;
|
||
|
|
Screen.Cursor := Ord(Value) + CURSOR_CNT_OFFSET;
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMSystem.MakeCursors(aSprites: TKMSpritePack);
|
||
|
|
const
|
||
|
|
SF = 17; //Full width/height of a scroll cursor
|
||
|
|
SH = 8; //Half width/height of a scroll cursor
|
||
|
|
// Measured manually
|
||
|
|
CURSOR_OFFSET_X: array [TKMCursorImageType] of Integer = (0,0,20, 0, 0,-8, 9,0, 1,1,1,0,-1,-1,-1,0, SH,SF,SF,SF,SH, 0, 0,0, 0,0,0,27,0);
|
||
|
|
CURSOR_OFFSET_Y: array [TKMCursorImageType] of Integer = (0,9,10,18,20,44,13,0,-1,0,1,1, 1, 0,-1,0, 0 ,0 ,SH,SF,SF,SF,SH,0,28,0,0,28,0);
|
||
|
|
|
||
|
|
CUSTOM_CUR_FILENAME: array[0..CUSTOM_CUR_CNT-1] of UnicodeString = ('cur1.ani');
|
||
|
|
var
|
||
|
|
C: TKMCursorImageType;
|
||
|
|
I, sx, sy, x, y: Integer;
|
||
|
|
bm, bm2: TBitmap;
|
||
|
|
iconInfo: TIconInfo;
|
||
|
|
Px: PRGBQuad;
|
||
|
|
h : THandle;
|
||
|
|
path: PChar;
|
||
|
|
rxData: PRXData;
|
||
|
|
begin
|
||
|
|
if SKIP_RENDER then Exit;
|
||
|
|
|
||
|
|
rxData := @aSprites.RXData; // Store pointer to record instead of duplicating it
|
||
|
|
|
||
|
|
bm := TBitmap.Create; bm.HandleType := bmDIB; bm.PixelFormat := pf32bit;
|
||
|
|
bm2 := TBitmap.Create; bm2.HandleType := bmDIB; bm2.PixelFormat := pf32bit;
|
||
|
|
|
||
|
|
for C := Low(TKMCursorImageType) to kmcPaintBucket do
|
||
|
|
begin
|
||
|
|
// Special case for invisible cursor
|
||
|
|
if C = kmcInvisible then
|
||
|
|
begin
|
||
|
|
bm.Width := 1; bm.Height := 1;
|
||
|
|
bm2.Width := 1; bm2.Height := 1;
|
||
|
|
bm2.Canvas.Pixels[0,0] := clWhite; //Invisible mask, we don't care for Image color
|
||
|
|
iconInfo.xHotspot := 0;
|
||
|
|
iconInfo.yHotspot := 0;
|
||
|
|
end
|
||
|
|
else
|
||
|
|
begin
|
||
|
|
// Just skip missing cursor, do not crash the game
|
||
|
|
if CURSOR_SPRITE_INDEX[C] >= Length(rxData.Size) then
|
||
|
|
Continue;
|
||
|
|
|
||
|
|
sx := rxData.Size[CURSOR_SPRITE_INDEX[C]].X;
|
||
|
|
sy := rxData.Size[CURSOR_SPRITE_INDEX[C]].Y;
|
||
|
|
bm.Width := sx; bm.Height := sy;
|
||
|
|
bm2.Width := sx; bm2.Height := sy;
|
||
|
|
|
||
|
|
for y := 0 to sy - 1 do
|
||
|
|
begin
|
||
|
|
Px := bm.ScanLine[y];
|
||
|
|
for x := 0 to sx - 1 do
|
||
|
|
begin
|
||
|
|
if rxData.RGBA[CURSOR_SPRITE_INDEX[C],y*sx+x] and $FF000000 = 0 then
|
||
|
|
Px.rgbReserved := $00
|
||
|
|
else
|
||
|
|
Px.rgbReserved := $FF;
|
||
|
|
// Here we have BGR, not RGB
|
||
|
|
Px.rgbBlue := (rxData.RGBA[CURSOR_SPRITE_INDEX[C],y*sx+x] and $FF0000) shr 16;
|
||
|
|
Px.rgbGreen := (rxData.RGBA[CURSOR_SPRITE_INDEX[C],y*sx+x] and $FF00) shr 8;
|
||
|
|
Px.rgbRed := rxData.RGBA[CURSOR_SPRITE_INDEX[C],y*sx+x] and $FF;
|
||
|
|
Inc(Px);
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
//Load hotspot offsets from RX file, adding the manual offsets (normally 0)
|
||
|
|
iconInfo.xHotspot := Max(-rxData.Pivot[CURSOR_SPRITE_INDEX[C]].x + CURSOR_OFFSET_X[C], 0);
|
||
|
|
iconInfo.yHotspot := Max(-rxData.Pivot[CURSOR_SPRITE_INDEX[C]].y + CURSOR_OFFSET_Y[C], 0);
|
||
|
|
end;
|
||
|
|
|
||
|
|
//Release the Mask, otherwise there is black rect in Lazarus
|
||
|
|
//it works only from within the loop, means mask is recreated when we access canvas or something like that
|
||
|
|
bm2.ReleaseMaskHandle;
|
||
|
|
|
||
|
|
iconInfo.fIcon := False; //True=Icon, False=Cursor
|
||
|
|
iconInfo.hbmColor := bm.Handle;
|
||
|
|
|
||
|
|
//I have a suspicion that maybe Windows could create icon delayed, at a time when bitmap data is
|
||
|
|
//no longer valid (replaced by other bitmap or freed). Hence issues with transparency.
|
||
|
|
{$IFDEF MSWindows}
|
||
|
|
iconInfo.hbmMask := bm2.Handle;
|
||
|
|
Screen.Cursors[Byte(C) + CURSOR_CNT_OFFSET] := CreateIconIndirect(iconInfo);
|
||
|
|
{$ENDIF}
|
||
|
|
{$IFDEF Unix}
|
||
|
|
bm2.Mask(clWhite);
|
||
|
|
IconInfo.hbmMask := bm2.MaskHandle;
|
||
|
|
Screen.Cursors[Byte(C) + CURSOR_CNT_OFFSET] := CreateIconIndirect(@IconInfo);
|
||
|
|
{$ENDIF}
|
||
|
|
end;
|
||
|
|
|
||
|
|
bm.Free;
|
||
|
|
bm2.Free;
|
||
|
|
|
||
|
|
{$IFDEF WDC}
|
||
|
|
for I := Low(CUSTOM_CURSORS) to High(CUSTOM_CURSORS) do
|
||
|
|
begin
|
||
|
|
path := PChar(ExeDir + 'data' + PathDelim + 'cursors' + PathDelim + CUSTOM_CUR_FILENAME[I]);
|
||
|
|
h := LoadImage(0,
|
||
|
|
path,
|
||
|
|
IMAGE_CURSOR,
|
||
|
|
35,
|
||
|
|
36,
|
||
|
|
LR_DEFAULTSIZE or
|
||
|
|
LR_LOADFROMFILE);
|
||
|
|
|
||
|
|
if h <> 0 then
|
||
|
|
Screen.Cursors[Byte(CUSTOM_CURSORS[I]) + CURSOR_CNT_OFFSET] := h
|
||
|
|
else
|
||
|
|
// Use default cursor, in case of missing cursor file
|
||
|
|
Screen.Cursors[Byte(CUSTOM_CURSORS[I]) + CURSOR_CNT_OFFSET] := Screen.Cursors[Byte(kmcDefault) + CURSOR_CNT_OFFSET];
|
||
|
|
end;
|
||
|
|
{$ENDIF}
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMSystem.FlashingStart;
|
||
|
|
{$IFNDEF FPC}{$IFDEF MSWindows}
|
||
|
|
var
|
||
|
|
flashInfo: TFlashWInfo;
|
||
|
|
{$ENDIF}{$ENDIF}
|
||
|
|
begin
|
||
|
|
if Self = nil then Exit;
|
||
|
|
|
||
|
|
{$IFNDEF FPC}{$IFDEF MSWindows}
|
||
|
|
if (GetForegroundWindow <> fFormMainHandle) then
|
||
|
|
begin
|
||
|
|
flashInfo.cbSize := 20;
|
||
|
|
flashInfo.hwnd := Application.Handle;
|
||
|
|
flashInfo.dwflags := FLASHW_ALL;
|
||
|
|
flashInfo.ucount := 5; // Flash 5 times
|
||
|
|
flashInfo.dwtimeout := 0; // Use default cursor blink rate
|
||
|
|
fFlashing := True;
|
||
|
|
FlashWindowEx(flashInfo);
|
||
|
|
end
|
||
|
|
{$ENDIF}{$ENDIF}
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
procedure TKMSystem.FlashingStop;
|
||
|
|
{$IFNDEF FPC}{$IFDEF MSWindows}
|
||
|
|
var
|
||
|
|
flashInfo: TFlashWInfo;
|
||
|
|
{$ENDIF}{$ENDIF}
|
||
|
|
begin
|
||
|
|
if Self = nil then Exit;
|
||
|
|
|
||
|
|
{$IFNDEF FPC}{$IFDEF MSWindows}
|
||
|
|
if fFlashing then
|
||
|
|
begin
|
||
|
|
flashInfo.cbSize := 20;
|
||
|
|
flashInfo.hwnd := Application.Handle;
|
||
|
|
flashInfo.dwflags := FLASHW_STOP;
|
||
|
|
flashInfo.ucount := 0;
|
||
|
|
flashInfo.dwtimeout := 0;
|
||
|
|
fFlashing := False;
|
||
|
|
FlashWindowEx(flashInfo);
|
||
|
|
end
|
||
|
|
{$ENDIF}{$ENDIF}
|
||
|
|
end;
|
||
|
|
|
||
|
|
|
||
|
|
end.
|