For Linux and other Unix platforms, a slightly different approach should be used.
QueryPerformanceFrequency and
QueryPerformanceCounter do not exist on these platforms, so you need to use e.g. other functions. The class of this clock was taken from the
Deep Platformer project — in this project the clock class is adapted to Linux. These functions look like this:
// from the Deep Platformer sources
function TClock.GetCounterFrequency(): Int64;
begin
{$IFDEF WINDOWS}
Result := 0;
QueryPerformanceFrequency(Result);
{$ELSE}
Result := 1000000000;
{$ENDIF}
end;
function TClock.GetCounterValue(): Int64;
{$IFDEF UNIX}
var
Counter: TTimeVal;
{$ENDIF}
begin
{$IFDEF WINDOWS}
Result := 0;
QueryPerformanceCounter(Result);
{$ELSE}
FPGetTimeOfDay(@Counter, nil);
Result := Int64(Counter.tv_sec) * 1000000000 + Int64(Counter.tv_usec) * 1000;
{$ENDIF}
end;
In the thread hold method, the number of nanoseconds is calculated and the thread is frozen using the
FPNanoSleep function. In the case of Unix, busy waiting is not needed, because
FPNanoSleep supports precision down to a nanosecond (actually a microsecond, but that's enough), while under Windows,
Sleep has only a millisecond precision, which is not enough — that's why busy waiting is used additionally:
// from the Deep Platformer sources
procedure TClock.WaitForNMI();
var
{$IFDEF WINDOWS}
SleepTime: Single;
{$ELSE}
SleepTime: Int64;
RequestedTime, RemainingTime: TTimeSpec;
{$ENDIF}
begin
if not Machine.Stopped then
begin
{$IFDEF WINDOWS}
if not FPrecise then
begin
SleepTime := 1000 / FFrameRateLimit * (1 - (FFrameTicksEnd - FFrameTicksBegin) / FTicksPerFrame) - 1;
SleepTime -= Ord(Round(SleepTime) > SleepTime);
SleepTime := Max(SleepTime, 0);
Sleep(Round(SleepTime));
end;
while GetCounterValue() < FFrameTicksNext do ;
{$ELSE}
SleepTime := FFrameTicksBegin + FTicksPerFrame - FFrameTicksEnd;
if SleepTime > 0 then
begin
RemainingTime.tv_sec := SleepTime div 1000000000;
RemainingTime.tv_nsec := SleepTime mod 1000000000;
repeat
RequestedTime := RemainingTime;
if FPNanoSleep(@RequestedTime, @RemainingTime) = 0 then Exit;
if FPGetErrNo() <> ESysEINTR then Exit;
until False;
end;
{$ENDIF}
end
else
Sleep(50);
end;
When using SDL under Windows, the
Sleep function does not suspend the thread's work (I don't know why), so the specified number of milliseconds is frozen by the
SDL_Delay function, which also has millisecond precision. The remainder is waited with busy waiting, calling the assembly
pause instruction in loop:
// from the Fairtris sources
procedure TClock.UpdateFrameAlign();
var
SleepTime: Single;
begin
// calculating the number of milliseconds
SleepTime := 1000 / FFrameRateLimit * (1 - (FFrameTicksEnd - FFrameTicksBegin) / FTicksPerFrame) - 1;
SleepTime -= Ord(Round(SleepTime) > SleepTime);
SleepTime := Max(SleepTime, 0);
// suspending the thread's work for a given number of milliseconds
SDL_Delay(Round(SleepTime));
// suspending the thread's work for the remaining number of nanoseconds (busy waiting)
while GetCounterValue() < FFrameTicksNext do
asm
pause
end;
end;
So, to make the clock compatible with Linux, you just need to use the fixed value
1,000,000,000 as the number of ticks of the clock (ticks per one second) and find the current time with high precision using the
FPGetTimeOfDay function and simple calculations as I showed in the first code snippet.
However, to suspend the work of the thread for a given amount of time (a few or several milliseconds and the microsecond part), use the above code, i.e. simple calculations and the
FPNanoSleep function in a loop. If the
FPNanoSleep function will not stop the thread (like
Sleep in Windows), then do the same as in Windows — calculate the millisecond part and freeze the thread with
SDL_Delay, and eat the remaining (microsecond) part with busy waiting (loop checking the current number of ticks and the
pause command).
So, probably the only thing that needs to be changed in the
Fairtris code is the contents of the
GetCounterFrequency and
GetCounterValue methods, using the code taken from the clock in the
Deep Platformer game. The
UpdateFrameAlign method will most likely not need to be changed. And if
FPNanoSleep can freeze the thread, then it should be used to avoid busy waiting and save even more computing power.
I cannot use the terminal for debugging, because of the fullscreen mode.
If you need the game not to use the exclusive video mode, just open the
bin\settings.ini file and change its value:
[VIDEO]
ENABLED=1 ; set "0" to disable video mode
[GENERAL]
MONITOR=0
LEFT=0
TOP=0
INPUT=0
WINDOW=2
THEME=0
SOUNDS=0
SCROLL=0
REGION=0
RNG=0
LEVEL=0
; ...
Additionally, you can change the initial window size by changing the
WINDOW value to something else —
0 is the smallest window;
1,
2 and
3 are zoomed windows; and
4 is full screen in windowed mode (without exclusive video mode). The default value is
2, which is
Zoom 3x (
878×720 pixels).
Exclusive video mode can be toggled with the
F11 key, while other options can be modified in the in-game
settings screen and
gameplay settings screen (and also using the mouse). It is not necessary to manually modify the contents of the configuration file.