Merge branch 'master' into jgrpp
# Conflicts: # src/dock_gui.cpp # src/rail_gui.cpp # src/road_gui.cpp # src/terraform_gui.cpp # src/vehicle.cpp # src/video/allegro_v.cpp # src/video/cocoa/cocoa_v.mm # src/video/dedicated_v.cpp # src/video/sdl2_v.cpp # src/video/sdl_v.cpp # src/video/win32_v.cpp
This commit is contained in:
@@ -31,5 +31,6 @@ add_files(
|
||||
dedicated_v.h
|
||||
null_v.cpp
|
||||
null_v.h
|
||||
video_driver.cpp
|
||||
video_driver.hpp
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ void VideoDriver_Allegro::MakeDirty(int left, int top, int width, int height)
|
||||
_num_dirty_rects++;
|
||||
}
|
||||
|
||||
static void DrawSurfaceToScreen()
|
||||
void VideoDriver_Allegro::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
@@ -95,7 +95,7 @@ static void InitPalette()
|
||||
UpdatePalette(0, 256);
|
||||
}
|
||||
|
||||
static void CheckPaletteAnim()
|
||||
void VideoDriver_Allegro::CheckPaletteAnim()
|
||||
{
|
||||
if (_cur_palette.count_dirty != 0) {
|
||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||
@@ -447,95 +447,49 @@ void VideoDriver_Allegro::Stop()
|
||||
if (--_allegro_instance_count == 0) allegro_exit();
|
||||
}
|
||||
|
||||
void VideoDriver_Allegro::InputLoop()
|
||||
{
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
|
||||
_ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG) != _invert_ctrl;
|
||||
_shift_pressed = !!(key_shifts & KB_SHIFT_FLAG) != _invert_shift;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application. */
|
||||
if (key[KEY_TAB] && (key_shifts & KB_ALT_FLAG) == 0)
|
||||
#endif
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
/* Determine which directional keys are down. */
|
||||
_dirkeys =
|
||||
(key[KEY_LEFT] ? 1 : 0) |
|
||||
(key[KEY_UP] ? 2 : 0) |
|
||||
(key[KEY_RIGHT] ? 4 : 0) |
|
||||
(key[KEY_DOWN] ? 8 : 0);
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
}
|
||||
|
||||
void VideoDriver_Allegro::MainLoop()
|
||||
{
|
||||
auto cur_ticks = std::chrono::steady_clock::now();
|
||||
auto last_realtime_tick = cur_ticks;
|
||||
auto next_game_tick = cur_ticks;
|
||||
auto next_draw_tick = cur_ticks;
|
||||
|
||||
CheckPaletteAnim();
|
||||
|
||||
for (;;) {
|
||||
InteractiveRandom(); // randomness
|
||||
|
||||
PollEvent();
|
||||
if (_exit_game) return;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application */
|
||||
if (key[KEY_TAB] && (key_shifts & KB_ALT_FLAG) == 0)
|
||||
#endif
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
|
||||
IncreaseRealtimeTick(delta.count());
|
||||
last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
|
||||
if (_fast_forward && !_pause_mode) {
|
||||
next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
} else {
|
||||
next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
GameLoop();
|
||||
GameLoopPaletteAnimations();
|
||||
}
|
||||
|
||||
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
|
||||
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
|
||||
next_draw_tick += this->GetDrawInterval();
|
||||
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = !!(key_shifts & KB_CTRL_FLAG) != _invert_ctrl;
|
||||
_shift_pressed = !!(key_shifts & KB_SHIFT_FLAG) != _invert_shift;
|
||||
|
||||
/* determine which directional keys are down */
|
||||
_dirkeys =
|
||||
(key[KEY_LEFT] ? 1 : 0) |
|
||||
(key[KEY_UP] ? 2 : 0) |
|
||||
(key[KEY_RIGHT] ? 4 : 0) |
|
||||
(key[KEY_DOWN] ? 8 : 0);
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
|
||||
InputLoop();
|
||||
UpdateWindows();
|
||||
CheckPaletteAnim();
|
||||
|
||||
DrawSurfaceToScreen();
|
||||
}
|
||||
|
||||
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
||||
if (!_fast_forward || _pause_mode) {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto next_tick = std::min(next_draw_tick, next_game_tick);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_tick > now) {
|
||||
std::this_thread::sleep_for(next_tick - now);
|
||||
}
|
||||
if (this->Tick()) {
|
||||
this->Paint();
|
||||
}
|
||||
this->SleepTillNextTick();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,11 @@ public:
|
||||
bool ClaimMousePointer() override;
|
||||
|
||||
const char *GetName() const override { return "allegro"; }
|
||||
|
||||
protected:
|
||||
void InputLoop() override;
|
||||
void Paint() override;
|
||||
void CheckPaletteAnim() override;
|
||||
};
|
||||
|
||||
/** Factory for the allegro video driver. */
|
||||
|
||||
@@ -32,10 +32,8 @@ private:
|
||||
void *pixel_buffer; ///< used for direct pixel access
|
||||
void *window_buffer; ///< Colour translation from palette to screen
|
||||
|
||||
static const int MAX_DIRTY_RECTS = 100;
|
||||
Rect dirty_rect; ///< Region of the screen that needs redrawing.
|
||||
|
||||
Rect dirty_rects[MAX_DIRTY_RECTS]; ///< dirty rectangles
|
||||
uint num_dirty_rects; ///< Number of dirty rectangles
|
||||
uint32 palette[256]; ///< Colour Palette
|
||||
|
||||
public:
|
||||
@@ -74,6 +72,9 @@ public:
|
||||
protected:
|
||||
Dimension GetScreenSize() const override;
|
||||
float GetDPIScale() override;
|
||||
void InputLoop() override;
|
||||
void Paint() override;
|
||||
void CheckPaletteAnim() override;
|
||||
|
||||
private:
|
||||
bool PollEvent();
|
||||
@@ -86,9 +87,7 @@ private:
|
||||
bool MakeWindow(int width, int height);
|
||||
|
||||
void UpdatePalette(uint first_color, uint num_colors);
|
||||
void CheckPaletteAnim();
|
||||
|
||||
void Draw(bool force_update = false);
|
||||
void BlitIndexedToView32(int left, int top, int right, int bottom);
|
||||
};
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
#define Rect OTTDRect
|
||||
#define Point OTTDPoint
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#undef Rect
|
||||
#undef Point
|
||||
|
||||
#include "../../openttd.h"
|
||||
#include "../../debug.h"
|
||||
#include "../../core/geometry_type.hpp"
|
||||
#include "../../core/geometry_func.hpp"
|
||||
#include "../../core/math_func.hpp"
|
||||
#include "cocoa_v.h"
|
||||
#include "cocoa_wnd.h"
|
||||
@@ -125,7 +126,7 @@ VideoDriver_Cocoa::VideoDriver_Cocoa()
|
||||
this->color_space = nullptr;
|
||||
this->cgcontext = nullptr;
|
||||
|
||||
this->num_dirty_rects = lengthof(this->dirty_rects);
|
||||
this->dirty_rect = {};
|
||||
}
|
||||
|
||||
/** Stop Cocoa video driver. */
|
||||
@@ -192,13 +193,8 @@ const char *VideoDriver_Cocoa::Start(const StringList &parm)
|
||||
*/
|
||||
void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height)
|
||||
{
|
||||
if (this->num_dirty_rects < lengthof(this->dirty_rects)) {
|
||||
dirty_rects[this->num_dirty_rects].left = left;
|
||||
dirty_rects[this->num_dirty_rects].top = top;
|
||||
dirty_rects[this->num_dirty_rects].right = left + width;
|
||||
dirty_rects[this->num_dirty_rects].bottom = top + height;
|
||||
}
|
||||
this->num_dirty_rects++;
|
||||
Rect r = {left, top, left + width, top + height};
|
||||
this->dirty_rect = BoundingRect(this->dirty_rect, r);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,49 +461,39 @@ void VideoDriver_Cocoa::BlitIndexedToView32(int left, int top, int right, int bo
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw window.
|
||||
* Paint window.
|
||||
* @param force_update Whether to redraw unconditionally
|
||||
*/
|
||||
void VideoDriver_Cocoa::Draw(bool force_update)
|
||||
void VideoDriver_Cocoa::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
/* Check if we need to do anything */
|
||||
if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return;
|
||||
if (IsEmptyRect(this->dirty_rect) || [ this->window isMiniaturized ]) return;
|
||||
|
||||
if (this->num_dirty_rects >= lengthof(this->dirty_rects)) {
|
||||
this->num_dirty_rects = 1;
|
||||
this->dirty_rects[0].left = 0;
|
||||
this->dirty_rects[0].top = 0;
|
||||
this->dirty_rects[0].right = this->window_width;
|
||||
this->dirty_rects[0].bottom = this->window_height;
|
||||
/* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */
|
||||
if (this->buffer_depth == 8) {
|
||||
BlitIndexedToView32(
|
||||
this->dirty_rect.left,
|
||||
this->dirty_rect.top,
|
||||
this->dirty_rect.right,
|
||||
this->dirty_rect.bottom
|
||||
);
|
||||
}
|
||||
|
||||
/* Build the region of dirty rectangles */
|
||||
for (uint i = 0; i < this->num_dirty_rects; i++) {
|
||||
/* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */
|
||||
if (this->buffer_depth == 8) {
|
||||
BlitIndexedToView32(
|
||||
this->dirty_rects[i].left,
|
||||
this->dirty_rects[i].top,
|
||||
this->dirty_rects[i].right,
|
||||
this->dirty_rects[i].bottom
|
||||
);
|
||||
}
|
||||
NSRect dirtyrect;
|
||||
dirtyrect.origin.x = this->dirty_rect.left;
|
||||
dirtyrect.origin.y = this->window_height - this->dirty_rect.bottom;
|
||||
dirtyrect.size.width = this->dirty_rect.right - this->dirty_rect.left;
|
||||
dirtyrect.size.height = this->dirty_rect.bottom - this->dirty_rect.top;
|
||||
|
||||
NSRect dirtyrect;
|
||||
dirtyrect.origin.x = this->dirty_rects[i].left;
|
||||
dirtyrect.origin.y = this->window_height - this->dirty_rects[i].bottom;
|
||||
dirtyrect.size.width = this->dirty_rects[i].right - this->dirty_rects[i].left;
|
||||
dirtyrect.size.height = this->dirty_rects[i].bottom - this->dirty_rects[i].top;
|
||||
/* Notify OS X that we have new content to show. */
|
||||
[ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ];
|
||||
|
||||
/* Normally drawRect will be automatically called by Mac OS X during next update cycle,
|
||||
* and then blitting will occur. If force_update is true, it will be done right now. */
|
||||
[ this->cocoaview setNeedsDisplayInRect:[ this->cocoaview getVirtualRect:dirtyrect ] ];
|
||||
if (force_update) [ this->cocoaview displayIfNeeded ];
|
||||
}
|
||||
/* Tell the OS to get our contents to screen as soon as possible. */
|
||||
[ CATransaction flush ];
|
||||
|
||||
this->num_dirty_rects = 0;
|
||||
this->dirty_rect = {};
|
||||
}
|
||||
|
||||
/** Update the palette. */
|
||||
@@ -523,7 +509,7 @@ void VideoDriver_Cocoa::UpdatePalette(uint first_color, uint num_colors)
|
||||
this->palette[i] = clr;
|
||||
}
|
||||
|
||||
this->num_dirty_rects = lengthof(this->dirty_rects);
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
/** Clear buffer to opaque black. */
|
||||
@@ -581,8 +567,8 @@ void VideoDriver_Cocoa::AllocateBackingStore()
|
||||
}
|
||||
|
||||
/* Redraw screen */
|
||||
this->num_dirty_rects = lengthof(this->dirty_rects);
|
||||
this->GameSizeChanged();
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
/** Check if palette updates need to be performed. */
|
||||
@@ -632,14 +618,33 @@ bool VideoDriver_Cocoa::PollEvent()
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoDriver_Cocoa::InputLoop()
|
||||
{
|
||||
NSUInteger cur_mods = [ NSEvent modifierFlags ];
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = ((cur_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)) != 0) != _invert_ctrl;
|
||||
_shift_pressed = ((cur_mods & NSShiftKeyMask) != 0) != _invert_shift;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed) {
|
||||
#else
|
||||
if (_tab_is_down) {
|
||||
#endif
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
}
|
||||
|
||||
/** Main game loop. */
|
||||
void VideoDriver_Cocoa::GameLoop()
|
||||
{
|
||||
auto cur_ticks = std::chrono::steady_clock::now();
|
||||
auto last_realtime_tick = cur_ticks;
|
||||
auto next_game_tick = cur_ticks;
|
||||
auto next_draw_tick = cur_ticks;
|
||||
|
||||
for (;;) {
|
||||
@autoreleasepool {
|
||||
|
||||
@@ -653,69 +658,10 @@ void VideoDriver_Cocoa::GameLoop()
|
||||
break;
|
||||
}
|
||||
|
||||
NSUInteger cur_mods = [ NSEvent modifierFlags ];
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (cur_mods & NSShiftKeyMask) {
|
||||
#else
|
||||
if (_tab_is_down) {
|
||||
#endif
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
|
||||
IncreaseRealtimeTick(delta.count());
|
||||
last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
|
||||
if (_fast_forward && !_pause_mode) {
|
||||
next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
} else {
|
||||
next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
::GameLoop();
|
||||
}
|
||||
|
||||
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
|
||||
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
|
||||
next_draw_tick += this->GetDrawInterval();
|
||||
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
|
||||
_ctrl_pressed = (cur_mods & ( _settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask)) != 0;
|
||||
_shift_pressed = (cur_mods & NSShiftKeyMask) != 0;
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
|
||||
InputLoop();
|
||||
UpdateWindows();
|
||||
this->CheckPaletteAnim();
|
||||
|
||||
this->Draw();
|
||||
}
|
||||
|
||||
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
||||
if (!_fast_forward || _pause_mode) {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto next_tick = std::min(next_draw_tick, next_game_tick);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_tick > now) {
|
||||
std::this_thread::sleep_for(next_tick - now);
|
||||
}
|
||||
if (this->Tick()) {
|
||||
this->Paint();
|
||||
}
|
||||
this->SleepTillNextTick();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -733,6 +679,7 @@ void VideoDriver_Cocoa::GameLoop()
|
||||
self.wantsLayer = YES;
|
||||
|
||||
self.layer.magnificationFilter = kCAFilterNearest;
|
||||
self.layer.opaque = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -236,10 +236,6 @@ static void DedicatedHandleKeyInput()
|
||||
|
||||
void VideoDriver_Dedicated::MainLoop()
|
||||
{
|
||||
auto cur_ticks = std::chrono::steady_clock::now();
|
||||
auto last_realtime_tick = cur_ticks;
|
||||
auto next_game_tick = cur_ticks;
|
||||
|
||||
/* Signal handlers */
|
||||
#if defined(UNIX)
|
||||
signal(SIGTERM, DedicatedSignalHandler);
|
||||
@@ -283,44 +279,8 @@ void VideoDriver_Dedicated::MainLoop()
|
||||
|
||||
if (!_dedicated_forks) DedicatedHandleKeyInput();
|
||||
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
|
||||
IncreaseRealtimeTick(delta.count());
|
||||
last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= next_game_tick || _ddc_fastforward) {
|
||||
if (_ddc_fastforward) {
|
||||
next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
} else {
|
||||
next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
GameLoop();
|
||||
GameLoopPaletteAnimations();
|
||||
InputLoop();
|
||||
UpdateWindows();
|
||||
}
|
||||
|
||||
/* Don't sleep when fast forwarding (for desync debugging) */
|
||||
if (!_ddc_fastforward) {
|
||||
/* Sleep longer on a dedicated server, if the game is paused and no clients connected.
|
||||
* That can allow the CPU to better use deep sleep states. */
|
||||
if (_pause_mode != 0 && !HasClients()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
} else {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_game_tick > now) {
|
||||
std::this_thread::sleep_for(next_game_tick - now);
|
||||
}
|
||||
}
|
||||
}
|
||||
_fast_forward = _ddc_fastforward;
|
||||
this->Tick();
|
||||
this->SleepTillNextTick();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ void VideoDriver_SDL::CheckPaletteAnim()
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
static void Paint()
|
||||
void VideoDriver_SDL::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
@@ -372,10 +372,10 @@ static void Paint()
|
||||
}
|
||||
SDL_UpdateWindowSurfaceRects(_sdl_window, &r, 1);
|
||||
|
||||
MemSetT(&_dirty_rect, 0);
|
||||
_dirty_rect = {};
|
||||
}
|
||||
|
||||
static void PaintThread()
|
||||
void VideoDriver_SDL::PaintThread()
|
||||
{
|
||||
/* First tell the main thread we're started */
|
||||
std::unique_lock<std::recursive_mutex> lock(*_draw_mutex);
|
||||
@@ -386,11 +386,16 @@ static void PaintThread()
|
||||
|
||||
while (_draw_continue) {
|
||||
/* Then just draw and wait till we stop */
|
||||
Paint();
|
||||
this->Paint();
|
||||
_draw_signal->wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void VideoDriver_SDL::PaintThreadThunk(VideoDriver_SDL *drv)
|
||||
{
|
||||
drv->PaintThread();
|
||||
}
|
||||
|
||||
static const Dimension default_resolutions[] = {
|
||||
{ 640, 480 },
|
||||
{ 800, 600 },
|
||||
@@ -556,7 +561,7 @@ bool VideoDriver_SDL::CreateMainSurface(uint w, uint h, bool resize)
|
||||
* gotten smaller, reset our dirty rects. GameSizeChanged() a bit lower
|
||||
* will mark the whole screen dirty again anyway, but this time with the
|
||||
* new dimensions. */
|
||||
MemSetT(&_dirty_rect, 0);
|
||||
_dirty_rect = {};
|
||||
|
||||
_screen.width = _sdl_surface->w;
|
||||
_screen.height = _sdl_surface->h;
|
||||
@@ -1059,11 +1064,43 @@ void VideoDriver_SDL::Stop()
|
||||
}
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::InputLoop()
|
||||
{
|
||||
uint32 mod = SDL_GetModState();
|
||||
const Uint8 *keys = SDL_GetKeyboardState(NULL);
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl;
|
||||
_shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application. */
|
||||
if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
|
||||
#endif /* defined(_DEBUG) */
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
/* Determine which directional keys are down. */
|
||||
_dirkeys =
|
||||
(keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
|
||||
(keys[SDL_SCANCODE_UP] ? 2 : 0) |
|
||||
(keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
|
||||
(keys[SDL_SCANCODE_DOWN] ? 8 : 0);
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::LoopOnce()
|
||||
{
|
||||
uint32 mod;
|
||||
int numkeys;
|
||||
const Uint8 *keys;
|
||||
InteractiveRandom(); // randomness
|
||||
|
||||
while (PollEvent() == -1) {}
|
||||
@@ -1081,106 +1118,23 @@ void VideoDriver_SDL::LoopOnce()
|
||||
return;
|
||||
}
|
||||
|
||||
mod = SDL_GetModState();
|
||||
keys = SDL_GetKeyboardState(&numkeys);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application */
|
||||
if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
|
||||
#endif /* defined(_DEBUG) */
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
|
||||
IncreaseRealtimeTick(delta.count());
|
||||
last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
|
||||
if (_fast_forward && !_pause_mode) {
|
||||
next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
} else {
|
||||
next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
/* The gameloop is the part that can run asynchronously. The rest
|
||||
* except sleeping can't. */
|
||||
if (_draw_mutex != nullptr) draw_lock.unlock();
|
||||
GameLoop();
|
||||
if (_draw_mutex != nullptr) draw_lock.lock();
|
||||
GameLoopPaletteAnimations();
|
||||
}
|
||||
|
||||
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
|
||||
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
|
||||
next_draw_tick += this->GetDrawInterval();
|
||||
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl;
|
||||
_shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift;
|
||||
|
||||
/* determine which directional keys are down */
|
||||
_dirkeys =
|
||||
(keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
|
||||
(keys[SDL_SCANCODE_UP] ? 2 : 0) |
|
||||
(keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
|
||||
(keys[SDL_SCANCODE_DOWN] ? 8 : 0);
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
|
||||
InputLoop();
|
||||
UpdateWindows();
|
||||
this->CheckPaletteAnim();
|
||||
|
||||
if (VideoDriver::Tick()) {
|
||||
if (_draw_mutex != nullptr && !HasModalProgress()) {
|
||||
_draw_signal->notify_one();
|
||||
} else {
|
||||
Paint();
|
||||
this->Paint();
|
||||
}
|
||||
}
|
||||
|
||||
/* Emscripten is running an event-based mainloop; there is already some
|
||||
* downtime between each iteration, so no need to sleep. */
|
||||
#ifndef __EMSCRIPTEN__
|
||||
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
||||
if (!_fast_forward || _pause_mode) {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto next_tick = std::min(next_draw_tick, next_game_tick);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_tick > now) {
|
||||
if (_draw_mutex != nullptr) draw_lock.unlock();
|
||||
std::this_thread::sleep_for(next_tick - now);
|
||||
if (_draw_mutex != nullptr) draw_lock.lock();
|
||||
}
|
||||
}
|
||||
this->SleepTillNextTick();
|
||||
#endif
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::MainLoop()
|
||||
{
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
last_realtime_tick = cur_ticks;
|
||||
next_game_tick = cur_ticks;
|
||||
|
||||
this->CheckPaletteAnim();
|
||||
|
||||
if (_draw_threaded) {
|
||||
/* Initialise the mutex first, because that's the thing we *need*
|
||||
* directly in the newly created thread. */
|
||||
@@ -1192,7 +1146,7 @@ void VideoDriver_SDL::MainLoop()
|
||||
_draw_signal = new std::condition_variable_any();
|
||||
_draw_continue = true;
|
||||
|
||||
_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &PaintThread);
|
||||
_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &VideoDriver_SDL::PaintThreadThunk, this);
|
||||
|
||||
/* Free the mutex if we won't be able to use it. */
|
||||
if (!_draw_threaded) {
|
||||
@@ -1317,3 +1271,14 @@ Dimension VideoDriver_SDL::GetScreenSize() const
|
||||
|
||||
return { static_cast<uint>(mode.w), static_cast<uint>(mode.h) };
|
||||
}
|
||||
|
||||
bool VideoDriver_SDL::LockVideoBuffer()
|
||||
{
|
||||
if (_draw_threaded) this->draw_lock.lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::UnlockVideoBuffer()
|
||||
{
|
||||
if (_draw_threaded) this->draw_lock.unlock();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ public:
|
||||
|
||||
protected:
|
||||
Dimension GetScreenSize() const override;
|
||||
void InputLoop() override;
|
||||
bool LockVideoBuffer() override;
|
||||
void UnlockVideoBuffer() override;
|
||||
void Paint() override;
|
||||
void PaintThread() override;
|
||||
void CheckPaletteAnim() override;
|
||||
|
||||
private:
|
||||
int PollEvent();
|
||||
@@ -50,7 +56,6 @@ private:
|
||||
void MainLoopCleanup();
|
||||
bool CreateMainSurface(uint w, uint h, bool resize);
|
||||
bool CreateMainWindow(uint w, uint h);
|
||||
void CheckPaletteAnim();
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
/* Convert a constant pointer back to a non-constant pointer to a member function. */
|
||||
@@ -62,14 +67,11 @@ private:
|
||||
*/
|
||||
bool edit_box_focused;
|
||||
|
||||
std::chrono::steady_clock::time_point cur_ticks;
|
||||
std::chrono::steady_clock::time_point last_realtime_tick;
|
||||
std::chrono::steady_clock::time_point next_game_tick;
|
||||
std::chrono::steady_clock::time_point next_draw_tick;
|
||||
|
||||
int startup_display;
|
||||
std::thread draw_thread;
|
||||
std::unique_lock<std::recursive_mutex> draw_lock;
|
||||
|
||||
static void PaintThreadThunk(VideoDriver_SDL *drv);
|
||||
};
|
||||
|
||||
/** Factory for the SDL video driver. */
|
||||
|
||||
@@ -128,8 +128,10 @@ static void InitPalette()
|
||||
UpdatePalette(true);
|
||||
}
|
||||
|
||||
static void CheckPaletteAnim()
|
||||
void VideoDriver_SDL::CheckPaletteAnim()
|
||||
{
|
||||
_local_palette = _cur_palette;
|
||||
|
||||
if (_cur_palette.count_dirty != 0) {
|
||||
Blitter *blitter = BlitterFactory::GetCurrentBlitter();
|
||||
|
||||
@@ -152,7 +154,7 @@ static void CheckPaletteAnim()
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawSurfaceToScreen()
|
||||
void VideoDriver_SDL::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
@@ -178,7 +180,7 @@ static void DrawSurfaceToScreen()
|
||||
}
|
||||
}
|
||||
|
||||
static void DrawSurfaceToScreenThread()
|
||||
void VideoDriver_SDL::PaintThread()
|
||||
{
|
||||
/* First tell the main thread we're started */
|
||||
std::unique_lock<std::recursive_mutex> lock(*_draw_mutex);
|
||||
@@ -188,13 +190,17 @@ static void DrawSurfaceToScreenThread()
|
||||
_draw_signal->wait(*_draw_mutex);
|
||||
|
||||
while (_draw_continue) {
|
||||
CheckPaletteAnim();
|
||||
/* Then just draw and wait till we stop */
|
||||
DrawSurfaceToScreen();
|
||||
this->Paint();
|
||||
_draw_signal->wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void VideoDriver_SDL::PaintThreadThunk(VideoDriver_SDL *drv)
|
||||
{
|
||||
drv->PaintThread();
|
||||
}
|
||||
|
||||
static const Dimension _default_resolutions[] = {
|
||||
{ 640, 480},
|
||||
{ 800, 600},
|
||||
@@ -410,11 +416,7 @@ bool VideoDriver_SDL::ClaimMousePointer()
|
||||
}
|
||||
|
||||
struct SDLVkMapping {
|
||||
#if SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
SDL_Keycode vk_from;
|
||||
#else
|
||||
uint16 vk_from;
|
||||
#endif
|
||||
byte vk_count;
|
||||
byte map_to;
|
||||
};
|
||||
@@ -653,20 +655,45 @@ void VideoDriver_SDL::Stop()
|
||||
}
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::InputLoop()
|
||||
{
|
||||
uint32 mod = SDL_GetModState();
|
||||
int numkeys;
|
||||
Uint8 *keys = SDL_GetKeyState(&numkeys);
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl;
|
||||
_shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application. */
|
||||
if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
|
||||
#endif /* defined(_DEBUG) */
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
/* Determine which directional keys are down. */
|
||||
_dirkeys =
|
||||
(keys[SDLK_LEFT] ? 1 : 0) |
|
||||
(keys[SDLK_UP] ? 2 : 0) |
|
||||
(keys[SDLK_RIGHT] ? 4 : 0) |
|
||||
(keys[SDLK_DOWN] ? 8 : 0);
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::MainLoop()
|
||||
{
|
||||
auto cur_ticks = std::chrono::steady_clock::now();
|
||||
auto last_realtime_tick = cur_ticks;
|
||||
auto next_game_tick = cur_ticks;
|
||||
auto next_draw_tick = cur_ticks;
|
||||
uint32 mod;
|
||||
int numkeys;
|
||||
Uint8 *keys;
|
||||
|
||||
CheckPaletteAnim();
|
||||
|
||||
std::thread draw_thread;
|
||||
std::unique_lock<std::recursive_mutex> draw_lock;
|
||||
if (_draw_threaded) {
|
||||
/* Initialise the mutex first, because that's the thing we *need*
|
||||
* directly in the newly created thread. */
|
||||
@@ -674,16 +701,16 @@ void VideoDriver_SDL::MainLoop()
|
||||
if (_draw_mutex == nullptr) {
|
||||
_draw_threaded = false;
|
||||
} else {
|
||||
draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
|
||||
this->draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
|
||||
_draw_signal = new std::condition_variable_any();
|
||||
_draw_continue = true;
|
||||
|
||||
_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &DrawSurfaceToScreenThread);
|
||||
_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-sdl", &VideoDriver_SDL::PaintThreadThunk, this);
|
||||
|
||||
/* Free the mutex if we won't be able to use it. */
|
||||
if (!_draw_threaded) {
|
||||
draw_lock.unlock();
|
||||
draw_lock.release();
|
||||
this->draw_lock.unlock();
|
||||
this->draw_lock.release();
|
||||
delete _draw_mutex;
|
||||
delete _draw_signal;
|
||||
_draw_mutex = nullptr;
|
||||
@@ -703,107 +730,14 @@ void VideoDriver_SDL::MainLoop()
|
||||
while (PollEvent() == -1) {}
|
||||
if (_exit_game) break;
|
||||
|
||||
mod = SDL_GetModState();
|
||||
#if SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
keys = SDL_GetKeyboardState(&numkeys);
|
||||
#else
|
||||
keys = SDL_GetKeyState(&numkeys);
|
||||
#endif
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application */
|
||||
#if SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
if (keys[SDL_SCANCODE_TAB] && (mod & KMOD_ALT) == 0)
|
||||
#else
|
||||
if (keys[SDLK_TAB] && (mod & KMOD_ALT) == 0)
|
||||
#endif /* SDL_VERSION_ATLEAST(1, 3, 0) */
|
||||
#endif /* defined(_DEBUG) */
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
|
||||
IncreaseRealtimeTick(delta.count());
|
||||
last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
|
||||
if (_fast_forward && !_pause_mode) {
|
||||
next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
} else {
|
||||
next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
/* The gameloop is the part that can run asynchronously. The rest
|
||||
* except sleeping can't. */
|
||||
if (_draw_mutex != nullptr) draw_lock.unlock();
|
||||
GameLoop();
|
||||
if (_draw_mutex != nullptr) draw_lock.lock();
|
||||
GameLoopPaletteAnimations();
|
||||
}
|
||||
|
||||
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
|
||||
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
|
||||
next_draw_tick += this->GetDrawInterval();
|
||||
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = !!(mod & KMOD_CTRL) != _invert_ctrl;
|
||||
_shift_pressed = !!(mod & KMOD_SHIFT) != _invert_shift;
|
||||
|
||||
/* determine which directional keys are down */
|
||||
_dirkeys =
|
||||
#if SDL_VERSION_ATLEAST(1, 3, 0)
|
||||
(keys[SDL_SCANCODE_LEFT] ? 1 : 0) |
|
||||
(keys[SDL_SCANCODE_UP] ? 2 : 0) |
|
||||
(keys[SDL_SCANCODE_RIGHT] ? 4 : 0) |
|
||||
(keys[SDL_SCANCODE_DOWN] ? 8 : 0);
|
||||
#else
|
||||
(keys[SDLK_LEFT] ? 1 : 0) |
|
||||
(keys[SDLK_UP] ? 2 : 0) |
|
||||
(keys[SDLK_RIGHT] ? 4 : 0) |
|
||||
(keys[SDLK_DOWN] ? 8 : 0);
|
||||
#endif
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
|
||||
InputLoop();
|
||||
UpdateWindows();
|
||||
_local_palette = _cur_palette;
|
||||
|
||||
if (this->Tick()) {
|
||||
if (_draw_mutex != nullptr && !HasModalProgress()) {
|
||||
_draw_signal->notify_one();
|
||||
} else {
|
||||
CheckPaletteAnim();
|
||||
DrawSurfaceToScreen();
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
||||
if (!_fast_forward || _pause_mode) {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto next_tick = std::min(next_draw_tick, next_game_tick);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_tick > now) {
|
||||
if (_draw_mutex != nullptr) draw_lock.unlock();
|
||||
std::this_thread::sleep_for(next_tick - now);
|
||||
if (_draw_mutex != nullptr) draw_lock.lock();
|
||||
this->Paint();
|
||||
}
|
||||
}
|
||||
this->SleepTillNextTick();
|
||||
}
|
||||
|
||||
if (_draw_mutex != nullptr) {
|
||||
@@ -811,8 +745,8 @@ void VideoDriver_SDL::MainLoop()
|
||||
/* Sending signal if there is no thread blocked
|
||||
* is very valid and results in noop */
|
||||
_draw_signal->notify_one();
|
||||
if (draw_lock.owns_lock()) draw_lock.unlock();
|
||||
draw_lock.release();
|
||||
if (this->draw_lock.owns_lock()) this->draw_lock.unlock();
|
||||
this->draw_lock.release();
|
||||
draw_thread.join();
|
||||
|
||||
delete _draw_mutex;
|
||||
@@ -863,4 +797,15 @@ void VideoDriver_SDL::ReleaseBlitterLock()
|
||||
if (_draw_mutex != nullptr) _draw_mutex->unlock();
|
||||
}
|
||||
|
||||
bool VideoDriver_SDL::LockVideoBuffer()
|
||||
{
|
||||
if (_draw_threaded) this->draw_lock.lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoDriver_SDL::UnlockVideoBuffer()
|
||||
{
|
||||
if (_draw_threaded) this->draw_lock.unlock();
|
||||
}
|
||||
|
||||
#endif /* WITH_SDL */
|
||||
|
||||
@@ -36,10 +36,23 @@ public:
|
||||
bool ClaimMousePointer() override;
|
||||
|
||||
const char *GetName() const override { return "sdl"; }
|
||||
|
||||
protected:
|
||||
void InputLoop() override;
|
||||
bool LockVideoBuffer() override;
|
||||
void UnlockVideoBuffer() override;
|
||||
void Paint() override;
|
||||
void PaintThread() override;
|
||||
void CheckPaletteAnim();
|
||||
|
||||
private:
|
||||
std::unique_lock<std::recursive_mutex> draw_lock;
|
||||
|
||||
int PollEvent();
|
||||
bool CreateMainSurface(uint w, uint h);
|
||||
void SetupKeyboard();
|
||||
|
||||
static void PaintThreadThunk(VideoDriver_SDL *drv);
|
||||
};
|
||||
|
||||
/** Factory for the SDL video driver. */
|
||||
|
||||
84
src/video/video_driver.cpp
Normal file
84
src/video/video_driver.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD 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 General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file video_driver.cpp Common code between video driver implementations. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../debug.h"
|
||||
#include "../gfx_func.h"
|
||||
#include "../progress.h"
|
||||
#include "../thread.h"
|
||||
#include "../window_func.h"
|
||||
#include "video_driver.hpp"
|
||||
|
||||
bool VideoDriver::Tick()
|
||||
{
|
||||
auto cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - this->last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - this->last_realtime_tick);
|
||||
_realtime_tick += delta.count();
|
||||
this->last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= this->next_game_tick || (_fast_forward && !_pause_mode)) {
|
||||
if (_fast_forward && !_pause_mode) {
|
||||
this->next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
} else {
|
||||
this->next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (this->next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
/* The game loop is the part that can run asynchronously.
|
||||
* The rest except sleeping can't. */
|
||||
this->UnlockVideoBuffer();
|
||||
::GameLoop();
|
||||
this->LockVideoBuffer();
|
||||
::GameLoopPaletteAnimations();
|
||||
|
||||
/* For things like dedicated server, don't run a separate draw-tick. */
|
||||
if (!this->HasGUI()) {
|
||||
::InputLoop();
|
||||
UpdateWindows();
|
||||
this->next_draw_tick = this->next_game_tick;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
|
||||
if (this->HasGUI() && cur_ticks >= this->next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
|
||||
this->next_draw_tick += this->GetDrawInterval();
|
||||
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
|
||||
if (this->next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) this->next_draw_tick = cur_ticks;
|
||||
|
||||
this->InputLoop();
|
||||
::InputLoop();
|
||||
UpdateWindows();
|
||||
this->CheckPaletteAnim();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void VideoDriver::SleepTillNextTick()
|
||||
{
|
||||
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
||||
if (!_fast_forward || _pause_mode) {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto next_tick = std::min(this->next_draw_tick, this->next_game_tick);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_tick > now) {
|
||||
this->UnlockVideoBuffer();
|
||||
std::this_thread::sleep_for(next_tick - now);
|
||||
this->LockVideoBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,50 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle input logic, is CTRL pressed, should we fast-forward, etc.
|
||||
*/
|
||||
virtual void InputLoop() {}
|
||||
|
||||
/**
|
||||
* Make sure the video buffer is ready for drawing.
|
||||
* @returns True if the video buffer has to be unlocked.
|
||||
*/
|
||||
virtual bool LockVideoBuffer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock a previously locked video buffer.
|
||||
*/
|
||||
virtual void UnlockVideoBuffer() {}
|
||||
|
||||
/**
|
||||
* Paint the window.
|
||||
*/
|
||||
virtual void Paint() {}
|
||||
|
||||
/**
|
||||
* Thread function for threaded drawing.
|
||||
*/
|
||||
virtual void PaintThread() {}
|
||||
|
||||
/**
|
||||
* Process any pending palette animation.
|
||||
*/
|
||||
virtual void CheckPaletteAnim() {}
|
||||
|
||||
/**
|
||||
* Run the game for a single tick, processing boththe game-tick and draw-tick.
|
||||
* @returns True if the driver should redraw the screen.
|
||||
*/
|
||||
bool Tick();
|
||||
|
||||
/**
|
||||
* Sleep till the next tick is about to happen.
|
||||
*/
|
||||
void SleepTillNextTick();
|
||||
|
||||
std::chrono::steady_clock::duration GetGameInterval()
|
||||
{
|
||||
return std::chrono::milliseconds(MILLISECONDS_PER_TICK);
|
||||
@@ -165,6 +209,10 @@ protected:
|
||||
{
|
||||
return std::chrono::microseconds(1000000 / _settings_client.gui.refresh_rate);
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point last_realtime_tick;
|
||||
std::chrono::steady_clock::time_point next_game_tick;
|
||||
std::chrono::steady_clock::time_point next_draw_tick;
|
||||
};
|
||||
|
||||
#endif /* VIDEO_VIDEO_DRIVER_HPP */
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "../rev.h"
|
||||
#include "../blitter/factory.hpp"
|
||||
#include "../network/network.h"
|
||||
#include "../core/geometry_func.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../core/random_func.hpp"
|
||||
#include "../texteff.hpp"
|
||||
@@ -49,7 +50,6 @@ static struct {
|
||||
HBITMAP dib_sect; ///< System bitmap object referencing our rendering buffer.
|
||||
void *buffer_bits; ///< Internal rendering buffer.
|
||||
HPALETTE gdi_palette; ///< Palette object for 8bpp blitter.
|
||||
RECT update_rect; ///< Current dirty rect.
|
||||
int width; ///< Width in pixels of our display surface.
|
||||
int height; ///< Height in pixels of our display surface.
|
||||
int width_org; ///< Original monitor resolution width, before we changed it.
|
||||
@@ -59,9 +59,7 @@ static struct {
|
||||
bool running; ///< Is the main loop running?
|
||||
} _wnd;
|
||||
|
||||
bool _force_full_redraw;
|
||||
bool _window_maximize;
|
||||
uint _display_hz;
|
||||
static Dimension _bck_resolution;
|
||||
DWORD _imm_props;
|
||||
|
||||
@@ -75,6 +73,8 @@ static std::condition_variable_any *_draw_signal = nullptr;
|
||||
static volatile bool _draw_continue;
|
||||
/** Local copy of the palette for use in the drawing thread. */
|
||||
static Palette _local_palette;
|
||||
/** Region of the screen that needs redrawing. */
|
||||
static Rect _dirty_rect;
|
||||
|
||||
static void MakePalette()
|
||||
{
|
||||
@@ -256,12 +256,10 @@ bool VideoDriver_Win32::MakeWindow(bool full_screen)
|
||||
settings.dmFields =
|
||||
DM_BITSPERPEL |
|
||||
DM_PELSWIDTH |
|
||||
DM_PELSHEIGHT |
|
||||
(_display_hz != 0 ? DM_DISPLAYFREQUENCY : 0);
|
||||
DM_PELSHEIGHT;
|
||||
settings.dmBitsPerPel = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
|
||||
settings.dmPelsWidth = _wnd.width_org;
|
||||
settings.dmPelsHeight = _wnd.height_org;
|
||||
settings.dmDisplayFrequency = _display_hz;
|
||||
|
||||
/* Check for 8 bpp support. */
|
||||
if (settings.dmBitsPerPel == 8 &&
|
||||
@@ -335,10 +333,24 @@ bool VideoDriver_Win32::MakeWindow(bool full_screen)
|
||||
}
|
||||
|
||||
/** Do palette animation and blit to the window. */
|
||||
static void PaintWindow(HDC dc)
|
||||
void VideoDriver_Win32::Paint()
|
||||
{
|
||||
PerformanceMeasurer framerate(PFE_VIDEO);
|
||||
|
||||
if (IsEmptyRect(_dirty_rect)) return;
|
||||
|
||||
/* Convert update region from logical to device coordinates. */
|
||||
POINT pt = {0, 0};
|
||||
ClientToScreen(_wnd.main_wnd, &pt);
|
||||
|
||||
RECT r = { _dirty_rect.left, _dirty_rect.top, _dirty_rect.right, _dirty_rect.bottom };
|
||||
OffsetRect(&r, pt.x, pt.y);
|
||||
|
||||
/* Create a device context that is clipped to the region we need to draw.
|
||||
* GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
|
||||
HRGN rgn = CreateRectRgnIndirect(&r);
|
||||
HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
|
||||
|
||||
HDC dc2 = CreateCompatibleDC(dc);
|
||||
HBITMAP old_bmp = (HBITMAP)SelectObject(dc2, _wnd.dib_sect);
|
||||
HPALETTE old_palette = SelectPalette(dc, _wnd.gdi_palette, FALSE);
|
||||
@@ -368,9 +380,13 @@ static void PaintWindow(HDC dc)
|
||||
SelectPalette(dc, old_palette, TRUE);
|
||||
SelectObject(dc2, old_bmp);
|
||||
DeleteDC(dc2);
|
||||
|
||||
ReleaseDC(_wnd.main_wnd, dc);
|
||||
|
||||
_dirty_rect = {};
|
||||
}
|
||||
|
||||
static void PaintWindowThread()
|
||||
void VideoDriver_Win32::PaintThread()
|
||||
{
|
||||
/* First tell the main thread we're started */
|
||||
std::unique_lock<std::recursive_mutex> lock(*_draw_mutex);
|
||||
@@ -380,21 +396,7 @@ static void PaintWindowThread()
|
||||
_draw_signal->wait(*_draw_mutex);
|
||||
|
||||
while (_draw_continue) {
|
||||
/* Convert update region from logical to device coordinates. */
|
||||
POINT pt = {0, 0};
|
||||
ClientToScreen(_wnd.main_wnd, &pt);
|
||||
OffsetRect(&_wnd.update_rect, pt.x, pt.y);
|
||||
|
||||
/* Create a device context that is clipped to the region we need to draw.
|
||||
* GetDCEx 'consumes' the update region, so we may not destroy it ourself. */
|
||||
HRGN rgn = CreateRectRgnIndirect(&_wnd.update_rect);
|
||||
HDC dc = GetDCEx(_wnd.main_wnd, rgn, DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | DCX_INTERSECTRGN);
|
||||
|
||||
PaintWindow(dc);
|
||||
|
||||
/* Clear update rect. */
|
||||
SetRectEmpty(&_wnd.update_rect);
|
||||
ReleaseDC(_wnd.main_wnd, dc);
|
||||
this->Paint();
|
||||
|
||||
/* Flush GDI buffer to ensure drawing here doesn't conflict with any GDI usage in the main WndProc. */
|
||||
GdiFlush();
|
||||
@@ -403,6 +405,11 @@ static void PaintWindowThread()
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void VideoDriver_Win32::PaintThreadThunk(VideoDriver_Win32 *drv)
|
||||
{
|
||||
drv->PaintThread();
|
||||
}
|
||||
|
||||
/** Forward key presses to the window system. */
|
||||
static LRESULT HandleCharMsg(uint keycode, WChar charcode)
|
||||
{
|
||||
@@ -606,7 +613,6 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
{
|
||||
static uint32 keycode = 0;
|
||||
static bool console = false;
|
||||
static bool in_sizemove = false;
|
||||
|
||||
switch (msg) {
|
||||
case WM_CREATE:
|
||||
@@ -615,32 +621,14 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
_imm_props = ImmGetProperty(GetKeyboardLayout(0), IGP_PROPERTY);
|
||||
break;
|
||||
|
||||
case WM_ENTERSIZEMOVE:
|
||||
in_sizemove = true;
|
||||
break;
|
||||
case WM_PAINT: {
|
||||
RECT r;
|
||||
GetUpdateRect(hwnd, &r, FALSE);
|
||||
static_cast<VideoDriver_Win32 *>(VideoDriver::GetInstance())->MakeDirty(r.left, r.top, r.right - r.left, r.bottom - r.top);
|
||||
|
||||
case WM_EXITSIZEMOVE:
|
||||
in_sizemove = false;
|
||||
break;
|
||||
|
||||
case WM_PAINT:
|
||||
if (!in_sizemove && _draw_mutex != nullptr && !HasModalProgress()) {
|
||||
/* Get the union of the old update rect and the new update rect. */
|
||||
RECT r;
|
||||
GetUpdateRect(hwnd, &r, FALSE);
|
||||
UnionRect(&_wnd.update_rect, &_wnd.update_rect, &r);
|
||||
|
||||
/* Mark the window as updated, otherwise Windows would send more WM_PAINT messages. */
|
||||
ValidateRect(hwnd, nullptr);
|
||||
_draw_signal->notify_one();
|
||||
} else {
|
||||
PAINTSTRUCT ps;
|
||||
|
||||
BeginPaint(hwnd, &ps);
|
||||
PaintWindow(ps.hdc);
|
||||
EndPaint(hwnd, &ps);
|
||||
}
|
||||
ValidateRect(hwnd, nullptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_PALETTECHANGED:
|
||||
if ((HWND)wParam == hwnd) return 0;
|
||||
@@ -653,7 +641,9 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
|
||||
SelectPalette(hDC, hOldPalette, TRUE);
|
||||
ReleaseDC(hwnd, hDC);
|
||||
if (nChanged != 0) InvalidateRect(hwnd, nullptr, FALSE);
|
||||
if (nChanged != 0) {
|
||||
static_cast<VideoDriver_Win32 *>(VideoDriver::GetInstance())->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1122,29 +1112,59 @@ void VideoDriver_Win32::Stop()
|
||||
|
||||
void VideoDriver_Win32::MakeDirty(int left, int top, int width, int height)
|
||||
{
|
||||
RECT r = { left, top, left + width, top + height };
|
||||
|
||||
InvalidateRect(_wnd.main_wnd, &r, FALSE);
|
||||
Rect r = {left, top, left + width, top + height};
|
||||
_dirty_rect = BoundingRect(_dirty_rect, r);
|
||||
}
|
||||
|
||||
static void CheckPaletteAnim()
|
||||
void VideoDriver_Win32::CheckPaletteAnim()
|
||||
{
|
||||
if (_cur_palette.count_dirty == 0) return;
|
||||
|
||||
_local_palette = _cur_palette;
|
||||
InvalidateRect(_wnd.main_wnd, nullptr, FALSE);
|
||||
this->MakeDirty(0, 0, _screen.width, _screen.height);
|
||||
}
|
||||
|
||||
void VideoDriver_Win32::InputLoop()
|
||||
{
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_CONTROL) < 0) != _invert_ctrl;
|
||||
_shift_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) != _invert_shift;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_shift_pressed)
|
||||
#else
|
||||
/* Speedup when pressing tab, except when using ALT+TAB
|
||||
* to switch to another application. */
|
||||
if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0)
|
||||
#endif
|
||||
{
|
||||
if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
|
||||
/* Determine which directional keys are down. */
|
||||
if (_wnd.has_focus) {
|
||||
_dirkeys =
|
||||
(GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
|
||||
(GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
|
||||
(GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
|
||||
(GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
|
||||
} else {
|
||||
_dirkeys = 0;
|
||||
}
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
}
|
||||
|
||||
void VideoDriver_Win32::MainLoop()
|
||||
{
|
||||
MSG mesg;
|
||||
auto cur_ticks = std::chrono::steady_clock::now();
|
||||
auto last_realtime_tick = cur_ticks;
|
||||
auto next_game_tick = cur_ticks;
|
||||
auto next_draw_tick = cur_ticks;
|
||||
|
||||
std::thread draw_thread;
|
||||
std::unique_lock<std::recursive_mutex> draw_lock;
|
||||
|
||||
if (_draw_threaded) {
|
||||
/* Initialise the mutex first, because that's the thing we *need*
|
||||
@@ -1157,15 +1177,15 @@ void VideoDriver_Win32::MainLoop()
|
||||
}
|
||||
|
||||
if (_draw_threaded) {
|
||||
draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
|
||||
this->draw_lock = std::unique_lock<std::recursive_mutex>(*_draw_mutex);
|
||||
|
||||
_draw_continue = true;
|
||||
_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &PaintWindowThread);
|
||||
_draw_threaded = StartNewThread(&draw_thread, "ottd:draw-win32", &VideoDriver_Win32::PaintThreadThunk, this);
|
||||
|
||||
/* Free the mutex if we won't be able to use it. */
|
||||
if (!_draw_threaded) {
|
||||
draw_lock.unlock();
|
||||
draw_lock.release();
|
||||
this->draw_lock.unlock();
|
||||
this->draw_lock.release();
|
||||
delete _draw_mutex;
|
||||
delete _draw_signal;
|
||||
_draw_mutex = nullptr;
|
||||
@@ -1180,108 +1200,27 @@ void VideoDriver_Win32::MainLoop()
|
||||
|
||||
_wnd.running = true;
|
||||
|
||||
CheckPaletteAnim();
|
||||
for (;;) {
|
||||
InteractiveRandom(); // randomness
|
||||
|
||||
while (PeekMessage(&mesg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
InteractiveRandom(); // randomness
|
||||
/* Convert key messages to char messages if we want text input. */
|
||||
if (EditBoxInGlobalFocus()) TranslateMessage(&mesg);
|
||||
DispatchMessage(&mesg);
|
||||
}
|
||||
if (_exit_game) break;
|
||||
|
||||
#if defined(_DEBUG)
|
||||
if (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0 &&
|
||||
#else
|
||||
/* Speed up using TAB, but disable for ALT+TAB of course */
|
||||
if (_wnd.has_focus && GetAsyncKeyState(VK_TAB) < 0 && GetAsyncKeyState(VK_MENU) >= 0 &&
|
||||
#endif
|
||||
!_networking && _game_mode != GM_MENU) {
|
||||
_fast_forward |= 2;
|
||||
} else if (_fast_forward & 2) {
|
||||
_fast_forward = 0;
|
||||
}
|
||||
/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
|
||||
GdiFlush();
|
||||
|
||||
cur_ticks = std::chrono::steady_clock::now();
|
||||
|
||||
/* If more than a millisecond has passed, increase the _realtime_tick. */
|
||||
if (cur_ticks - last_realtime_tick > std::chrono::milliseconds(1)) {
|
||||
auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(cur_ticks - last_realtime_tick);
|
||||
IncreaseRealtimeTick(delta.count());
|
||||
last_realtime_tick += delta;
|
||||
}
|
||||
|
||||
if (cur_ticks >= next_game_tick || (_fast_forward && !_pause_mode)) {
|
||||
if (_fast_forward && !_pause_mode) {
|
||||
next_game_tick = cur_ticks + this->GetGameInterval();
|
||||
if (this->Tick()) {
|
||||
if (_draw_mutex != nullptr && !HasModalProgress()) {
|
||||
_draw_signal->notify_one();
|
||||
} else {
|
||||
next_game_tick += this->GetGameInterval();
|
||||
/* Avoid next_game_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_game_tick < cur_ticks - ALLOWED_DRIFT * this->GetGameInterval()) next_game_tick = cur_ticks;
|
||||
}
|
||||
|
||||
/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
|
||||
GdiFlush();
|
||||
|
||||
/* The game loop is the part that can run asynchronously.
|
||||
* The rest except sleeping can't. */
|
||||
if (_draw_threaded) draw_lock.unlock();
|
||||
GameLoop();
|
||||
if (_draw_threaded) draw_lock.lock();
|
||||
GameLoopPaletteAnimations();
|
||||
}
|
||||
|
||||
/* Prevent drawing when switching mode, as windows can be removed when they should still appear. */
|
||||
if (cur_ticks >= next_draw_tick && (_switch_mode == SM_NONE || HasModalProgress())) {
|
||||
next_draw_tick += this->GetDrawInterval();
|
||||
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */
|
||||
if (next_draw_tick < cur_ticks - ALLOWED_DRIFT * this->GetDrawInterval()) next_draw_tick = cur_ticks;
|
||||
|
||||
bool old_ctrl_pressed = _ctrl_pressed;
|
||||
bool old_shift_pressed = _shift_pressed;
|
||||
|
||||
_ctrl_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_CONTROL) < 0) != _invert_ctrl;
|
||||
_shift_pressed = (_wnd.has_focus && GetAsyncKeyState(VK_SHIFT) < 0) != _invert_shift;
|
||||
|
||||
/* determine which directional keys are down */
|
||||
if (_wnd.has_focus) {
|
||||
_dirkeys =
|
||||
(GetAsyncKeyState(VK_LEFT) < 0 ? 1 : 0) +
|
||||
(GetAsyncKeyState(VK_UP) < 0 ? 2 : 0) +
|
||||
(GetAsyncKeyState(VK_RIGHT) < 0 ? 4 : 0) +
|
||||
(GetAsyncKeyState(VK_DOWN) < 0 ? 8 : 0);
|
||||
} else {
|
||||
_dirkeys = 0;
|
||||
}
|
||||
|
||||
if (old_ctrl_pressed != _ctrl_pressed) HandleCtrlChanged();
|
||||
if (old_shift_pressed != _shift_pressed) HandleShiftChanged();
|
||||
|
||||
if (_force_full_redraw) MarkWholeScreenDirty();
|
||||
|
||||
/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
|
||||
GdiFlush();
|
||||
|
||||
InputLoop();
|
||||
UpdateWindows();
|
||||
CheckPaletteAnim();
|
||||
}
|
||||
|
||||
/* If we are not in fast-forward, create some time between calls to ease up CPU usage. */
|
||||
if (!_fast_forward || _pause_mode) {
|
||||
/* See how much time there is till we have to process the next event, and try to hit that as close as possible. */
|
||||
auto next_tick = std::min(next_draw_tick, next_game_tick);
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (next_tick > now) {
|
||||
/* Flush GDI buffer to ensure we don't conflict with the drawing thread. */
|
||||
GdiFlush();
|
||||
|
||||
if (_draw_mutex != nullptr) draw_lock.unlock();
|
||||
std::this_thread::sleep_for(next_tick - now);
|
||||
if (_draw_mutex != nullptr) draw_lock.lock();
|
||||
this->Paint();
|
||||
}
|
||||
}
|
||||
this->SleepTillNextTick();
|
||||
}
|
||||
|
||||
if (_draw_threaded) {
|
||||
@@ -1289,8 +1228,8 @@ void VideoDriver_Win32::MainLoop()
|
||||
/* Sending signal if there is no thread blocked
|
||||
* is very valid and results in noop */
|
||||
_draw_signal->notify_all();
|
||||
if (draw_lock.owns_lock()) draw_lock.unlock();
|
||||
draw_lock.release();
|
||||
if (this->draw_lock.owns_lock()) this->draw_lock.unlock();
|
||||
this->draw_lock.release();
|
||||
draw_thread.join();
|
||||
|
||||
delete _draw_mutex;
|
||||
@@ -1391,3 +1330,14 @@ float VideoDriver_Win32::GetDPIScale()
|
||||
|
||||
return cur_dpi > 0 ? cur_dpi / 96.0f : 1.0f; // Default Windows DPI value is 96.
|
||||
}
|
||||
|
||||
bool VideoDriver_Win32::LockVideoBuffer()
|
||||
{
|
||||
if (_draw_threaded) this->draw_lock.lock();
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoDriver_Win32::UnlockVideoBuffer()
|
||||
{
|
||||
if (_draw_threaded) this->draw_lock.unlock();
|
||||
}
|
||||
|
||||
@@ -43,8 +43,18 @@ public:
|
||||
|
||||
protected:
|
||||
Dimension GetScreenSize() const override;
|
||||
|
||||
float GetDPIScale() override;
|
||||
void InputLoop() override;
|
||||
bool LockVideoBuffer() override;
|
||||
void UnlockVideoBuffer() override;
|
||||
void Paint() override;
|
||||
void PaintThread() override;
|
||||
void CheckPaletteAnim() override;
|
||||
|
||||
private:
|
||||
std::unique_lock<std::recursive_mutex> draw_lock;
|
||||
|
||||
static void PaintThreadThunk(VideoDriver_Win32 *drv);
|
||||
};
|
||||
|
||||
/** The factory for Windows' video driver. */
|
||||
|
||||
Reference in New Issue
Block a user