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

Return-To-The-Roots / s25client / 5735031298

pending completion
5735031298

Pull #1617

github

web-flow
Merge d291cd187 into a397539c1
Pull Request #1617: Show Popup on missing game files

11 of 11 new or added lines in 3 files covered. (100.0%)

21762 of 43119 relevant lines covered (50.47%)

32710.67 hits per line

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

10.66
/extras/videoDrivers/SDL2/VideoSDL2.cpp
1
// Copyright (C) 2005 - 2021 Settlers Freaks (sf-team at siedler25.org)
2
//
3
// SPDX-License-Identifier: GPL-2.0-or-later
4

5
#include "VideoSDL2.h"
6
#include "driver/Interface.h"
7
#include "driver/VideoDriverLoaderInterface.h"
8
#include "driver/VideoInterface.h"
9
#include "enum_cast.hpp"
10
#include "helpers/LSANUtils.h"
11
#include "helpers/containerUtils.h"
12
#include "icon.h"
13
#include "openglCfg.hpp"
14
#include <s25util/utf8.h>
15
#include <boost/nowide/iostream.hpp>
16
#include <SDL.h>
17
#include <algorithm>
18
#include <memory>
19

20
#ifdef _WIN32
21
#    include <boost/nowide/convert.hpp>
22
#    include <SDL_syswm.h>
23
#endif // _WIN32
24

25
#define CHECK_SDL(call)                 \
26
    do                                  \
27
    {                                   \
28
        if((call) == -1)                \
29
            PrintError(SDL_GetError()); \
30
    } while(false)
31

32
namespace {
33
template<typename T>
34
struct SDLMemoryDeleter
35
{
36
    void operator()(T* p) const { SDL_free(p); }
×
37
};
38

39
template<typename T>
40
using SDL_memory = std::unique_ptr<T, SDLMemoryDeleter<T>>;
41
} // namespace
42

43
IVideoDriver* CreateVideoInstance(VideoDriverLoaderInterface* CallBack)
1✔
44
{
45
    return new VideoSDL2(CallBack);
1✔
46
}
47

48
void FreeVideoInstance(IVideoDriver* driver)
1✔
49
{
50
    delete driver;
1✔
51
}
1✔
52

53
const char* GetDriverName()
5✔
54
{
55
    return "(SDL2) OpenGL via SDL2-Library";
5✔
56
}
57

58
VideoSDL2::VideoSDL2(VideoDriverLoaderInterface* CallBack) : VideoDriver(CallBack), window(nullptr), context(nullptr) {}
1✔
59

60
VideoSDL2::~VideoSDL2()
2✔
61
{
62
    CleanUp();
1✔
63
}
2✔
64

65
const char* VideoSDL2::GetName() const
2✔
66
{
67
    return GetDriverName();
2✔
68
}
69

70
bool VideoSDL2::Initialize()
1✔
71
{
72
    initialized = false;
1✔
73
    rttr::ScopedLeakDisabler _;
1✔
74
    if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
1✔
75
    {
76
        PrintError(SDL_GetError());
×
77
        return false;
×
78
    }
79

80
    initialized = true;
1✔
81
    return initialized;
1✔
82
}
83

84
void VideoSDL2::CleanUp()
1✔
85
{
86
    if(!initialized)
1✔
87
        return;
×
88

89
    if(context)
1✔
90
        SDL_GL_DeleteContext(context);
×
91
    if(window)
1✔
92
        SDL_DestroyWindow(window);
×
93
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
1✔
94
    SDL_Quit();
1✔
95
    initialized = false;
1✔
96
}
97

98
void VideoSDL2::UpdateCurrentSizes()
×
99
{
100
    int w, h, w2, h2;
101
    SDL_GetWindowSize(window, &w, &h);
×
102
    SDL_GL_GetDrawableSize(window, &w2, &h2);
×
103
    SetNewSize(VideoMode(w, h), Extent(w2, h2));
×
104
}
×
105

106
bool VideoSDL2::CreateScreen(const std::string& title, const VideoMode& size, bool fullscreen)
×
107
{
108
    if(!initialized)
×
109
        return false;
×
110

111
    // GL-Attributes
112
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, RTTR_OGL_MAJOR));
×
113
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, RTTR_OGL_MINOR));
×
114
    SDL_GLprofile profile;
115
    if((RTTR_OGL_ES))
116
        profile = SDL_GL_CONTEXT_PROFILE_ES;
117
    else if((RTTR_OGL_COMPAT))
118
        profile = SDL_GL_CONTEXT_PROFILE_COMPATIBILITY;
×
119
    else
120
        profile = SDL_GL_CONTEXT_PROFILE_CORE;
121
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile));
×
122

123
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8));
×
124
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8));
×
125
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8));
×
126
    CHECK_SDL(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1));
×
127

128
    int wndPos = SDL_WINDOWPOS_CENTERED;
×
129

130
    const auto requestedSize = fullscreen ? FindClosestVideoMode(size) : size;
×
131

132
    window = SDL_CreateWindow(title.c_str(), wndPos, wndPos, requestedSize.width, requestedSize.height,
×
133
                              SDL_WINDOW_OPENGL | (fullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_RESIZABLE));
134

135
    // Fallback to non-fullscreen
136
    if(!window && fullscreen)
×
137
    {
138
        window = SDL_CreateWindow(title.c_str(), wndPos, wndPos, requestedSize.width, requestedSize.height,
×
139
                                  SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
140
    }
141

142
    if(!window)
×
143
    {
144
        PrintError(SDL_GetError());
×
145
        return false;
×
146
    }
147

148
    isFullscreen_ = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
×
149
    UpdateCurrentSizes();
×
150

151
    if(!isFullscreen_)
×
152
        MoveWindowToCenter();
×
153

154
    SDL_Surface* iconSurf =
155
      SDL_CreateRGBSurfaceFrom(image.data(), 48, 48, 32, 48 * 4, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
×
156
    if(iconSurf)
×
157
    {
158
        SDL_SetWindowIcon(window, iconSurf);
×
159
        SDL_FreeSurface(iconSurf);
×
160
    } else
161
        PrintError(SDL_GetError());
×
162

163
    context = SDL_GL_CreateContext(window);
×
164

165
#ifdef _WIN32
166
    SetWindowTextW(GetConsoleWindow(), boost::nowide::widen(title).c_str());
167
#endif
168

169
    std::fill(keyboard.begin(), keyboard.end(), false);
×
170

171
    SDL_ShowCursor(0);
×
172

173
    return true;
×
174
}
175

176
bool VideoSDL2::ResizeScreen(const VideoMode& newSize, bool fullscreen)
×
177
{
178
    if(!initialized)
×
179
        return false;
×
180

181
    if(isFullscreen_ != fullscreen)
×
182
    {
183
        SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN : 0);
×
184
        isFullscreen_ = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
×
185
        if(!isFullscreen_)
×
186
        {
187
#if SDL_VERSION_ATLEAST(2, 0, 5)
188
            SDL_SetWindowResizable(window, SDL_TRUE);
×
189
#endif
190
            MoveWindowToCenter();
×
191
        }
192
    }
193

194
    if(newSize != GetWindowSize())
×
195
    {
196
        if(isFullscreen_)
×
197
        {
198
            auto const targetMode = FindClosestVideoMode(newSize);
×
199
            SDL_DisplayMode target;
200
            target.w = targetMode.width;
×
201
            target.h = targetMode.height;
×
202
            target.format = 0;           // don't care
×
203
            target.refresh_rate = 0;     // don't care
×
204
            target.driverdata = nullptr; // initialize to 0
×
205
            // Explicitly change the window size to avoid a bug with SDL reporting the wrong size until alt+tab
206
            SDL_SetWindowSize(window, target.w, target.h);
×
207
            if(SDL_SetWindowDisplayMode(window, &target) < 0)
×
208
            {
209
                PrintError(SDL_GetError());
×
210
                return false;
×
211
            }
212
        } else
213
        {
214
            SDL_SetWindowSize(window, newSize.width, newSize.height);
×
215
        }
216
        UpdateCurrentSizes();
×
217
    }
218
    return true;
×
219
}
220

221
void VideoSDL2::PrintError(const std::string& msg) const
×
222
{
223
    boost::nowide::cerr << msg << std::endl;
×
224
}
×
225

226
void VideoSDL2::ShowErrorMessage(const std::string& title, const std::string& message)
×
227
{
228
    // window==nullptr is okay too ("no parent")
229
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title.c_str(), message.c_str(), window);
×
230
}
×
231

232
void VideoSDL2::HandlePaste()
×
233
{
234
    if(!SDL_HasClipboardText())
×
235
        return;
×
236

237
    SDL_memory<char> text(SDL_GetClipboardText());
×
238
    if(!text || *text == '\0') // empty string indicates error
×
239
        PrintError(text ? SDL_GetError() : "Paste failed.");
×
240

241
    KeyEvent ke = {KeyType::Char, 0, false, false, false};
×
242
    for(const char32_t c : s25util::utf8to32(text.get()))
×
243
    {
244
        ke.c = static_cast<unsigned>(c);
×
245
        CallBack->Msg_KeyDown(ke);
×
246
    }
247
}
248

249
void VideoSDL2::DestroyScreen()
×
250
{
251
    CleanUp();
×
252
}
×
253

254
bool VideoSDL2::SwapBuffers()
×
255
{
256
    SDL_GL_SwapWindow(window);
×
257
    return true;
×
258
}
259

260
bool VideoSDL2::MessageLoop()
×
261
{
262
    SDL_Event ev;
263
    while(SDL_PollEvent(&ev))
×
264
    {
265
        switch(ev.type)
×
266
        {
267
            default: break;
×
268

269
            case SDL_QUIT: return false;
×
270
            case SDL_WINDOWEVENT:
×
271
            {
272
                switch(ev.window.event)
×
273
                {
274
                    case SDL_WINDOWEVENT_RESIZED:
×
275
                    {
276
                        isFullscreen_ = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN) != 0;
×
277
                        VideoMode newSize(ev.window.data1, ev.window.data2);
×
278
                        if(newSize != GetWindowSize())
×
279
                        {
280
                            UpdateCurrentSizes();
×
281
                            CallBack->WindowResized();
×
282
                        }
283
                    }
284
                    break;
×
285
                }
286
            }
287
            break;
×
288

289
            case SDL_KEYDOWN:
×
290
            {
291
                KeyEvent ke = {KeyType::Invalid, 0, false, false, false};
×
292

293
                switch(ev.key.keysym.sym)
×
294
                {
295
                    default:
×
296
                    {
297
                        // Die 12 F-Tasten
298
                        if(ev.key.keysym.sym >= SDLK_F1 && ev.key.keysym.sym <= SDLK_F12)
×
299
                            ke.kt = static_cast<KeyType>(rttr::enum_cast(KeyType::F1) + ev.key.keysym.sym - SDLK_F1);
×
300
                    }
301
                    break;
×
302
                    case SDLK_RETURN: ke.kt = KeyType::Return; break;
×
303
                    case SDLK_SPACE: ke.kt = KeyType::Space; break;
×
304
                    case SDLK_LEFT: ke.kt = KeyType::Left; break;
×
305
                    case SDLK_RIGHT: ke.kt = KeyType::Right; break;
×
306
                    case SDLK_UP: ke.kt = KeyType::Up; break;
×
307
                    case SDLK_DOWN: ke.kt = KeyType::Down; break;
×
308
                    case SDLK_BACKSPACE: ke.kt = KeyType::Backspace; break;
×
309
                    case SDLK_DELETE: ke.kt = KeyType::Delete; break;
×
310
                    case SDLK_LSHIFT:
×
311
                    case SDLK_RSHIFT: ke.kt = KeyType::Shift; break;
×
312
                    case SDLK_TAB: ke.kt = KeyType::Tab; break;
×
313
                    case SDLK_HOME: ke.kt = KeyType::Home; break;
×
314
                    case SDLK_END: ke.kt = KeyType::End; break;
×
315
                    case SDLK_ESCAPE: ke.kt = KeyType::Escape; break;
×
316
                    case SDLK_PRINTSCREEN: ke.kt = KeyType::Print; break;
×
317
                    // case SDLK_BACKQUOTE: ev.key.keysym.scancode = '^'; break;
318
                    case SDLK_v:
×
319
                        if(SDL_GetModState() & KMOD_CTRL)
×
320
                        {
321
                            HandlePaste();
×
322
                            continue;
×
323
                        }
324
                        break;
×
325
                }
326

327
                if(ke.kt == KeyType::Invalid)
×
328
                    break;
×
329

330
                /// Strg, Alt, usw gedrückt?
331
                if(ev.key.keysym.mod & KMOD_CTRL)
×
332
                    ke.ctrl = true;
×
333
                if(ev.key.keysym.mod & KMOD_SHIFT)
×
334
                    ke.shift = true;
×
335
                if(ev.key.keysym.mod & KMOD_ALT)
×
336
                    ke.alt = true;
×
337

338
                CallBack->Msg_KeyDown(ke);
×
339
            }
340
            break;
×
341
            case SDL_TEXTINPUT:
×
342
            {
343
                const std::u32string text = s25util::utf8to32(ev.text.text);
×
344
                SDL_Keymod mod = SDL_GetModState();
×
345
                KeyEvent ke = {KeyType::Char, 0, (mod & KMOD_CTRL) != 0, (mod & KMOD_SHIFT) != 0,
×
346
                               (mod & KMOD_ALT) != 0};
×
347
                for(char32_t c : text)
×
348
                {
349
                    ke.c = static_cast<unsigned>(c);
×
350
                    CallBack->Msg_KeyDown(ke);
×
351
                }
352
                break;
×
353
            }
354
            case SDL_MOUSEBUTTONDOWN:
×
355
                mouse_xy.pos = Position(ev.button.x, ev.button.y);
×
356

357
                if(/*!mouse_xy.ldown && */ ev.button.button == SDL_BUTTON_LEFT)
×
358
                {
359
                    mouse_xy.ldown = true;
×
360
                    CallBack->Msg_LeftDown(mouse_xy);
×
361
                }
362
                if(/*!mouse_xy.rdown &&*/ ev.button.button == SDL_BUTTON_RIGHT)
×
363
                {
364
                    mouse_xy.rdown = true;
×
365
                    CallBack->Msg_RightDown(mouse_xy);
×
366
                }
367
                break;
×
368
            case SDL_MOUSEBUTTONUP:
×
369
                mouse_xy.pos = Position(ev.button.x, ev.button.y);
×
370

371
                if(/*mouse_xy.ldown &&*/ ev.button.button == SDL_BUTTON_LEFT)
×
372
                {
373
                    mouse_xy.ldown = false;
×
374
                    CallBack->Msg_LeftUp(mouse_xy);
×
375
                }
376
                if(/*mouse_xy.rdown &&*/ ev.button.button == SDL_BUTTON_RIGHT)
×
377
                {
378
                    mouse_xy.rdown = false;
×
379
                    CallBack->Msg_RightUp(mouse_xy);
×
380
                }
381
                break;
×
382
            case SDL_MOUSEWHEEL:
×
383
            {
384
                int y = ev.wheel.y;
×
385
#if SDL_VERSION_ATLEAST(2, 0, 4)
386
                if(ev.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
×
387
                    y = -y;
×
388
#endif
389
                if(y > 0)
×
390
                    CallBack->Msg_WheelUp(mouse_xy);
×
391
                else if(y < 0)
×
392
                    CallBack->Msg_WheelDown(mouse_xy);
×
393
            }
394
            break;
×
395
            case SDL_MOUSEMOTION:
×
396
                mouse_xy.pos = Position(ev.motion.x, ev.motion.y);
×
397
                CallBack->Msg_MouseMove(mouse_xy);
×
398
                break;
×
399
        }
400
    }
401

402
    return true;
×
403
}
404

405
unsigned long VideoSDL2::GetTickCount() const
×
406
{
407
    return SDL_GetTicks();
×
408
}
409

410
void VideoSDL2::ListVideoModes(std::vector<VideoMode>& video_modes) const
×
411
{
412
    int display = SDL_GetWindowDisplayIndex(window);
×
413
    if(display < 0)
×
414
        display = 0;
×
415
    for(int i = SDL_GetNumDisplayModes(display) - 1; i >= 0; --i)
×
416
    {
417
        SDL_DisplayMode mode;
418
        if(SDL_GetDisplayMode(display, i, &mode) != 0)
×
419
            PrintError(SDL_GetError());
×
420
        else
421
        {
422
            VideoMode vm(mode.w, mode.h);
×
423
            if(!helpers::contains(video_modes, vm))
×
424
                video_modes.push_back(vm);
×
425
        }
426
    }
427
}
×
428

429
OpenGL_Loader_Proc VideoSDL2::GetLoaderFunction() const
×
430
{
431
    return SDL_GL_GetProcAddress;
×
432
}
433

434
void VideoSDL2::SetMousePos(Position pos)
×
435
{
436
    mouse_xy.pos = pos;
×
437
    SDL_WarpMouseInWindow(window, pos.x, pos.y);
×
438
}
×
439

440
KeyEvent VideoSDL2::GetModKeyState() const
×
441
{
442
    const SDL_Keymod modifiers = SDL_GetModState();
×
443
    const KeyEvent ke = {KeyType::Invalid, 0, ((modifiers & KMOD_CTRL) != 0), ((modifiers & KMOD_SHIFT) != 0),
×
444
                         ((modifiers & KMOD_ALT) != 0)};
×
445
    return ke;
×
446
}
447

448
void* VideoSDL2::GetMapPointer() const
×
449
{
450
#ifdef WIN32
451
    SDL_SysWMinfo wmInfo;
452
    SDL_VERSION(&wmInfo.version);
453
    SDL_GetWindowWMInfo(window, &wmInfo);
454
    // return (void*)wmInfo.info.win.window;
455
    return (void*)wmInfo.info.win.window;
456
#else
457
    return nullptr;
×
458
#endif
459
}
460

461
void VideoSDL2::MoveWindowToCenter()
×
462
{
463
    SDL_Rect usableBounds;
464
#if SDL_VERSION_ATLEAST(2, 0, 5)
465
    CHECK_SDL(SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(window), &usableBounds));
×
466
    int top, left, bottom, right;
467
    CHECK_SDL(SDL_GetWindowBordersSize(window, &top, &left, &bottom, &right));
×
468
    usableBounds.w -= left + right;
×
469
    usableBounds.h -= top + bottom;
×
470
#else
471
    CHECK_SDL(SDL_GetDisplayBounds(SDL_GetWindowDisplayIndex(window), &usableBounds));
472
    // rough estimates
473
    usableBounds.w -= 10;
474
    usableBounds.h -= 30;
475
#endif
476
    if(usableBounds.w < GetWindowSize().width || usableBounds.h < GetWindowSize().height)
×
477
    {
478
        SDL_SetWindowSize(window, usableBounds.w, usableBounds.h);
×
479
        UpdateCurrentSizes();
×
480
    }
481
    SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
×
482
}
×
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