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

Return-To-The-Roots / s25client / 24634202623

19 Apr 2026 04:51PM UTC coverage: 50.201% (-0.2%) from 50.362%
24634202623

Pull #1890

github

web-flow
Merge a2d1e17a9 into 7b5704a17
Pull Request #1890: Add support for borderless Windows

167 of 641 new or added lines in 44 files covered. (26.05%)

27 existing lines in 10 files now uncovered.

23077 of 45969 relevant lines covered (50.2%)

45438.5 hits per line

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

63.4
/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::set<VideoMode> VideoDriverWrapper::ListVideoModes() const
×
432
{
433
    if(!videodriver)
×
NEW
434
        return {};
×
435

NEW
436
    const auto videoModes = videodriver->ListVideoModes();
×
NEW
437
    std::set<VideoMode> result;
×
438
    // Keep only modes larger than the minimum
439
    std::copy_if(videoModes.begin(), videoModes.end(), std::inserter(result, result.end()),
NEW
440
                 [](const auto& m) { return m.width >= MinWindowSize.width || m.height >= MinWindowSize.height; });
×
NEW
441
    return result;
×
442
}
443

NEW
444
std::set<VideoMode> VideoDriverWrapper::GetDefaultWindowSizes() const
×
445
{
446
    // clang-format off
NEW
447
    std::array defaultWindowSizes = {
×
448
      VideoMode(800, 600),
449
      VideoMode(1024, 768),
450
      VideoMode(1152, 648),
451
      VideoMode(1280, 720),
452
      VideoMode(1280, 800),
453
      VideoMode(1366, 768),
454
      VideoMode(1440, 810),
455
      VideoMode(1600, 900),
456
      VideoMode(1680, 1050),
457
      VideoMode(1920, 1080)
458
    };
459
    // clang-format on
NEW
460
    std::set<VideoMode> windowSizes = ListVideoModes();
×
NEW
461
    windowSizes.insert(defaultWindowSizes.begin(), defaultWindowSizes.end());
×
NEW
462
    return windowSizes;
×
463
}
464

465
bool VideoDriverWrapper::HasVSync() const
×
466
{
467
    return wglSwapIntervalEXT != nullptr;
×
468
}
469

470
void* VideoDriverWrapper::GetMapPointer() const
1✔
471
{
472
    if(!videodriver)
1✔
473
        return nullptr;
1✔
474

475
    return videodriver->GetMapPointer();
×
476
}
477

478
VideoMode VideoDriverWrapper::GetWindowSize() const
14✔
479
{
480
    // Always return at least MinWindowSize even if real window is smaller
481
    VideoMode windowSize = videodriver->GetWindowSize();
14✔
482
    windowSize.width = std::max(MinWindowSize.width, windowSize.width);
14✔
483
    windowSize.height = std::max(MinWindowSize.height, windowSize.height);
14✔
484
    return windowSize;
14✔
485
}
486

487
Extent VideoDriverWrapper::GetRenderSize() const
478✔
488
{
489
    return videodriver->GetRenderSize();
478✔
490
}
491

492
DisplayMode VideoDriverWrapper::GetDisplayMode() const
9✔
493
{
494
    return videodriver->GetDisplayMode();
9✔
495
}
496

497
float VideoDriverWrapper::getDpiScale() const
10✔
498
{
499
    return videodriver->getDpiScale();
10✔
500
}
501

502
const GuiScale& VideoDriverWrapper::getGuiScale() const
10✔
503
{
504
    return videodriver->getGuiScale();
10✔
505
}
506

507
void VideoDriverWrapper::setGuiScalePercent(unsigned percent)
×
508
{
509
    videodriver->setGuiScalePercent(percent);
×
510
}
×
511

512
GuiScaleRange VideoDriverWrapper::getGuiScaleRange() const
×
513
{
514
    return videodriver->getGuiScaleRange();
×
515
}
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