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

Return-To-The-Roots / s25client / 15651837780

14 Jun 2025 12:06PM UTC coverage: 50.443% (-0.03%) from 50.476%
15651837780

Pull #1534

github

web-flow
Merge 4685d273f into 19c9b91c8
Pull Request #1534: Portrait support

56 of 155 new or added lines in 18 files covered. (36.13%)

23 existing lines in 5 files now uncovered.

22500 of 44605 relevant lines covered (50.44%)

35606.31 hits per line

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

0.0
/libs/s25main/desktops/dskOptions.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 "dskOptions.h"
6
#include "GlobalGameSettings.h"
7
#include "GlobalVars.h"
8
#include "Loader.h"
9
#include "MusicPlayer.h"
10
#include "Settings.h"
11
#include "WindowManager.h"
12
#include "controls/ctrlComboBox.h"
13
#include "controls/ctrlEdit.h"
14
#include "controls/ctrlGroup.h"
15
#include "controls/ctrlImageButton.h"
16
#include "controls/ctrlOptionGroup.h"
17
#include "controls/ctrlProgress.h"
18
#include "driver/VideoDriver.h"
19
#include "drivers/AudioDriverWrapper.h"
20
#include "drivers/VideoDriverWrapper.h"
21
#include "dskMainMenu.h"
22
#include "helpers/containerUtils.h"
23
#include "helpers/format.hpp"
24
#include "helpers/mathFuncs.h"
25
#include "helpers/toString.h"
26
#include "ingameWindows/iwAddons.h"
27
#include "ingameWindows/iwMsgbox.h"
28
#include "ingameWindows/iwMusicPlayer.h"
29
#include "ingameWindows/iwTextfile.h"
30
#include "languages.h"
31
#include "ogl/FontStyle.h"
32
#include "gameData/PortraitConsts.h"
33
#include "s25util/StringConversion.h"
34
#include "s25util/colors.h"
35
#include <mygettext/mygettext.h>
36
#include <sstream>
37

38
namespace {
39
enum
40
{
41
    ID_btBack = dskMenuBase::ID_FIRST_FREE,
42
    ID_txtOptions,
43
    ID_btAddons,
44
    ID_grpOptions,
45
    ID_btGeneral,
46
    ID_btGraphics,
47
    ID_btSound,
48
    ID_grpGeneral,
49
    ID_grpGraphics,
50
    ID_grpSound,
51
    ID_txtName,
52
    ID_edtName,
53
    ID_txtLanguage,
54
    ID_cbLanguage,
55
    ID_txtKeyboardLayout,
56
    ID_btKeyboardLayout,
57
    ID_txtPort,
58
    ID_edtPort,
59
    ID_txtIpv6,
60
    ID_grpIpv6,
61
    ID_txtProxy,
62
    ID_edtProxy,
63
    ID_edtProxyPort,
64
    ID_txtProxyType,
65
    ID_cbProxyType,
66
    ID_txtDebugData,
67
    ID_grpDebugData,
68
    ID_txtUPNP,
69
    ID_grpUPNP,
70
    ID_txtInvertScroll,
71
    ID_grpInvertScroll,
72
    ID_txtSmartCursor,
73
    ID_grpSmartCursor,
74
    ID_txtGFInfo,
75
    ID_grpGFInfo,
76
    ID_txtResolution,
77
    ID_cbResolution,
78
    ID_txtFullscreen,
79
    ID_grpFullscreen,
80
    ID_txtFramerate,
81
    ID_cbFramerate,
82
    ID_txtVBO,
83
    ID_grpVBO,
84
    ID_txtVideoDriver,
85
    ID_cbVideoDriver,
86
    ID_txtOptTextures,
87
    ID_grpOptTextures,
88
    ID_txtGuiScale,
89
    ID_cbGuiScale,
90
    ID_txtAudioDriver,
91
    ID_cbAudioDriver,
92
    ID_txtMusic,
93
    ID_grpMusic,
94
    ID_pgMusicVol,
95
    ID_txtEffects,
96
    ID_grpEffects,
97
    ID_pgEffectsVol,
98
    ID_btMusicPlayer,
99
    ID_txtCommonPortrait,
100
    ID_btCommonPortrait,
101
    ID_cbCommonPortrait,
102
};
103
// Use these as IDs in dedicated groups
104
constexpr auto ID_btOn = 1;
105
constexpr auto ID_btOff = 0;
106
// Special case: Submit debug data uses "2" for "ask user" and "0" for "unset, ask at start"
107
constexpr auto ID_btSubmitDebugOn = 1;
108
constexpr auto ID_btSubmitDebugAsk = 2;
109
} // namespace
110

111
static VideoMode getAspectRatio(const VideoMode& vm)
×
112
{
113
    // First some a bit off values where the aspect ratio is defined by convention
114
    if(vm == VideoMode(1360, 1024))
×
115
        return VideoMode(4, 3);
×
116
    else if(vm == VideoMode(1360, 768) || vm == VideoMode(1366, 768))
×
117
        return VideoMode(16, 9);
×
118

119
    // Normally Aspect ration is simply width/height as integer numbers (e.g. 4:3)
120
    int divisor = helpers::gcd(vm.width, vm.height);
×
121
    VideoMode ratio(vm.width / divisor, vm.height / divisor);
×
122
    // But there are some special cases:
123
    if(ratio == VideoMode(8, 5))
×
124
        return VideoMode(16, 10);
×
125
    else if(ratio == VideoMode(5, 3))
×
126
        return VideoMode(15, 9);
×
127
    else
128
        return ratio;
×
129
}
130

131
dskOptions::dskOptions() : Desktop(LOADER.GetImageN("setup013", 0))
×
132
{
133
    // Zurück
134
    AddTextButton(ID_btBack, DrawPoint(300, 550), Extent(200, 22), TextureColor::Red1, _("Back"), NormalFont);
×
135

136
    // "Optionen"
137
    AddText(ID_txtOptions, DrawPoint(400, 10), _("Options"), COLOR_YELLOW, FontStyle::CENTER, LargeFont);
×
138

139
    ctrlOptionGroup* optiongroup = AddOptionGroup(ID_grpOptions, GroupSelectType::Check);
×
140

141
    AddTextButton(ID_btAddons, DrawPoint(520, 550), Extent(200, 22), TextureColor::Green2, _("Addons"), NormalFont);
×
142

143
    // "Allgemein"
144
    optiongroup->AddTextButton(ID_btGeneral, DrawPoint(80, 510), Extent(200, 22), TextureColor::Green2, _("Common"),
×
145
                               NormalFont);
×
146
    // "Grafik"
147
    optiongroup->AddTextButton(ID_btGraphics, DrawPoint(300, 510), Extent(200, 22), TextureColor::Green2, _("Graphics"),
×
148
                               NormalFont);
×
149
    // "Sound"
150
    optiongroup->AddTextButton(ID_btSound, DrawPoint(520, 510), Extent(200, 22), TextureColor::Green2, _("Sound/Music"),
×
151
                               NormalFont);
×
152

153
    ctrlGroup* groupAllgemein = AddGroup(ID_grpGeneral);
×
154
    ctrlGroup* groupGrafik = AddGroup(ID_grpGraphics);
×
155
    ctrlGroup* groupSound = AddGroup(ID_grpSound);
×
156
    ctrlComboBox* combo;
157

158
    // Allgemein
159
    // {
160

161
    DrawPoint curPos = DrawPoint(80, 80);
×
162
    using Offset = DrawPoint;
163
    constexpr Offset ctrlOffset(200, -5);                       // Offset of control to its description text
×
164
    constexpr Offset ctrlOffset2 = ctrlOffset + Offset(200, 0); // Offset of 2nd control to its description text
×
165
    constexpr Extent ctrlSize(190, 22);
×
166
    constexpr Extent ctrlSizeLarge = ctrlSize + Extent(ctrlOffset2 - ctrlOffset);
×
167

168
    groupAllgemein->AddText(ID_txtName, curPos, _("Name in Game:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
169
    ctrlEdit* name =
170
      groupAllgemein->AddEdit(ID_edtName, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 15);
×
171
    name->SetText(SETTINGS.lobby.name);
×
172
    curPos.y += 30;
×
173

NEW
174
    const auto& currentPortrait = Portraits[SETTINGS.lobby.portraitIndex];
×
NEW
175
    groupAllgemein->AddImageButton(ID_btCommonPortrait, DrawPoint(500, curPos.y - 5), Extent(40, 54),
×
176
                                   TextureColor::Grey,
NEW
177
                                   LOADER.GetImageN(currentPortrait.resourceId, currentPortrait.resourceIndex));
×
178

NEW
179
    groupAllgemein->AddText(ID_txtCommonPortrait, DrawPoint(80, curPos.y), _("Portrait:"), COLOR_YELLOW, FontStyle{},
×
NEW
180
                            NormalFont);
×
NEW
181
    combo = groupAllgemein->AddComboBox(ID_cbCommonPortrait, DrawPoint(280, curPos.y - 5), Extent(190, 20),
×
NEW
182
                                        TextureColor::Grey, NormalFont, 100);
×
183

NEW
184
    for(unsigned i = 0; i < Portraits.size(); ++i)
×
185
    {
NEW
186
        combo->AddString(_(Portraits[i].name));
×
NEW
187
        if(SETTINGS.lobby.portraitIndex == i)
×
188
        {
NEW
189
            combo->SetSelection(i);
×
190
        }
191
    }
192

NEW
193
    curPos.y += 30;
×
194

UNCOV
195
    groupAllgemein->AddText(ID_txtLanguage, curPos, _("Language:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
196
    combo =
197
      groupAllgemein->AddComboBox(ID_cbLanguage, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 100);
×
198

199
    bool selected = false;
×
200
    for(unsigned i = 0; i < LANGUAGES.size(); ++i)
×
201
    {
202
        const Language& l = LANGUAGES.getLanguage(i);
×
203

204
        combo->AddString(_(l.name));
×
205
        if(SETTINGS.language.language == l.code)
×
206
        {
207
            combo->SetSelection(static_cast<unsigned short>(i));
×
208
            selected = true;
×
209
        }
210
    }
211
    if(!selected)
×
212
        combo->SetSelection(0);
×
213
    curPos.y += 30;
×
214

215
    groupAllgemein->AddTextButton(ID_btKeyboardLayout, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey,
×
216
                                  _("Keyboard layout"), NormalFont);
×
217
    curPos.y += 40;
×
218

219
    groupAllgemein->AddText(ID_txtPort, curPos, _("Local Port:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
220
    ctrlEdit* edtPort =
221
      groupAllgemein->AddEdit(ID_edtPort, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 15);
×
222
    edtPort->SetNumberOnly(true);
×
223
    edtPort->SetText(SETTINGS.server.localPort);
×
224
    curPos.y += 30;
×
225

226
    // IPv4/6
227
    groupAllgemein->AddText(ID_txtIpv6, curPos, _("Use IPv6:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
228

229
    ctrlOptionGroup* ipv6 = groupAllgemein->AddOptionGroup(ID_grpIpv6, GroupSelectType::Check);
×
230
    ipv6->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("IPv6"), NormalFont);
×
231
    ipv6->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("IPv4"), NormalFont);
×
232
    ipv6->SetSelection(SETTINGS.server.ipv6);
×
233
    // ipv6-feld ggf (de-)aktivieren
234
    ipv6->GetCtrl<ctrlButton>(1)->SetEnabled(SETTINGS.proxy.type != ProxyType::Socks5); //-V807
×
235
    curPos.y += 40;
×
236

237
    // Proxyserver
238
    groupAllgemein->AddText(ID_txtProxy, curPos, _("Proxyserver:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
239
    ctrlEdit* proxy =
240
      groupAllgemein->AddEdit(ID_edtProxy, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont);
×
241
    proxy->SetText(SETTINGS.proxy.hostname);
×
242
    proxy =
243
      groupAllgemein->AddEdit(ID_edtProxyPort, curPos + ctrlOffset2, Extent(50, 22), TextureColor::Grey, NormalFont, 5);
×
244
    proxy->SetNumberOnly(true);
×
245
    proxy->SetText(SETTINGS.proxy.port);
×
246
    curPos.y += 30;
×
247

248
    groupAllgemein->AddText(ID_txtUPNP, curPos, _("Use UPnP"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
249
    ctrlOptionGroup* upnp = groupAllgemein->AddOptionGroup(ID_grpUPNP, GroupSelectType::Check);
×
250
    upnp->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont);
×
251
    upnp->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont);
×
252
    upnp->SetSelection(SETTINGS.global.use_upnp);
×
253
    curPos.y += 30;
×
254

255
    // Proxytyp
256
    groupAllgemein->AddText(ID_txtProxyType, curPos, _("Proxytyp:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
257
    combo = groupAllgemein->AddComboBox(ID_cbProxyType, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey,
×
258
                                        NormalFont, 100);
×
259
    combo->AddString(_("No Proxy"));
×
260
    combo->AddString(_("Socks v4"));
×
261
    // TODO: not implemented
262
    // combo->AddString(_("Socks v5"));
263

264
    switch(SETTINGS.proxy.type)
×
265
    {
266
        default: combo->SetSelection(0); break;
×
267
        case ProxyType::Socks4: combo->SetSelection(1); break;
×
268
        case ProxyType::Socks5: combo->SetSelection(2); break;
×
269
    }
270
    curPos.y += 50;
×
271

272
    groupAllgemein->AddText(ID_txtInvertScroll, curPos, _("Invert Mouse Pan:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
273
    ctrlOptionGroup* invertScroll = groupAllgemein->AddOptionGroup(ID_grpInvertScroll, GroupSelectType::Check);
×
274
    invertScroll->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont,
×
275
                                _("Map moves in the opposite direction the mouse is moved when scrolling/panning."));
276
    invertScroll->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont,
×
277
                                _("Map moves in the same direction the mouse is moved when scrolling/panning."));
278
    invertScroll->SetSelection(SETTINGS.interface.invertMouse);
×
279
    curPos.y += 30;
×
280

281
    groupAllgemein->AddText(ID_txtSmartCursor, curPos, _("Smart Cursor"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
282
    ctrlOptionGroup* smartCursor = groupAllgemein->AddOptionGroup(ID_grpSmartCursor, GroupSelectType::Check);
×
283
    smartCursor->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont,
×
284
                               _("Place cursor on default button for new dialogs / action windows (default)"));
285
    smartCursor->AddTextButton(
×
286
      ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont,
×
287
      _("Don't move cursor automatically\nUseful e.g. for split-screen / dual-mice multiplayer (see wiki)"));
288
    smartCursor->SetSelection(SETTINGS.global.smartCursor);
×
289
    curPos.y += 50;
×
290

291
    groupAllgemein->AddText(ID_txtDebugData, curPos, _("Submit debug data:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
292
    optiongroup = groupAllgemein->AddOptionGroup(ID_grpDebugData, GroupSelectType::Check);
×
293
    optiongroup->AddTextButton(ID_btSubmitDebugOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"),
×
294
                               NormalFont);
×
295
    optiongroup->AddTextButton(ID_btSubmitDebugAsk, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Ask always"),
×
296
                               NormalFont);
×
297

298
    optiongroup->SetSelection((SETTINGS.global.submit_debug_data == 1) ? ID_btSubmitDebugOn :
×
299
                                                                         ID_btSubmitDebugAsk); //-V807
300
    curPos.y += 30;
×
301

302
    groupAllgemein->AddText(ID_txtGFInfo, curPos, _("Show GameFrame Info:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
303
    optiongroup = groupAllgemein->AddOptionGroup(ID_grpGFInfo, GroupSelectType::Check);
×
304
    optiongroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont);
×
305
    optiongroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont);
×
306

307
    optiongroup->SetSelection(SETTINGS.global.showGFInfo);
×
308

309
    // "Auflösung"
310
    curPos.y = 80;
×
311
    groupGrafik->AddText(ID_txtResolution, curPos, _("Fullscreen resolution:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
312
    groupGrafik->AddComboBox(ID_cbResolution, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 150);
×
313
    curPos.y += 50;
×
314

315
    // "Vollbild"
316
    groupGrafik->AddText(ID_txtFullscreen, curPos, _("Mode:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
317
    optiongroup = groupGrafik->AddOptionGroup(ID_grpFullscreen, GroupSelectType::Check);
×
318
    optiongroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("Fullscreen"), NormalFont);
×
319
    optiongroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Windowed"), NormalFont);
×
320
    curPos.y += 50;
×
321

322
    // "VSync"
323
    groupGrafik->AddText(ID_txtFramerate, curPos, _("Limit Framerate:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
324
    groupGrafik->AddComboBox(ID_cbFramerate, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey, NormalFont, 150);
×
325
    curPos.y += 50;
×
326

327
    // "VBO"
328
    groupGrafik->AddText(ID_txtVBO, curPos, _("Vertex Buffer Objects:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
329
    optiongroup = groupGrafik->AddOptionGroup(ID_grpVBO, GroupSelectType::Check);
×
330
    optiongroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont);
×
331
    optiongroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont);
×
332
    curPos.y += 50;
×
333

334
    // "Grafiktreiber"
335
    groupGrafik->AddText(ID_txtVideoDriver, curPos, _("Graphics Driver"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
336
    combo = groupGrafik->AddComboBox(ID_cbVideoDriver, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey,
×
337
                                     NormalFont, 100);
×
338

339
    const auto video_drivers = drivers::DriverWrapper::LoadDriverList(drivers::DriverType::Video);
×
340

341
    for(const auto& video_driver : video_drivers)
×
342
    {
343
        combo->AddString(video_driver.GetName());
×
344
        if(video_driver.GetName() == SETTINGS.driver.video)
×
345
            combo->SetSelection(combo->GetNumItems() - 1);
×
346
    }
347
    curPos.y += 50;
×
348

349
    groupGrafik->AddText(ID_txtOptTextures, curPos, _("Optimized Textures:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
350
    optiongroup = groupGrafik->AddOptionGroup(ID_grpOptTextures, GroupSelectType::Check);
×
351

352
    optiongroup->AddTextButton(ID_btOn, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("On"), NormalFont);
×
353
    optiongroup->AddTextButton(ID_btOff, curPos + ctrlOffset2, ctrlSize, TextureColor::Grey, _("Off"), NormalFont);
×
354
    curPos.y += 50;
×
355

356
    groupGrafik->AddText(ID_txtGuiScale, curPos, _("GUI Scale:"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
357
    groupGrafik->AddComboBox(ID_cbGuiScale, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, NormalFont, 100);
×
358
    updateGuiScale();
×
359

360
    curPos.y = 80;
×
361
    constexpr Offset bt1Offset(200, -5);
×
362
    constexpr Offset bt2Offset(300, -5);
×
363
    constexpr Offset volOffset(400, -5);
×
364
    constexpr Extent ctrlSizeSmall(90, ctrlSize.y);
×
365

366
    groupSound->AddText(ID_txtMusic, curPos, _("Music"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
367
    optiongroup = groupSound->AddOptionGroup(ID_grpMusic, GroupSelectType::Check);
×
368
    optiongroup->AddTextButton(ID_btOn, curPos + bt1Offset, ctrlSizeSmall, TextureColor::Grey, _("On"), NormalFont);
×
369
    optiongroup->AddTextButton(ID_btOff, curPos + bt2Offset, ctrlSizeSmall, TextureColor::Grey, _("Off"), NormalFont);
×
370

371
    ctrlProgress* Mvolume =
372
      groupSound->AddProgress(ID_pgMusicVol, curPos + volOffset, ctrlSize, TextureColor::Grey, 139, 138, 100);
×
373
    Mvolume->SetPosition((SETTINGS.sound.musicVolume * 100) / 255); //-V807
×
374
    curPos.y += 50;
×
375

376
    // Effekte
377
    groupSound->AddText(ID_txtEffects, curPos, _("Effects"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
378
    optiongroup = groupSound->AddOptionGroup(ID_grpEffects, GroupSelectType::Check);
×
379
    optiongroup->AddTextButton(ID_btOn, curPos + bt1Offset, ctrlSizeSmall, TextureColor::Grey, _("On"), NormalFont);
×
380
    optiongroup->AddTextButton(ID_btOff, curPos + bt2Offset, ctrlSizeSmall, TextureColor::Grey, _("Off"), NormalFont);
×
381

382
    ctrlProgress* FXvolume =
383
      groupSound->AddProgress(ID_pgEffectsVol, curPos + volOffset, ctrlSize, TextureColor::Grey, 139, 138, 100);
×
384
    FXvolume->SetPosition((SETTINGS.sound.effectsVolume * 100) / 255);
×
385
    curPos.y += 50;
×
386

387
    groupSound->AddTextButton(ID_btMusicPlayer, curPos + ctrlOffset, ctrlSize, TextureColor::Grey, _("Music player"),
×
388
                              NormalFont);
×
389
    curPos.y += 50;
×
390

391
    groupSound->AddText(ID_txtAudioDriver, curPos, _("Sounddriver"), COLOR_YELLOW, FontStyle{}, NormalFont);
×
392
    combo = groupSound->AddComboBox(ID_cbAudioDriver, curPos + ctrlOffset, ctrlSizeLarge, TextureColor::Grey,
×
393
                                    NormalFont, 100);
×
394

395
    const auto audio_drivers = drivers::DriverWrapper::LoadDriverList(drivers::DriverType::Audio);
×
396

397
    for(const auto& audio_driver : audio_drivers)
×
398
    {
399
        combo->AddString(audio_driver.GetName());
×
400
        if(audio_driver.GetName() == SETTINGS.driver.audio)
×
401
            combo->SetSelection(combo->GetNumItems() - 1);
×
402
    }
403

404
    // "Allgemein" auswählen
405
    optiongroup = GetCtrl<ctrlOptionGroup>(ID_grpOptions);
×
406
    optiongroup->SetSelection(ID_btGeneral, true);
×
407

408
    // Grafik
409
    // {
410

411
    loadVideoModes();
×
412

413
    // Und zu der Combobox hinzufügen
414
    ctrlComboBox& cbVideoModes = *groupGrafik->GetCtrl<ctrlComboBox>(ID_cbResolution);
×
415
    for(const auto& videoMode : video_modes)
×
416
    {
417
        VideoMode ratio = getAspectRatio(videoMode);
×
418
        s25util::ClassicImbuedStream<std::ostringstream> str;
×
419
        str << videoMode.width << "x" << videoMode.height;
×
420
        // Make the length always the same as 'iiiixiiii' to align the ratio
421
        int len = str.str().length();
×
422
        for(int i = len; i < 4 + 1 + 4; i++)
×
423
            str << " ";
×
424
        str << " (" << ratio.width << ":" << ratio.height << ")";
×
425

426
        cbVideoModes.AddString(str.str());
×
427

428
        // Ist das die aktuelle Auflösung? Dann selektieren
429
        if(videoMode == SETTINGS.video.fullscreenSize) //-V807
×
430
            cbVideoModes.SetSelection(cbVideoModes.GetNumItems() - 1);
×
431
    }
432

433
    // "Vollbild" setzen
434
    groupGrafik->GetCtrl<ctrlOptionGroup>(ID_grpFullscreen)->SetSelection(SETTINGS.video.fullscreen); //-V807
×
435

436
    // "Limit Framerate" füllen
437
    auto* cbFrameRate = groupGrafik->GetCtrl<ctrlComboBox>(ID_cbFramerate);
×
438
    if(VIDEODRIVER.HasVSync())
×
439
        cbFrameRate->AddString(_("Dynamic (Limits to display refresh rate, works with most drivers)"));
×
440
    for(int framerate : Settings::SCREEN_REFRESH_RATES)
×
441
    {
442
        if(framerate == -1)
×
443
            cbFrameRate->AddString(_("Disabled"));
×
444
        else
445
            cbFrameRate->AddString(helpers::toString(framerate) + " FPS");
×
446
        if(SETTINGS.video.framerate == framerate)
×
447
            cbFrameRate->SetSelection(cbFrameRate->GetNumItems() - 1);
×
448
    }
449
    if(!cbFrameRate->GetSelection())
×
450
        cbFrameRate->SetSelection(0);
×
451

452
    groupGrafik->GetCtrl<ctrlOptionGroup>(ID_grpVBO)->SetSelection(SETTINGS.video.vbo);
×
453

454
    groupGrafik->GetCtrl<ctrlOptionGroup>(ID_grpOptTextures)->SetSelection(SETTINGS.video.shared_textures);
×
455
    // }
456

457
    // Sound
458
    // {
459

460
    groupSound->GetCtrl<ctrlOptionGroup>(ID_grpMusic)->SetSelection(SETTINGS.sound.musicEnabled);
×
461
    groupSound->GetCtrl<ctrlOptionGroup>(ID_grpEffects)->SetSelection(SETTINGS.sound.effectsEnabled);
×
462

463
    // }
464

465
    // Load game settings
466
    ggs.LoadSettings();
×
467
}
×
468

469
dskOptions::~dskOptions()
×
470
{
471
    // Save game settings
472
    ggs.SaveSettings();
×
473
}
×
474

475
void dskOptions::Msg_Group_ProgressChange(const unsigned /*group_id*/, const unsigned ctrl_id,
×
476
                                          const unsigned short position)
477
{
478
    switch(ctrl_id)
×
479
    {
480
        case ID_pgEffectsVol:
×
481
            SETTINGS.sound.effectsVolume = static_cast<uint8_t>((position * 255) / 100);
×
482
            AUDIODRIVER.SetMasterEffectVolume(SETTINGS.sound.effectsVolume);
×
483
            break;
×
484
        case ID_pgMusicVol:
×
485
            SETTINGS.sound.musicVolume = static_cast<uint8_t>((position * 255) / 100);
×
486
            AUDIODRIVER.SetMusicVolume(SETTINGS.sound.musicVolume);
×
487
            break;
×
488
    }
489
}
×
490

491
void dskOptions::Msg_Group_ComboSelectItem(const unsigned group_id, const unsigned ctrl_id, const unsigned selection)
×
492
{
493
    auto* group = GetCtrl<ctrlGroup>(group_id);
×
494
    auto* combo = group->GetCtrl<ctrlComboBox>(ctrl_id);
×
495

496
    switch(ctrl_id)
×
497
    {
NEW
498
        case ID_cbCommonPortrait:
×
NEW
499
            SETTINGS.lobby.portraitIndex = selection;
×
NEW
500
            updatePortraitControls();
×
NEW
501
            break;
×
UNCOV
502
        case ID_cbLanguage:
×
503
        {
504
            // Language changed?
505
            std::string old_lang = SETTINGS.language.language; //-V807
×
506
            SETTINGS.language.language = LANGUAGES.setLanguage(selection);
×
507
            if(SETTINGS.language.language != old_lang)
×
508
                WINDOWMANAGER.Switch(std::make_unique<dskOptions>());
×
509
        }
510
        break;
×
511
        case ID_cbProxyType:
×
512
            switch(selection)
513
            {
514
                case 0: SETTINGS.proxy.type = ProxyType::None; break;
×
515
                case 1: SETTINGS.proxy.type = ProxyType::Socks4; break;
×
516
                case 2: SETTINGS.proxy.type = ProxyType::Socks5; break;
×
517
            }
518

519
            // ipv6 gleich sichtbar deaktivieren
520
            if(SETTINGS.proxy.type == ProxyType::Socks4 && SETTINGS.server.ipv6)
×
521
            {
522
                GetCtrl<ctrlGroup>(ID_grpGeneral)->GetCtrl<ctrlOptionGroup>(ID_grpIpv6)->SetSelection(0);
×
523
                GetCtrl<ctrlGroup>(ID_grpGeneral)
×
524
                  ->GetCtrl<ctrlOptionGroup>(ID_grpIpv6)
×
525
                  ->GetCtrl<ctrlButton>(1)
526
                  ->SetEnabled(false);
×
527
                SETTINGS.server.ipv6 = false;
×
528
            }
529

530
            if(SETTINGS.proxy.type != ProxyType::Socks4)
×
531
                GetCtrl<ctrlGroup>(ID_grpGeneral)
×
532
                  ->GetCtrl<ctrlOptionGroup>(ID_grpIpv6)
×
533
                  ->GetCtrl<ctrlButton>(1)
534
                  ->SetEnabled(true);
×
535
            break;
×
536
        case ID_cbResolution: SETTINGS.video.fullscreenSize = video_modes[selection]; break;
×
537
        case ID_cbFramerate:
×
538
            if(VIDEODRIVER.HasVSync())
×
539
            {
540
                if(selection == 0)
×
541
                    SETTINGS.video.framerate = 0;
×
542
                else
543
                    SETTINGS.video.framerate = Settings::SCREEN_REFRESH_RATES[selection - 1];
×
544
            } else
545
                SETTINGS.video.framerate = Settings::SCREEN_REFRESH_RATES[selection];
×
546

547
            VIDEODRIVER.setTargetFramerate(SETTINGS.video.framerate);
×
548
            break;
×
549
        case ID_cbVideoDriver: SETTINGS.driver.video = combo->GetText(selection); break;
×
550
        case ID_cbGuiScale:
×
551
            SETTINGS.video.guiScale = guiScales_[selection];
×
552
            VIDEODRIVER.setGuiScalePercent(SETTINGS.video.guiScale);
×
553
            break;
×
554
        case ID_cbAudioDriver: SETTINGS.driver.audio = combo->GetText(selection); break;
×
555
    }
556
}
×
557

558
void dskOptions::Msg_Group_OptionGroupChange(const unsigned /*group_id*/, const unsigned ctrl_id,
×
559
                                             const unsigned selection)
560
{
561
    const bool enabled = selection == ID_btOn;
×
562
    switch(ctrl_id)
×
563
    {
564
        case ID_grpIpv6: SETTINGS.server.ipv6 = enabled; break;
×
565
        case ID_grpFullscreen: SETTINGS.video.fullscreen = enabled; break;
×
566
        case ID_grpVBO: SETTINGS.video.vbo = enabled; break;
×
567
        case ID_grpOptTextures: SETTINGS.video.shared_textures = enabled; break;
×
568
        case ID_grpMusic:
×
569
            SETTINGS.sound.musicEnabled = enabled;
×
570
            if(enabled)
×
571
                MUSICPLAYER.Play();
×
572
            else
573
                MUSICPLAYER.Stop();
×
574
            break;
×
575
        case ID_grpEffects: SETTINGS.sound.effectsEnabled = enabled; break;
×
576
        case ID_grpDebugData:
×
577
            // Special case: Uses e.g. ID_btSubmitDebugOn directly
578
            SETTINGS.global.submit_debug_data = selection;
×
579
            break;
×
580
        case ID_grpUPNP: SETTINGS.global.use_upnp = enabled; break;
×
581
        case ID_grpInvertScroll: SETTINGS.interface.invertMouse = enabled; break;
×
582
        case ID_grpSmartCursor:
×
583
            SETTINGS.global.smartCursor = enabled;
×
584
            VIDEODRIVER.SetMouseWarping(enabled);
×
585
            break;
×
586
        case ID_grpGFInfo: SETTINGS.global.showGFInfo = enabled; break;
×
587
    }
588
}
×
589

590
void dskOptions::Msg_OptionGroupChange(const unsigned ctrl_id, const unsigned selection)
×
591
{
592
    if(ctrl_id == ID_grpOptions)
×
593
    {
594
        const auto visGrp = selection + ID_grpGeneral - ID_btGeneral;
×
595
        for(const unsigned id : {ID_grpGeneral, ID_grpGraphics, ID_grpSound})
×
596
            GetCtrl<ctrlGroup>(id)->SetVisible(id == visGrp);
×
597
    }
598
}
×
599

600
/// Check that the port is valid and sets outPort to it. Shows an error otherwise
601
static bool validatePort(const std::string& sPort, uint16_t& outPort)
×
602
{
603
    boost::optional<uint16_t> port = validate::checkPort(sPort);
×
604
    if(port)
×
605
        outPort = *port;
×
606
    else
607
    {
608
        WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(_("Error"),
×
609
                                                      _("Invalid port. The valid port-range is 1 to 65535!"), nullptr,
×
610
                                                      MsgboxButton::Ok, MsgboxIcon::ExclamationRed, 1));
×
611
    }
612
    return static_cast<bool>(port);
×
613
}
614

615
void dskOptions::Msg_ButtonClick(const unsigned ctrl_id)
×
616
{
617
    switch(ctrl_id)
×
618
    {
619
        case ID_btBack:
×
620
        {
621
            auto* groupAllgemein = GetCtrl<ctrlGroup>(ID_grpGeneral);
×
622

623
            // Name abspeichern
624
            SETTINGS.lobby.name = groupAllgemein->GetCtrl<ctrlEdit>(ID_edtName)->GetText();
×
625
            if(!validatePort(groupAllgemein->GetCtrl<ctrlEdit>(ID_edtPort)->GetText(), SETTINGS.server.localPort))
×
626
                return;
×
627

628
            SETTINGS.proxy.hostname = groupAllgemein->GetCtrl<ctrlEdit>(ID_edtProxy)->GetText();
×
629
            if(!validatePort(groupAllgemein->GetCtrl<ctrlEdit>(ID_edtProxyPort)->GetText(), SETTINGS.proxy.port))
×
630
                return;
×
631

632
            SETTINGS.Save();
×
633

634
            // Is the selected backend required to support GUI scaling to fullfill the user's choice?
635
            // If so, warn the user if the backend is unable to support GUI scaling.
636
            if(VIDEODRIVER.getGuiScale().percent() == 100
×
637
               && (SETTINGS.video.guiScale != 100
×
638
                   || (SETTINGS.video.guiScale == 0 && VIDEODRIVER.getGuiScaleRange().recommendedPercent != 100)))
×
639
            {
640
                WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
641
                  _("Sorry!"), _("The selected video driver does not support GUI scaling! Setting won't be used."),
×
642
                  this, MsgboxButton::Ok, MsgboxIcon::ExclamationGreen, 1));
×
643
            }
644

645
            if((SETTINGS.video.fullscreen && SETTINGS.video.fullscreenSize != VIDEODRIVER.GetWindowSize()) //-V807
×
646
               || SETTINGS.video.fullscreen != VIDEODRIVER.IsFullscreen())
×
647
            {
648
                const auto screenSize =
649
                  SETTINGS.video.fullscreen ? SETTINGS.video.fullscreenSize : SETTINGS.video.windowedSize;
×
650
                if(!VIDEODRIVER.ResizeScreen(screenSize, SETTINGS.video.fullscreen))
×
651
                {
652
                    WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
653
                      _("Sorry!"), _("You need to restart your game to change the screen resolution!"), this,
×
654
                      MsgboxButton::Ok, MsgboxIcon::ExclamationGreen, 1));
×
655
                    return;
×
656
                }
657
            }
658
            if(SETTINGS.driver.video != VIDEODRIVER.GetName() || SETTINGS.driver.audio != AUDIODRIVER.GetName())
×
659
            {
660
                WINDOWMANAGER.Show(std::make_unique<iwMsgbox>(
×
661
                  _("Sorry!"), _("You need to restart your game to change the video or audio driver!"), this,
×
662
                  MsgboxButton::Ok, MsgboxIcon::ExclamationGreen, 1));
×
663
                return;
×
664
            }
665

666
            WINDOWMANAGER.Switch(std::make_unique<dskMainMenu>());
×
667
        }
668
        break;
×
669
        case ID_btAddons: WINDOWMANAGER.ToggleWindow(std::make_unique<iwAddons>(ggs)); break;
×
670
    }
671
}
672

673
void dskOptions::Msg_Group_ButtonClick(const unsigned /*group_id*/, const unsigned ctrl_id)
×
674
{
675
    switch(ctrl_id)
×
676
    {
677
        default: break;
×
NEW
678
        case ID_btCommonPortrait:
×
NEW
679
            SETTINGS.lobby.portraitIndex = (SETTINGS.lobby.portraitIndex + 1) % Portraits.size();
×
NEW
680
            updatePortraitControls();
×
NEW
681
            break;
×
682
        case ID_btMusicPlayer: WINDOWMANAGER.ToggleWindow(std::make_unique<iwMusicPlayer>()); break;
×
683
        case ID_btKeyboardLayout:
×
684
            WINDOWMANAGER.ToggleWindow(std::make_unique<iwTextfile>("keyboardlayout.txt", _("Keyboard layout")));
×
685
            break;
×
686
    }
687
}
×
688

689
void dskOptions::Msg_MsgBoxResult(const unsigned msgbox_id, const MsgboxResult /*mbr*/)
×
690
{
691
    switch(msgbox_id)
×
692
    {
693
        default: break;
×
694
        // "You need to restart your game ..."
695
        // "The selected video driver does not support GUI scaling!"
696
        case 1: WINDOWMANAGER.Switch(std::make_unique<dskMainMenu>()); break;
×
697
    }
698
}
×
699

700
static bool cmpVideoModes(const VideoMode& left, const VideoMode& right)
×
701
{
702
    if(left.width == right.width)
×
703
        return left.height > right.height;
×
704
    else
705
        return left.width > right.width;
×
706
}
707

708
void dskOptions::loadVideoModes()
×
709
{
710
    // Get available modes
711
    VIDEODRIVER.ListVideoModes(video_modes);
×
712
    // Remove everything below 800x600
713
    helpers::erase_if(video_modes, [](const auto& it) { return it.width < 800 && it.height < 600; });
×
714
    // Sort by aspect ratio
715
    std::sort(video_modes.begin(), video_modes.end(), cmpVideoModes);
×
716
}
×
717

718
void dskOptions::Msg_ScreenResize(const ScreenResizeEvent& sr)
×
719
{
720
    Desktop::Msg_ScreenResize(sr);
×
721
    updateGuiScale();
×
722
}
×
723

724
bool dskOptions::Msg_WheelUp(const MouseCoords& mc)
×
725
{
726
    if(VIDEODRIVER.GetModKeyState().ctrl)
×
727
    {
728
        scrollGuiScale(true);
×
729
        return true;
×
730
    } else
731
        return Desktop::Msg_WheelUp(mc);
×
732
}
733

734
bool dskOptions::Msg_WheelDown(const MouseCoords& mc)
×
735
{
736
    if(VIDEODRIVER.GetModKeyState().ctrl)
×
737
    {
738
        scrollGuiScale(false);
×
739
        return true;
×
740
    } else
741
        return Desktop::Msg_WheelDown(mc);
×
742
}
743

744
void dskOptions::updateGuiScale()
×
745
{
746
    // generate GUI scale percentages in 10% increments
747
    constexpr auto stepSize = 10u;
×
748
    const auto roundGuiScale = [=](unsigned percent) {
×
749
        return helpers::iround<unsigned>(static_cast<float>(percent) / stepSize) * stepSize;
×
750
    };
751

752
    const auto range = VIDEODRIVER.getGuiScaleRange();
×
753
    const auto recommendedPercentRounded = roundGuiScale(range.recommendedPercent);
×
754
    auto* combo = GetCtrl<ctrlGroup>(ID_grpGraphics)->GetCtrl<ctrlComboBox>(ID_cbGuiScale);
×
755

756
    guiScales_.clear();
×
757
    combo->DeleteAllItems();
×
758

759
    guiScales_.push_back(0);
×
760
    combo->AddString(helpers::format(_("Auto (%u%%)"), range.recommendedPercent));
×
761
    if(SETTINGS.video.guiScale == 0)
×
762
        combo->SetSelection(0);
×
763

764
    for(unsigned percent = roundGuiScale(range.minPercent); percent <= range.maxPercent; percent += stepSize)
×
765
    {
766
        if(percent == recommendedPercentRounded)
×
767
            recommendedGuiScaleIndex_ = guiScales_.size();
×
768
        guiScales_.push_back(percent);
×
769

770
        combo->AddString(helpers::toString(percent) + "%");
×
771
        if(percent == SETTINGS.video.guiScale)
×
772
            combo->SetSelection(combo->GetNumItems() - 1);
×
773
    }
774

775
    // if GUI scale exceeds maximum, lower it to keep UI elements on screen
776
    if(SETTINGS.video.guiScale > guiScales_.back())
×
777
    {
778
        combo->SetSelection(combo->GetNumItems() - 1);
×
779
        SETTINGS.video.guiScale = guiScales_.back();
×
780
        VIDEODRIVER.setGuiScalePercent(SETTINGS.video.guiScale);
×
781
    }
782
}
×
783

784
void dskOptions::scrollGuiScale(bool up)
×
785
{
786
    auto* combo = GetCtrl<ctrlGroup>(ID_grpGraphics)->GetCtrl<ctrlComboBox>(ID_cbGuiScale);
×
787
    const auto& selection = combo->GetSelection();
×
788
    unsigned newSelection = 0;
×
789
    if(!selection || *selection == 0) // No selection or "Auto" item selected
×
790
        newSelection = recommendedGuiScaleIndex_;
×
791
    else
792
        newSelection = std::clamp<unsigned>(*selection + (up ? 1 : -1), 1, combo->GetNumItems() - 1);
×
793

794
    if(newSelection != selection)
×
795
    {
796
        combo->SetSelection(newSelection);
×
797
        SETTINGS.video.guiScale = guiScales_[newSelection];
×
798
        VIDEODRIVER.setGuiScalePercent(SETTINGS.video.guiScale);
×
799
    }
800
}
×
801

NEW
802
void dskOptions::updatePortraitControls()
×
803
{
NEW
804
    const auto& newPortrait = Portraits[SETTINGS.lobby.portraitIndex];
×
NEW
805
    auto* groupCommon = GetCtrl<ctrlGroup>(ID_grpGeneral);
×
806

NEW
807
    auto* portraitButton = groupCommon->GetCtrl<ctrlImageButton>(ID_btCommonPortrait);
×
NEW
808
    auto* newPortraitTexture = LOADER.GetTextureN(newPortrait.resourceId, newPortrait.resourceIndex);
×
NEW
809
    portraitButton->SetImage(newPortraitTexture);
×
810

NEW
811
    auto* portraitCombo = groupCommon->GetCtrl<ctrlComboBox>(ID_cbCommonPortrait);
×
NEW
812
    portraitCombo->SetSelection(SETTINGS.lobby.portraitIndex);
×
NEW
813
}
×
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