• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Return-To-The-Roots / s25client / 22797872430

07 Mar 2026 11:05AM UTC coverage: 50.156% (-0.2%) from 50.327%
22797872430

Pull #1890

github

web-flow
Merge 4597ccbf9 into d2a3730c9
Pull Request #1890: Add support for borderless Windows

155 of 626 new or added lines in 44 files covered. (24.76%)

26 existing lines in 9 files now uncovered.

23041 of 45939 relevant lines covered (50.16%)

44513.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.14
/libs/s25main/drivers/VideoDriverWrapper.cpp
1
// Copyright (C) 2005 - 2026 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "VideoDriverWrapper.h"
6
#include "FrameCounter.h"
7
#include "RTTR_Version.h"
8
#include "WindowManager.h"
9
#include "driver/VideoInterface.h"
10
#include "helpers/containerUtils.h"
11
#include "helpers/roundToNextPow2.h"
12
#include "mygettext/mygettext.h"
13
#include "ogl/DummyRenderer.h"
14
#include "ogl/OpenGLRenderer.h"
15
#include "openglCfg.hpp"
16
#include "s25util/Log.h"
17
#include "s25util/error.h"
18
#include <glad/glad.h>
19
#include <array>
20
#include <ctime>
21
#if !defined(NDEBUG) && defined(HAVE_MEMCHECK_H)
22
#    include <valgrind/memcheck.h>
23
#endif
24

25
#ifdef _WIN32
26
using SwapIntervalExt_t = BOOL APIENTRY(int);
27
#else
28
using SwapIntervalExt_t = int(int);
29
#endif
30

31
SwapIntervalExt_t* wglSwapIntervalEXT = nullptr;
32

33
VideoDriverWrapper::VideoDriverWrapper()
6✔
34
    : videodriver(nullptr, nullptr), renderer_(nullptr), enableMouseWarping(true), texture_current(0)
6✔
35
{}
6✔
36

37
VideoDriverWrapper::~VideoDriverWrapper()
6✔
38
{
39
    CleanUp();
6✔
40
    UnloadDriver();
6✔
41
}
6✔
42

43
bool VideoDriverWrapper::Initialize()
4✔
44
{
45
    if(!videodriver || !videodriver->Initialize())
4✔
46
    {
47
        UnloadDriver();
×
48
        return false;
×
49
    }
50

51
    LOG.write(_("Loaded video driver \"%1%\"\n")) % GetName();
4✔
52

53
    frameCtr_ = std::make_unique<FrameCounter>();
4✔
54
    frameLimiter_ = std::make_unique<FrameLimiter>();
4✔
55

56
    return true;
4✔
57
}
58

59
bool VideoDriverWrapper::LoadDriver(IVideoDriver* existingDriver)
3✔
60
{
61
    UnloadDriver();
3✔
62
    videodriver = Handle(existingDriver, [](IVideoDriver* p) { delete p; });
6✔
63
    return Initialize();
3✔
64
}
65

66
bool VideoDriverWrapper::LoadDriver(std::string& preference)
1✔
67
{
68
    UnloadDriver();
1✔
69
    // DLL laden
70
    if(!driver_wrapper.Load(drivers::DriverType::Video, preference))
1✔
71
        return false;
×
72

73
    auto createVideoInstance = driver_wrapper.GetFunction<CreateVideoInstance_t>("CreateVideoInstance");
1✔
74
    auto freeVideoInstance = driver_wrapper.GetFunction<FreeVideoInstance_t>("FreeVideoInstance");
1✔
75
    RTTR_Assert(createVideoInstance && freeVideoInstance);
1✔
76

77
    videodriver = Handle(createVideoInstance(&WINDOWMANAGER), freeVideoInstance);
1✔
78
    return Initialize();
1✔
79
}
80

81
void VideoDriverWrapper::UnloadDriver()
11✔
82
{
83
    videodriver.reset();
11✔
84
    driver_wrapper.Unload();
11✔
85
    renderer_.reset();
11✔
86
}
11✔
87

88
bool VideoDriverWrapper::CreateScreen(const VideoMode size, const DisplayMode displayMode)
5✔
89
{
90
    if(!videodriver)
5✔
91
    {
92
        s25util::fatal_error("No video driver selected!");
×
93
        return false;
×
94
    }
95

96
    if(!videodriver->CreateScreen(rttr::version::GetTitle(), size, displayMode))
5✔
97
    {
98
        s25util::fatal_error("Could not create window!");
×
99
        return false;
×
100
    }
101

102
    // DriverWrapper Initialisieren
103
    // Extensions laden
104
    if(!LoadAllExtensions())
5✔
105
    {
106
        s25util::fatal_error("Failed to initialize the OpenGL context!");
×
107
        return false;
×
108
    }
109

110
    RenewViewport();
5✔
111

112
    // Buffer swappen um den leeren Buffer darzustellen
113
    SwapBuffers();
5✔
114

115
    // WindowManager informieren
116
    WINDOWMANAGER.Msg_ScreenResize(GetRenderSize());
5✔
117

118
    return true;
5✔
119
}
120

121
bool VideoDriverWrapper::ResizeScreen(const VideoMode size, const DisplayMode displayMode)
3✔
122
{
123
    if(!videodriver)
3✔
124
    {
125
        s25util::fatal_error("No video driver selected!");
×
126
        return false;
×
127
    }
128

129
    const bool result = videodriver->ResizeScreen(size, displayMode);
3✔
130
#ifdef _WIN32
131
    if(videodriver->GetDisplayMode() != DisplayMode::Fullscreen)
132
    {
133
        // We cannot change the size of a maximized window. So restore it here
134
        WINDOWPLACEMENT wp;
135
        wp.length = sizeof(WINDOWPLACEMENT);
136

137
        if(GetWindowPlacement((HWND)GetMapPointer(), &wp) && ((wp.showCmd & SW_MAXIMIZE) == SW_MAXIMIZE))
138
            ShowWindow((HWND)GetMapPointer(), SW_RESTORE);
139
    }
140
#endif
141
    WINDOWMANAGER.WindowResized();
3✔
142
    return result;
3✔
143
}
144

145
bool VideoDriverWrapper::DestroyScreen()
2✔
146
{
147
    if(!videodriver)
2✔
148
    {
149
        s25util::fatal_error("No video driver selected!");
×
150
        return false;
×
151
    }
152

153
    LOG.write("Clearing textures: ");
2✔
154
    unsigned ladezeit = GetTickCount();
2✔
155
    CleanUp();
2✔
156
    LOG.write("Finished in %dms\n") % (GetTickCount() - ladezeit);
2✔
157

158
    // Videotreiber zurücksetzen
159
    videodriver->DestroyScreen();
2✔
160

161
    return true;
2✔
162
}
163

164
void VideoDriverWrapper::ShowErrorMessage(const std::string& title, const std::string& message)
×
165
{
166
    if(videodriver)
×
167
        videodriver->ShowErrorMessage(title, message);
×
168
}
×
169

170
void VideoDriverWrapper::setTargetFramerate(int target)
37✔
171
{
172
    frameLimiter_->setTargetFramerate(target);
37✔
173
    if(!setHwVSync(target == 0) && target == 0) // Fallback if no HW vsync but was requested
37✔
174
        frameLimiter_->setTargetFramerate(60);
37✔
175
}
37✔
176

177
unsigned VideoDriverWrapper::GetFPS() const
112✔
178
{
179
    return frameCtr_->getFrameRate();
112✔
180
}
181

182
void VideoDriverWrapper::CleanUp()
11✔
183
{
184
    if(!texture_list.empty())
11✔
185
    {
186
        glDeleteTextures(texture_list.size(), static_cast<const GLuint*>(texture_list.data()));
3✔
187
        texture_list.clear();
3✔
188
    }
189
}
11✔
190

191
unsigned VideoDriverWrapper::GenerateTexture()
59✔
192
{
193
    GLuint newTexture = 0;
59✔
194
    glGenTextures(1, &newTexture);
59✔
195
#if !defined(NDEBUG) && defined(HAVE_MEMCHECK_H)
196
    VALGRIND_MAKE_MEM_DEFINED(&newTexture, sizeof(newTexture));
197
#endif
198

199
    static_assert(sizeof(newTexture) == sizeof(texture_list[0]), "Unexpected texture size");
200
    if(newTexture)
59✔
201
        texture_list.push_back(newTexture);
59✔
202
    return newTexture;
59✔
203
}
204

205
void VideoDriverWrapper::BindTexture(unsigned t)
27,803✔
206
{
207
    if(t != texture_current)
27,803✔
208
    {
209
        texture_current = t;
13,977✔
210
        glBindTexture(GL_TEXTURE_2D, t);
13,977✔
211
    }
212
}
27,803✔
213

214
void VideoDriverWrapper::DeleteTexture(unsigned t)
75,736✔
215
{
216
    if(!t)
75,736✔
217
        return;
75,687✔
218
    if(t == texture_current)
49✔
219
        texture_current = 0;
10✔
220
    auto it = helpers::find(texture_list, t);
49✔
221
    if(it != texture_list.end())
49✔
222
    {
223
        glDeleteTextures(1, &t);
35✔
224
        texture_list.erase(it);
35✔
225
    }
226
}
227

228
KeyEvent VideoDriverWrapper::GetModKeyState() const
3✔
229
{
230
    if(videodriver)
3✔
231
        return videodriver->GetModKeyState();
3✔
232
    return KeyEvent();
×
233
}
234

235
void VideoDriverWrapper::SwapBuffers()
5✔
236
{
237
    if(!videodriver)
5✔
238
    {
239
        s25util::fatal_error("No video driver selected!");
×
240
        return;
×
241
    }
242
    frameLimiter_->sleepTillNextFrame(FrameCounter::clock::now());
5✔
243
    videodriver->SwapBuffers();
5✔
244
    FrameCounter::clock::time_point now = FrameCounter::clock::now();
5✔
245
    frameLimiter_->update(now);
5✔
246
    frameCtr_->update(now);
5✔
247
}
248

249
void VideoDriverWrapper::ClearScreen()
30✔
250
{
251
    glClear(GL_COLOR_BUFFER_BIT);
30✔
252
}
30✔
253

254
bool VideoDriverWrapper::Run()
×
255
{
256
    if(!videodriver)
×
257
    {
258
        s25util::fatal_error("No video driver selected!");
×
259
        return false;
×
260
    }
261

262
    return videodriver->MessageLoop();
×
263
}
264

265
Extent VideoDriverWrapper::calcPreferredTextureSize(const Extent& minSize) const
49✔
266
{
267
    return Extent(helpers::roundToNextPowerOfTwo(minSize.x), helpers::roundToNextPowerOfTwo(minSize.y));
49✔
268
}
269

270
bool VideoDriverWrapper::setHwVSync(bool enabled)
37✔
271
{
272
    if(!wglSwapIntervalEXT)
37✔
273
        return false;
37✔
274
    return wglSwapIntervalEXT(enabled ? 1 : 0) != 0;
×
275
}
276

277
void VideoDriverWrapper::RenewViewport()
14✔
278
{
279
    if(!videodriver->IsOpenGL() || !renderer_)
14✔
280
        return;
14✔
281

282
    const Extent renderSize = videodriver->GetRenderSize();
×
283
    const VideoMode windowSize = videodriver->GetWindowSize();
×
284

285
    // Set the viewport and scissor area to the entire window
286
    glViewport(0, 0, windowSize.width, windowSize.height);
×
287
    glScissor(0, 0, windowSize.width, windowSize.height);
×
288

289
    // Orthogonale Matrix erstellen
290
    glMatrixMode(GL_PROJECTION);
×
291
    glLoadIdentity();
×
292

293
    // 0,0 should be top left corner
294
    glOrtho(0, renderSize.x, renderSize.y, 0, -100, 100);
×
295

296
    glMatrixMode(GL_MODELVIEW);
×
297
    glLoadIdentity();
×
298

299
    // Depthbuffer und Colorbuffer einstellen
300
    glClearColor(0.0, 0.0, 0.0, 1.0);
×
301

302
    // Smooth - Shading aktivieren
303
    glShadeModel(GL_SMOOTH);
×
304

305
    glEnable(GL_ALPHA_TEST);
×
306
    glAlphaFunc(GL_GREATER, 0.0f);
×
307

308
    // Alphablending an
309
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
×
310
    glEnable(GL_BLEND);
×
311

312
    // Depthbuffer abschalten
313
    glDisable(GL_DEPTH_TEST);
×
314
    glDepthMask(GL_FALSE);
×
315

316
    // Texturen anstellen
317
    glEnable(GL_TEXTURE_2D);
×
318

319
    // Dither abstellen
320
    glDisable(GL_DITHER);
×
321

322
    // Scissoring aktivieren
323
    glEnable(GL_SCISSOR_TEST);
×
324

325
    // Nur obere Seite von Dreiecke rendern --> Performance
326
    glEnable(GL_CULL_FACE);
×
327

328
    glEnableClientState(GL_VERTEX_ARRAY);
×
329
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
×
330

331
    ClearScreen();
×
332
}
333

334
bool VideoDriverWrapper::LoadAllExtensions()
5✔
335
{
336
    if(videodriver->IsOpenGL())
5✔
337
        renderer_ = std::make_unique<OpenGLRenderer>();
×
338
    else
339
        renderer_ = std::make_unique<DummyRenderer>();
5✔
340
    if(!renderer_->initOpenGL(videodriver->GetLoaderFunction()))
5✔
341
        return false;
×
342
    LOG.write(_("OpenGL %1%.%2% supported\n")) % GLVersion.major % GLVersion.minor;
5✔
343
    if(GLVersion.major < RTTR_OGL_MAJOR || (GLVersion.major == RTTR_OGL_MAJOR && GLVersion.minor < RTTR_OGL_MINOR))
5✔
344
    {
345
        LOG.write(_("OpenGL %1% %2%.%3% is not supported. Try updating your GPU drivers or hardware!"))
×
346
          % ((RTTR_OGL_ES) ? "ES" : "") % RTTR_OGL_MAJOR % RTTR_OGL_MINOR;
×
347
        return false;
×
348
    }
349

350
// auf VSync-Extension testen
351
#ifdef _WIN32
352
    wglSwapIntervalEXT = reinterpret_cast<SwapIntervalExt_t*>(loadExtension("wglSwapIntervalEXT"));
353
#else
354
    wglSwapIntervalEXT = reinterpret_cast<SwapIntervalExt_t*>(loadExtension("glXSwapIntervalSGI"));
5✔
355
#endif
356

357
    return true;
5✔
358
}
359

360
unsigned VideoDriverWrapper::GetTickCount()
329✔
361
{
362
    if(!videodriver)
329✔
363
        return (unsigned)time(nullptr);
×
364

365
    return (unsigned)videodriver->GetTickCount();
329✔
366
}
367

368
std::string VideoDriverWrapper::GetName() const
10✔
369
{
370
    const char* name = (videodriver) ? videodriver->GetName() : nullptr;
10✔
371
    return (name) ? name : "";
10✔
372
}
373

374
/**
375
 *  lädt eine bestimmte DriverWrapper Extension-Funktion.
376
 *
377
 *  @param[in] extension Die Extension-Funktion
378
 *
379
 *  @return @p nullptr bei Fehler, Adresse der gewünschten Funktion bei Erfolg.
380
 */
381
void* VideoDriverWrapper::loadExtension(const std::string& extension)
5✔
382
{
383
    if(!videodriver)
5✔
384
    {
385
        s25util::fatal_error("No video driver selected!");
×
386
        return (nullptr);
×
387
    }
388

389
    return videodriver->GetLoaderFunction()(extension.c_str());
5✔
390
}
391

392
Position VideoDriverWrapper::GetMousePos() const
262✔
393
{
394
    if(!videodriver)
262✔
395
        return Position::Invalid();
×
396
    return videodriver->GetMousePos();
262✔
397
}
398

399
bool VideoDriverWrapper::IsLeftDown()
110✔
400
{
401
    if(!videodriver)
110✔
402
        return false;
×
403

404
    return videodriver->GetMouseStateL();
110✔
405
}
406

407
bool VideoDriverWrapper::IsRightDown()
×
408
{
409
    if(!videodriver)
×
410
        return false;
×
411

412
    return videodriver->GetMouseStateR();
×
413
}
414

415
bool VideoDriverWrapper::IsTouch()
85✔
416
{
417
    if(!videodriver)
85✔
418
        return false;
×
419

420
    return videodriver->IsTouchEvent();
85✔
421
}
422

423
void VideoDriverWrapper::SetMousePos(const Position& newPos)
26✔
424
{
425
    if(!videodriver || !enableMouseWarping)
26✔
426
        return;
×
427

428
    videodriver->SetMousePos(newPos);
26✔
429
}
430

NEW
431
std::vector<VideoMode> VideoDriverWrapper::ListVideoModes() const
×
432
{
433
    if(!videodriver)
×
NEW
434
        return {};
×
435

NEW
436
    auto videoModes = videodriver->ListVideoModes();
×
437
    // Remove everything below 800x600
NEW
438
    helpers::erase_if(videoModes,
×
NEW
439
                      [](const auto& m) { return m.width < MinWindowSize.width && m.height < MinWindowSize.height; });
×
NEW
440
    return videoModes;
×
441
}
442

NEW
443
std::vector<VideoMode> VideoDriverWrapper::GetDefaultWindowSizes() const
×
444
{
445
    std::vector<VideoMode> defaultWindowSizes = {
446
      VideoMode(800, 600),  VideoMode(1024, 768), VideoMode(1152, 648), VideoMode(1280, 720),  VideoMode(1280, 800),
NEW
447
      VideoMode(1366, 768), VideoMode(1440, 810), VideoMode(1600, 900), VideoMode(1680, 1050), VideoMode(1920, 1080)};
×
NEW
448
    std::vector<VideoMode> windowSizes = ListVideoModes();
×
NEW
449
    windowSizes.insert(windowSizes.end(), defaultWindowSizes.begin(), defaultWindowSizes.end());
×
NEW
450
    helpers::makeUnique(windowSizes);
×
NEW
451
    return windowSizes;
×
452
}
453

454
bool VideoDriverWrapper::HasVSync() const
×
455
{
456
    return wglSwapIntervalEXT != nullptr;
×
457
}
458

459
void* VideoDriverWrapper::GetMapPointer() const
1✔
460
{
461
    if(!videodriver)
1✔
462
        return nullptr;
1✔
463

464
    return videodriver->GetMapPointer();
×
465
}
466

467
VideoMode VideoDriverWrapper::GetWindowSize() const
14✔
468
{
469
    // Always return at least MinWindowSize even if real window is smaller
470
    VideoMode windowSize = videodriver->GetWindowSize();
14✔
471
    windowSize.width = std::max(MinWindowSize.width, windowSize.width);
14✔
472
    windowSize.height = std::max(MinWindowSize.height, windowSize.height);
14✔
473
    return windowSize;
14✔
474
}
475

476
Extent VideoDriverWrapper::GetRenderSize() const
478✔
477
{
478
    return videodriver->GetRenderSize();
478✔
479
}
480

481
DisplayMode VideoDriverWrapper::GetDisplayMode() const
9✔
482
{
483
    return videodriver->GetDisplayMode();
9✔
484
}
485

486
float VideoDriverWrapper::getDpiScale() const
10✔
487
{
488
    return videodriver->getDpiScale();
10✔
489
}
490

491
const GuiScale& VideoDriverWrapper::getGuiScale() const
10✔
492
{
493
    return videodriver->getGuiScale();
10✔
494
}
495

496
void VideoDriverWrapper::setGuiScalePercent(unsigned percent)
×
497
{
498
    videodriver->setGuiScalePercent(percent);
×
499
}
×
500

501
GuiScaleRange VideoDriverWrapper::getGuiScaleRange() const
×
502
{
503
    return videodriver->getGuiScaleRange();
×
504
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc