e8555c89 Andy McCall 1 month ago
1
#include " ImGuiMachine.hh"
2
3
#include " CustomFont.h"
4
#include " ImGuiConnector.hh"
5
#include " ImGuiCpp.hh"
6
#include " ImGuiManager.hh"
7
#include " ImGuiMedia.hh"
8
#include " ImGuiUtils.hh"
9
10
#include " BooleanSetting.hh"
11
#include " CartridgeSlotManager.hh"
12
#include " Debuggable.hh"
13
#include " Debugger.hh"
14
#include " FileException.hh"
15
#include " FileOperations.hh"
16
#include " GlobalSettings.hh"
17
#include " HardwareConfig.hh"
18
#include " MSXCommandController.hh"
19
#include " MSXMotherBoard.hh"
20
#include " Reactor.hh"
21
#include " RealDrive.hh"
22
#include " RomInfo.hh"
23
#include " VDP.hh"
24
#include " VDPVRAM.hh"
25
26
#include " enumerate.hh"
27
#include " narrow.hh"
28
29
#include < imgui.h>
30
#include < imgui_stdlib.h>
31
32
#include < algorithm>
33
#include < memory>
34
35
using namespace std:: literals;
36
37
constexpr static std:: string_view EMPTY = " (empty)" ;
38
39
namespace openmsx {
40
41
static constexpr array_with_enum_index< SetupDepth, zstring_view> depthNodeNames = {
42
" None" ,
43
" Machine" ,
44
" Extensions" ,
45
" Connectors" ,
46
" Media" ,
47
" Run time state"
48
} ;
49
50
51
ImGuiMachine:: ImGuiMachine ( ImGuiManager& manager_ )
52
: ImGuiPart ( manager_ )
53
, setupFileList ( " setup" , Reactor:: SETUP_EXTENSION, Reactor:: SETUP_DIR)
54
, confirmDialog ( " Confirm##setup" )
55
{
56
57
setupFileList. drawAction = [ & ] {
58
im:: Group ( [ & ] {
59
setupFileList. drawTable ( ) ;
60
im:: Disabled ( previewSetup. fullName . empty ( ) , [ & ] {
61
if ( ImGui:: Button ( " Load" ) ) {
62
manager. executeDelayed ( [ this ] {
63
try {
64
auto & reactor = manager. getReactor ( ) ;
65
reactor. switchMachineFromSetup ( previewSetup. fullName ) ;
66
} catch ( MSXException& e) {
67
68
previewSetup. lastExceptionMessage = e. getMessage ( ) ;
69
}
70
} ) ;
71
}
72
} ) ;
73
} ) ;
74
ImGui:: SameLine ( ) ;
75
im:: Group ( [ & ] {
76
if ( previewSetup. motherBoard ) {
77
showSetupOverviewView ( * previewSetup. motherBoard ) ;
78
} else {
79
showNonExistingPreview ( ) ;
80
}
81
} ) ;
82
} ;
83
84
setupFileList. displayColor = [ & ] ( const FileListWidget:: Entry& entry) {
85
return ! previewSetup. lastExceptionMessage . empty ( ) && previewSetup. fullName == entry. fullName ? imColor:: ERROR : imColor:: TEXT;
86
} ;
87
88
setupFileList. singleClickAction = [ & ] ( const FileListWidget:: Entry& entry) {
89
if ( previewSetup. fullName == entry. fullName ) return ;
90
91
92
93
previewSetup. fullName = entry. fullName ;
94
previewSetup. name = entry. getDefaultDisplayName ( ) ;
95
96
97
loadPreviewSetup ( ) ;
98
} ;
99
100
setupFileList. doubleClickAction = [ & ] ( const FileListWidget:: Entry& entry) {
101
102
if ( entry. fullName == previewSetup. fullName && ! previewSetup. lastExceptionMessage . empty ( ) ) return ;
103
manager. executeDelayed ( [ & entry = entry, & manager = manager] {
104
try {
105
manager. getReactor ( ) . switchMachineFromSetup ( entry. fullName ) ;
106
} catch ( MSXException& ) {
107
108
}
109
110
} ) ;
111
} ;
112
113
setupFileList. displayName = [ & ] ( const FileListWidget:: Entry& entry) {
114
auto defaultDisplayName = entry. getDefaultDisplayName ( ) ;
115
bool isDefault = manager. getReactor ( ) . getDefaultSetupSetting ( ) . getString ( ) == defaultDisplayName;
116
return strCat ( defaultDisplayName, strCat_if ( isDefault, " [default]" sv ) ) ;
117
} ;
118
119
setupFileList. deleteAction = [ & ] ( const FileListWidget:: Entry& entry) {
120
setupFileList. defaultDeleteAction ( entry ) ;
121
auto & defaultSetting = manager. getReactor ( ) . getDefaultSetupSetting ( ) ;
122
if ( defaultSetting. getString ( ) == previewSetup. name ) {
123
124
defaultSetting. setString ( " " ) ;
125
}
126
} ;
127
}
128
129
void ImGuiMachine:: save ( ImGuiTextBuffer& buf )
130
{
131
savePersistent ( buf, * this , persistentElements ) ;
132
for ( const auto & item : recentMachines) {
133
buf. appendf ( " machine.recent=%s \n " , item. c_str ( ) ) ;
134
}
135
}
136
137
void ImGuiMachine:: loadLine ( std:: string_view name , zstring_view value )
138
{
139
if ( loadOnePersistent ( name, value, * this , persistentElements ) ) {
140
141
} else if ( name == " machine.recent" ) {
142
recentMachines. push_back ( value ) ;
143
}
144
}
145
146
void ImGuiMachine:: showMenu ( MSXMotherBoard* motherBoard )
147
{
148
bool loadSetupOpen = false ;
149
im:: Menu ( " Machine" , [ & ] {
150
using enum SetupDepth;
151
152
auto & reactor = manager. getReactor ( ) ;
153
const auto & hotKey = reactor. getHotKey ( ) ;
154
155
ImGui:: MenuItem ( " Select MSX machine..." , nullptr , & showSelectMachine) ;
156
showRecentMachinesMenu ( ) ;
157
158
auto showSetupDepthLevelSelector = [ & ] ( const std:: string& displayText, const bool includeNone, SetupDepth currentDepth) {
159
static constexpr array_with_enum_index< SetupDepth, zstring_view> helpText = {
160
" Do not save." ,
161
" Only the machine itself, without anything in it." ,
162
" The machine with all plugged in extensions." ,
163
" The machine, with all plugged in extensions and all things that are plugged into the connectors." ,
164
" The machine, with all plugged in extensions and all things that are plugged into the connectors and also all inserted media." ,
165
" The full state of the machine, with everything that's in it at the current time."
166
} ;
167
168
auto depthNodeNameForCombo = [ & ] ( SetupDepth depth) {
169
return tmpStrCat ( strCat_if ( depth != one_of ( NONE, MACHINE ) , " + " sv ) , depthNodeNames[ depth] ) ;
170
} ;
171
172
SetupDepth selectedDepth = currentDepth;
173
im:: Combo ( displayText. c_str ( ) , depthNodeNameForCombo ( currentDepth ) . c_str ( ) , [ & ] {
174
const auto indent = ImGui:: CalcTextSize ( " m" ) . x ;
175
for ( auto d_ : xrange ( std:: to_underlying ( includeNone ? NONE : MACHINE ) , std:: to_underlying ( NUM ) ) ) {
176
const auto d = static_cast < SetupDepth> ( d_) ;
177
if ( d != one_of ( NONE, MACHINE ) ) {
178
ImGui:: Indent ( indent ) ;
179
}
180
if ( ImGui:: Selectable ( depthNodeNameForCombo ( d ) . c_str ( ) ) ) {
181
selectedDepth = d;
182
}
183
simpleToolTip ( helpText[ d] ) ;
184
}
185
} ) ;
186
HelpMarker ( " Select the depth level of what to include in the setup that will be saved. "
187
" All levels above the one you selected will also be included." ) ;
188
return selectedDepth;
189
} ;
190
191
if ( motherBoard) {
192
ImGui:: Separator ( ) ;
193
194
ImGui:: MenuItem ( " Quick setup editor" , nullptr , & showQuickSetupEditor) ;
195
196
loadSetupOpen = setupFileList. menu ( " Load setup" ) ;
197
198
saveSetupOpen = im:: Menu ( " Save setup" , true , [ & ] {
199
ImGui:: TextUnformatted ( " Save current setup:" ) ;
200
201
saveSetupDepth = showSetupDepthLevelSelector ( " Select depth level" , false , saveSetupDepth) ;
202
203
auto exists = [ & ] {
204
auto filename = FileOperations:: parseCommandFileArgument (
205
saveSetupName, Reactor:: SETUP_DIR, " " , Reactor:: SETUP_EXTENSION ) ;
206
return FileOperations:: exists ( filename ) ;
207
} ;
208
if ( ! saveSetupOpen) {
209
210
auto configName = motherBoard-> getMachineName ( ) ;
211
const auto * info = findMachineInfo ( configName ) ;
212
213
214
215
auto initialSaveSetupName = FileOperations:: cleanFilename (
216
info ? info-> displayName : configName ) ;
217
saveSetupName = initialSaveSetupName;
218
if ( exists ( ) ) {
219
saveSetupName = FileOperations:: stem ( FileOperations:: getNextNumberedFileName (
220
Reactor:: SETUP_DIR, initialSaveSetupName, Reactor:: SETUP_EXTENSION, true ) ) ;
221
}
222
}
223
ImGui:: InputText ( " ##save-setup-name" , & saveSetupName) ;
224
simpleToolTip ( saveSetupName ) ;
225
ImGui:: SameLine ( ) ;
226
if ( ImGui:: Button ( " Save" ) ) {
227
ImGui:: CloseCurrentPopup ( ) ;
228
229
auto action = [ this , motherBoard] {
230
if ( motherBoard) {
231
try {
232
233
auto filename = FileOperations:: parseCommandFileArgument (
234
saveSetupName, Reactor:: SETUP_DIR, " " , Reactor:: SETUP_EXTENSION ) ;
235
motherBoard-> storeAsSetup ( filename, saveSetupDepth ) ;
236
manager. getCliComm ( ) . printInfo ( strCat ( " Setup saved to " , filename) ) ;
237
if ( setSetupAsDefault) {
238
manager. getReactor ( ) . getDefaultSetupSetting ( ) . setString ( saveSetupName ) ;
239
}
240
} catch ( MSXException& e) {
241
manager. getCliComm ( ) . printError (
242
" Couldn't save setup: " , e. getMessage ( ) ) ;
243
}
244
setSetupAsDefault = false ;
245
}
246
} ;
247
auto delayedAction = [ manager = & manager, action] {
248
manager-> executeDelayed ( action ) ;
249
} ;
250
if ( exists ( ) ) {
251
confirmDialog. open (
252
strCat ( " Overwrite setup with name '" , saveSetupName, " '?" ) ,
253
delayedAction ) ;
254
} else {
255
delayedAction ( ) ;
256
}
257
}
258
ImGui:: Checkbox ( " Set as default" , & setSetupAsDefault) ;
259
simpleToolTip ( " Check this to set the setup you are saving as default setup: load this setup when starting up openMSX if no other setup is specified." ) ;
260
ImGui:: Separator ( ) ;
261
showSetupOverviewSave ( * motherBoard) ;
262
} ) ;
263
264
im:: Menu ( " Current setup" , true , [ & ] {
265
showSetupOverviewView ( * motherBoard) ;
266
} ) ;
267
}
268
269
setupSettingsOpen = im:: Menu ( " Setup settings" , true , [ & ] {
270
auto currentSaveAtExitName = manager. getReactor ( ) . getSaveSetupAtExitNameSetting ( ) . getString ( ) ;
271
auto & defaultSetupSetting = reactor. getDefaultSetupSetting ( ) ;
272
bool startMachine = defaultSetupSetting. getString ( ) . empty ( ) ;
273
if ( ! setupSettingsOpen) {
274
setups = Reactor:: getSetups ( ) ;
275
276
if ( defaultSetupSetting. getString ( ) . empty ( ) ) {
277
previousDefaultSetup = setups. empty ( ) ? currentSaveAtExitName : setups. front ( ) ;
278
} else {
279
previousDefaultSetup = defaultSetupSetting. getString ( ) ;
280
}
281
}
282
ImGui:: TextUnformatted ( " When openMSX starts:" ) ;
283
if ( ImGui:: RadioButton ( " Load a machine" , startMachine) ) {
284
previousDefaultSetup = defaultSetupSetting. getValue ( ) . getString ( ) ;
285
defaultSetupSetting. setValue ( TclObject ( ) ) ;
286
}
287
HelpMarker ( " Select this option to load the specified machine when openMSX starts up, instead of a setup." ) ;
288
im:: DisabledIndent ( ! startMachine, [ & ] {
289
auto & allMachines = getAllMachines ( ) ;
290
auto currentDefault = reactor. getDefaultMachineSetting ( ) . getString ( ) ;
291
auto currentDefaultDisplay = [ & ] {
292
if ( const auto * info = findMachineInfo ( currentDefault ) ) {
293
return info-> displayName ;
294
}
295
return std:: string ( currentDefault ) ;
296
} ( ) ;
297
im:: Combo ( " ##defaultMachine" , currentDefaultDisplay. c_str ( ) , [ & ] {
298
for ( auto & info: allMachines) {
299
bool ok = getTestResult ( info ) . empty ( ) ;
300
im:: StyleColor ( ! ok, ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
301
if ( ImGui:: Selectable ( info. displayName . c_str ( ) ) ) {
302
reactor. getDefaultMachineSetting ( ) . setValue ( TclObject ( info. configName ) ) ;
303
304
defaultSetupSetting. setValue ( TclObject ( ) ) ;
305
}
306
if ( info. configName == currentDefault && ImGui:: IsWindowAppearing ( ) ) ImGui:: SetScrollHereY ( ) ;
307
if ( ImGui:: IsItemHovered ( ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_Stationary ) ) {
308
im:: ItemTooltip ( [ & ] {
309
printConfigInfo ( info ) ;
310
} ) ;
311
}
312
} ) ;
313
}
314
} ) ;
315
} ) ;
316
if ( ImGui:: RadioButton ( " Load a setup" , ! startMachine) ) {
317
defaultSetupSetting. setValue ( TclObject ( previousDefaultSetup ) ) ;
318
}
319
320
HelpMarker ( " Select this option to load the specified setup when openMSX starts up. "
321
" Note that in case the setup cannot be loaded, the machine shown at the other "
322
" option will be loaded after all." ) ;
323
im:: DisabledIndent ( startMachine, [ & ] {
324
auto currentDefault = startMachine ? previousDefaultSetup : defaultSetupSetting. getString ( ) ;
325
auto showSetup = [ & ] ( zstring_view setup) {
326
if ( ImGui:: Selectable ( setup. c_str ( ) ) ) {
327
defaultSetupSetting. setValue ( TclObject ( setup ) ) ;
328
}
329
if ( setup == currentDefault && ImGui:: IsWindowAppearing ( ) ) ImGui:: SetScrollHereY ( ) ;
330
if ( ImGui:: IsItemHovered ( ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_Stationary ) ) {
331
if ( previewSetup. name != setup) {
332
try {
333
previewSetup. fullName = userDataFileContext ( Reactor:: SETUP_DIR ) . resolve ( tmpStrCat ( setup, Reactor:: SETUP_EXTENSION ) ) ;
334
loadPreviewSetup ( ) ;
335
} catch ( FileException& ) {
336
manager. executeDelayed ( [ this ] {
337
previewSetup. motherBoard . reset ( ) ;
338
} ) ;
339
}
340
}
341
im:: ItemTooltip ( [ & ] {
342
if ( previewSetup. motherBoard ) {
343
showSetupOverviewTooltip ( * previewSetup. motherBoard ) ;
344
} else {
345
showNonExistingPreview ( ) ;
346
}
347
} ) ;
348
}
349
} ;
350
351
im:: Combo ( " ##defaultSetup" , currentDefault. c_str ( ) , [ & ] {
352
for ( auto & setup: setups) {
353
showSetup ( setup ) ;
354
}
355
if ( ! contains ( setups, currentSaveAtExitName ) ) {
356
showSetup ( currentSaveAtExitName ) ;
357
}
358
} ) ;
359
} ) ;
360
ImGui:: Separator ( ) ;
361
ImGui:: TextUnformatted ( " When openMSX exits:" ) ;
362
im:: Indent ( [ & ] {
363
ImGui:: TextUnformatted ( " Save setup depth level" ) ;
364
auto & depthSetting = manager. getReactor ( ) . getSaveSetupAtExitDepthSetting ( ) ;
365
auto currentDepth = depthSetting. getEnum ( ) ;
366
auto newDepth = showSetupDepthLevelSelector ( " ##empty" , true , currentDepth) ;
367
im:: Disabled ( currentDepth == NONE, [ & ] {
368
ImGui:: TextUnformatted ( " as" ) ;
369
InputText ( " ##save-setup-name" , manager. getReactor ( ) . getSaveSetupAtExitNameSetting ( ) ) ;
370
HelpMarker ( " The setup name given here will be used when saving the setup at exit. "
371
" Select 'None' for the depth level to not save the current setup at exit." ) ;
372
} ) ;
373
if ( newDepth != currentDepth) {
374
depthSetting. setEnum ( newDepth ) ;
375
}
376
} ) ;
377
} ) ;
378
379
ImGui:: Separator ( ) ;
380
381
if ( motherBoard) {
382
const auto & controller = motherBoard-> getMSXCommandController ( ) ;
383
if ( auto * firmwareSwitch = dynamic_cast < BooleanSetting* > ( controller. findSetting ( " firmwareswitch" ) ) ) {
384
Checkbox ( hotKey, " Firmware switch" , * firmwareSwitch ) ;
385
}
386
}
387
388
auto & pauseSetting = reactor. getGlobalSettings ( ) . getPauseSetting ( ) ;
389
bool pause = pauseSetting. getBoolean ( ) ;
390
if ( auto shortCut = getShortCutForCommand ( hotKey, " toggle pause" ) ;
391
ImGui:: MenuItem ( " Pause" , shortCut. c_str ( ) , & pause) ) {
392
pauseSetting. setBoolean ( pause ) ;
393
}
394
395
if ( auto shortCut = getShortCutForCommand ( hotKey, " reset" ) ;
396
ImGui:: MenuItem ( " Reset" , shortCut. c_str ( ) , nullptr , motherBoard != nullptr ) ) {
397
manager. executeDelayed ( TclObject ( " reset" ) ) ;
398
}
399
400
auto & powerSetting = reactor. getGlobalSettings ( ) . getPowerSetting ( ) ;
401
bool power = powerSetting. getBoolean ( ) ;
402
if ( auto shortCut = getShortCutForCommand ( hotKey, " toggle power" ) ;
403
ImGui:: MenuItem ( " Power" , shortCut. c_str ( ) , & power) ) {
404
powerSetting. setBoolean ( power ) ;
405
}
406
407
ImGui:: Separator ( ) ;
408
ImGui:: MenuItem ( " Test MSX hardware" , nullptr , & showTestHardware) ;
409
} ) ;
410
411
confirmDialog. execute ( ) ;
412
413
if ( ! loadSetupOpen && ! setupSettingsOpen && previewSetup. motherBoard ) {
414
manager. executeDelayed ( [ this ] {
415
previewSetup. motherBoard . reset ( ) ;
416
} ) ;
417
}
418
}
419
420
void ImGuiMachine:: showRecentMachinesMenu ( )
421
{
422
423
424
bool anyMachine = std:: ranges:: any_of ( recentMachines, [ & ] ( const auto & item) {
425
return findMachineInfo ( item ) != nullptr ;
426
} ) ;
427
im:: Menu ( " Recent machines" , anyMachine, [ & ] {
428
429
std:: string selectedMachine;
430
for ( const auto & [ i, item] : enumerate ( recentMachines ) ) {
431
auto * info = findMachineInfo ( item ) ;
432
if ( ! info) continue ;
433
bool ok = getTestResult ( * info) . empty ( ) ;
434
im:: StyleColor ( ! ok, ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
435
if ( ImGui:: MenuItem ( tmpStrCat ( info-> displayName , " ##" , i ) . c_str ( ) ) && ok) {
436
selectedMachine = info-> configName ;
437
}
438
} ) ;
439
if ( ImGui:: IsItemHovered ( ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_Stationary ) ) {
440
im:: ItemTooltip ( [ & ] {
441
printConfigInfo ( * info) ;
442
} ) ;
443
}
444
}
445
if ( ! selectedMachine. empty ( ) ) {
446
manager. executeDelayed ( makeTclList ( " machine" , selectedMachine) ) ;
447
addRecentItem ( recentMachines, selectedMachine ) ;
448
}
449
} ) ;
450
}
451
452
void ImGuiMachine:: signalQuit ( )
453
{
454
previewSetup. motherBoard . reset ( ) ;
455
}
456
457
void ImGuiMachine:: loadPreviewSetup ( )
458
{
459
manager. executeDelayed ( [ this ] {
460
try {
461
462
previewSetup. motherBoard . reset ( ) ;
463
previewSetup. lastExceptionMessage . clear ( ) ;
464
auto newBoard = manager. getReactor ( ) . createEmptyMotherBoard ( ) ;
465
XmlInputArchive in ( previewSetup. fullName ) ;
466
in. serialize ( " machine" , * newBoard) ;
467
previewSetup. motherBoard = newBoard;
468
} catch ( MSXException& e) {
469
previewSetup. lastExceptionMessage = e. getMessage ( ) ;
470
}
471
} ) ;
472
}
473
474
void ImGuiMachine:: showNonExistingPreview ( )
475
{
476
if ( previewSetup. lastExceptionMessage . empty ( ) ) {
477
ImGui:: TextUnformatted ( " Nothing to preview..." ) ;
478
} else {
479
im:: StyleColor ( ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
480
im:: TextWrapPos ( ImGui:: GetFontSize ( ) * 50.0f , [ & ] {
481
ImGui:: StrCat ( " Setup " , previewSetup. name , " cannot be loaded:" ) ;
482
ImGui:: TextUnformatted ( previewSetup. lastExceptionMessage ) ;
483
} ) ;
484
} ) ;
485
}
486
}
487
488
void ImGuiMachine:: showSetupOverviewView ( MSXMotherBoard& motherBoard )
489
{
490
const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen;
491
if ( auto * info = findMachineInfo ( motherBoard. getMachineName ( ) ) ) {
492
ImGui:: TextUnformatted ( info-> displayName ) ;
493
showSetupOverviewMachine ( * info) ;
494
}
495
showSetupOverviewExtensions ( motherBoard, Mode:: VIEW, flags ) ;
496
showSetupOverviewConnectors ( motherBoard, Mode:: VIEW, flags ) ;
497
showSetupOverviewMedia ( motherBoard, Mode:: VIEW, flags ) ;
498
showSetupOverviewState ( motherBoard, flags ) ;
499
}
500
void ImGuiMachine:: showSetupOverviewSave ( MSXMotherBoard& motherBoard )
501
{
502
if ( auto * info = findMachineInfo ( motherBoard. getMachineName ( ) ) ) {
503
showSetupOverviewMachine ( * info) ;
504
}
505
auto colorDisabled = getColor ( imColor:: TEXT_DISABLED ) ;
506
im:: StyleColor ( saveSetupDepth < SetupDepth:: EXTENSIONS, ImGuiCol_Text, colorDisabled, [ & ] {
507
showSetupOverviewExtensions ( motherBoard, Mode:: VIEW ) ;
508
} ) ;
509
im:: StyleColor ( saveSetupDepth < SetupDepth:: CONNECTORS, ImGuiCol_Text, colorDisabled, [ & ] {
510
showSetupOverviewConnectors ( motherBoard, Mode:: VIEW ) ;
511
} ) ;
512
im:: StyleColor ( saveSetupDepth < SetupDepth:: MEDIA, ImGuiCol_Text, colorDisabled, [ & ] {
513
showSetupOverviewMedia ( motherBoard, Mode:: VIEW ) ;
514
} ) ;
515
im:: StyleColor ( saveSetupDepth < SetupDepth:: COMPLETE_STATE, ImGuiCol_Text, colorDisabled, [ & ] {
516
showSetupOverviewState ( motherBoard ) ;
517
} ) ;
518
}
519
void ImGuiMachine:: showSetupOverviewTooltip ( MSXMotherBoard& motherBoard )
520
{
521
const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_Bullet;
522
if ( auto * info = findMachineInfo ( motherBoard. getMachineName ( ) ) ) {
523
ImGui:: TextUnformatted ( info-> displayName ) ;
524
}
525
showSetupOverviewExtensions ( motherBoard, Mode:: VIEW, flags ) ;
526
showSetupOverviewConnectors ( motherBoard, Mode:: VIEW, flags ) ;
527
showSetupOverviewMedia ( motherBoard, Mode:: VIEW, flags ) ;
528
showSetupOverviewState ( motherBoard, flags ) ;
529
}
530
void ImGuiMachine:: showSetupOverviewEdit ( MSXMotherBoard& motherBoard )
531
{
532
const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen;
533
if ( auto * info = findMachineInfo ( motherBoard. getMachineName ( ) ) ) {
534
showSetupOverviewMachineEdit ( * info) ;
535
}
536
showSetupOverviewExtensions ( motherBoard, Mode:: EDIT, flags ) ;
537
showSetupOverviewConnectors ( motherBoard, Mode:: EDIT, flags ) ;
538
showSetupOverviewMedia ( motherBoard, Mode:: EDIT, flags ) ;
539
}
540
541
void ImGuiMachine:: showSetupOverviewMachine ( MachineInfo& info )
542
{
543
im:: TreeNode ( depthNodeNames[ SetupDepth:: MACHINE] . c_str ( ) , [ & ] {
544
545
printConfigInfo ( info ) ;
546
} ) ;
547
}
548
549
void ImGuiMachine:: showSetupOverviewMachineEdit ( MachineInfo& info )
550
{
551
im:: TreeNodeWithoutID ( " Machine" , ImGuiTreeNodeFlags_DefaultOpen, [ & ] {
552
im:: Table ( " ##shared-table" , 2 , [ & ] {
553
ImGui:: TableSetupColumn ( " Label" , ImGuiTableColumnFlags_WidthFixed) ;
554
ImGui:: TableSetupColumn ( " Value" ) ;
555
if ( ImGui:: TableNextColumn ( ) ) {
556
ImGui:: TextUnformatted ( " Brand and model" ) ;
557
}
558
if ( ImGui:: TableNextColumn ( ) ) {
559
if ( ImGui:: Selectable ( info. displayName . c_str ( ) ) ) {
560
showSelectMachine = true ;
561
}
562
}
563
if ( ImGui:: IsItemHovered ( ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_Stationary ) ) {
564
im:: ItemTooltip ( [ & ] {
565
printConfigInfo ( info ) ;
566
} ) ;
567
}
568
} ) ;
569
} ) ;
570
}
571
572
void ImGuiMachine:: showSetupOverviewExtensions ( MSXMotherBoard& motherBoard , Mode mode , ImGuiTreeNodeFlags flags )
573
{
574
im:: TreeNodeWithoutID ( depthNodeNames[ SetupDepth:: EXTENSIONS] . c_str ( ) , flags, [ & ] {
575
const auto & slotManager = motherBoard. getSlotManager ( ) ;
576
bool anySlot = false ;
577
im:: Table ( " ##shared-table" , 2 , [ & ] {
578
ImGui:: TableSetupColumn ( " Label" , ImGuiTableColumnFlags_WidthFixed) ;
579
ImGui:: TableSetupColumn ( " Value" ) ;
580
for ( auto i : xrange ( CartridgeSlotManager:: MAX_SLOTS ) ) {
581
if ( ! slotManager. slotExists ( i ) ) continue ;
582
anySlot = true ;
583
if ( ImGui:: TableNextColumn ( ) ) {
584
ImGui:: StrCat ( " Cartridge Slot " , char ( ' A' + i) , " (" , slotManager. getPsSsString ( i ) , " )" ) ;
585
}
586
if ( ImGui:: TableNextColumn ( ) ) {
587
const auto * config = slotManager. getConfigForSlot ( i ) ;
588
if ( mode == Mode:: EDIT) {
589
const std:: string currentConfigName = config ?
590
( config-> getType ( ) == HardwareConfig:: Type:: EXTENSION ? manager. media -> displayNameForExtension ( config-> getConfigName ( ) ) :
591
manager. media -> displayNameForRom ( std:: string ( config-> getRomFilename ( ) ) , true ) ) :
592
std:: string ( EMPTY ) ;
593
594
ImGui:: SetNextWindowSizeConstraints ( { 20.0f * ImGui:: GetFontSize ( ) , 0.0f } , { FLT_MAX, FLT_MAX} ) ;
595
im:: Menu ( strCat ( currentConfigName, " ##" , i ) . c_str ( ) , [ & ] {
596
manager. media -> showExtensionSelector ( i, currentConfigName ) ;
597
if ( config) {
598
if ( ImGui:: Button ( " Remove" ) ) {
599
manager. executeDelayed ( makeTclList ( " remove_extension" , config-> getName ( ) ) ) ;
600
}
601
}
602
} ) ;
603
} else {
604
if ( config) {
605
if ( config-> getType ( ) == HardwareConfig:: Type:: EXTENSION) {
606
ImGui:: TextUnformatted ( manager. media -> displayNameForExtension ( config-> getConfigName ( ) ) ) ;
607
if ( auto * extInfo = manager. media -> findExtensionInfo ( config-> getConfigName ( ) ) ) {
608
manager. media -> extensionTooltip ( * extInfo) ;
609
}
610
} else {
611
ImGui:: TextDisabledUnformatted ( manager. media -> displayNameForRom ( std:: string ( config-> getRomFilename ( ) ) , true ) ) ;
612
}
613
} else {
614
ImGui:: TextUnformatted ( EMPTY ) ;
615
}
616
}
617
}
618
}
619
if ( ! anySlot) {
620
ImGui:: TextDisabledUnformatted ( " No cartridge slots present" ) ;
621
}
622
623
for ( const auto & ext : motherBoard. getExtensions ( ) ) {
624
if ( ! slotManager. findSlotWith ( * ext) ) {
625
if ( ImGui:: TableNextColumn ( ) ) {
626
ImGui:: TextUnformatted ( " I/O only" ) ;
627
}
628
if ( ImGui:: TableNextColumn ( ) ) {
629
auto displayName = manager. media -> displayNameForExtension ( ext-> getConfigName ( ) ) ;
630
if ( mode == Mode:: EDIT) {
631
im:: Menu ( displayName. c_str ( ) , [ & ] {
632
if ( ImGui:: Button ( " Remove" ) ) {
633
manager. executeDelayed ( makeTclList ( " remove_extension" , ext-> getName ( ) ) ) ;
634
}
635
} ) ;
636
} else {
637
ImGui:: TextUnformatted ( displayName ) ;
638
if ( auto * extInfo = manager. media -> findExtensionInfo ( ext-> getConfigName ( ) ) ) {
639
manager. media -> extensionTooltip ( * extInfo) ;
640
}
641
}
642
}
643
}
644
}
645
} ) ;
646
} ) ;
647
}
648
649
void ImGuiMachine:: showSetupOverviewConnectors ( MSXMotherBoard& motherBoard , Mode mode , ImGuiTreeNodeFlags flags )
650
{
651
im:: TreeNodeWithoutID ( depthNodeNames[ SetupDepth:: CONNECTORS] . c_str ( ) , flags, [ & ] {
652
using enum ImGuiConnector:: Mode;
653
manager. connector -> showPluggables ( motherBoard. getPluggingController ( ) , mode == Mode:: EDIT ? SUBMENU : VIEW ) ;
654
} ) ;
655
}
656
657
void ImGuiMachine:: showSetupOverviewMedia ( MSXMotherBoard& motherBoard , Mode mode , ImGuiTreeNodeFlags flags )
658
{
659
im:: TreeNodeWithoutID ( depthNodeNames[ SetupDepth:: MEDIA] . c_str ( ) , flags, [ & ] {
660
im:: Table ( " ##shared-table" , 2 , [ & ] {
661
ImGui:: TableSetupColumn ( " Label" , ImGuiTableColumnFlags_WidthFixed) ;
662
ImGui:: TableSetupColumn ( " Value" ) ;
663
for ( const auto & media : motherBoard. getMediaProviders ( ) ) {
664
TclObject info;
665
media. provider -> getMediaInfo ( info ) ;
666
if ( auto target = info. getOptionalDictValue ( TclObject ( " target" ) ) ) {
667
bool isEmpty = target-> getString ( ) . empty ( ) ;
668
auto targetStr = isEmpty ? EMPTY : target-> getString ( ) ;
669
670
auto formatMediaName = [ ] ( std:: string_view name) {
671
constexpr auto multiSlotMediaDeviceTab = std:: to_array < std:: pair< std:: string_view, std:: string_view> > ( {
672
{ " cart" , " Cartridge Slot" } ,
673
{ " disk" , " Disk Drive" } ,
674
{ " hd" , " Hard Disk" } ,
675
{ " cd" , " CDROM Drive" } ,
676
{ " ls" , " LS120 Drive" } ,
677
} ) ;
678
for ( auto [ s, l] : multiSlotMediaDeviceTab) {
679
if ( name. starts_with ( s ) ) {
680
return strCat ( l, ' ' , char ( ' A' + ( name. back ( ) - ' a' ) ) ) ;
681
}
682
}
683
constexpr auto singleSlotMediaDeviceTab = std:: to_array < std:: pair< std:: string_view, std:: string_view> > ( {
684
{ " cassetteplayer" , " Tape Deck" } ,
685
{ " laserdiscplayer" , " LaserDisc Player" } ,
686
} ) ;
687
for ( const auto & [ s, l] : singleSlotMediaDeviceTab) {
688
if ( name == s) return std:: string ( l ) ;
689
}
690
691
return std:: string ( name ) ;
692
} ;
693
694
if ( media. name . starts_with ( " cart" ) ) {
695
unsigned num = media. name [ 4 ] - ' a' ;
696
const auto & slotManager = motherBoard. getSlotManager ( ) ;
697
if ( ImGui:: TableNextColumn ( ) ) {
698
ImGui:: StrCat ( formatMediaName ( media. name ) , " (" , slotManager. getPsSsString ( num ) , " )" ) ;
699
}
700
if ( ImGui:: TableNextColumn ( ) ) {
701
auto type = info. getOptionalDictValue ( TclObject ( " type" ) ) ;
702
if ( type && type-> getString ( ) == " extension" ) {
703
auto displayName = manager. media -> displayNameForExtension ( targetStr ) ;
704
if ( mode == Mode:: EDIT) {
705
im:: Menu ( strCat ( displayName, " ##" , num ) . c_str ( ) , [ & ] {
706
if ( ImGui:: Button ( " Remove" ) ) {
707
const auto * config = slotManager. getConfigForSlot ( num ) ;
708
manager. executeDelayed ( makeTclList ( " remove_extension" , config-> getName ( ) ) ) ;
709
}
710
} ) ;
711
} else {
712
ImGui:: TextDisabledUnformatted ( displayName ) ;
713
}
714
} else {
715
auto displayName = isEmpty ? std:: string ( EMPTY ) : manager. media -> displayNameForRom ( std:: string ( targetStr ) , true ) ;
716
if ( mode == Mode:: EDIT) {
717
if ( ImGui:: Selectable ( strCat ( displayName, " ##" , media. name ) . c_str ( ) , false ) ) {
718
manager. media -> showMediaWindow ( media. name ) ;
719
}
720
} else {
721
ImGui:: TextUnformatted ( displayName ) ;
722
}
723
if ( ! isEmpty) {
724
im:: ItemTooltip ( [ & ] {
725
RomType romType = RomType:: UNKNOWN;
726
if ( auto mapper = info. getOptionalDictValue ( TclObject ( " mappertype" ) ) ) {
727
romType = RomInfo:: nameToRomType ( mapper-> getString ( ) ) ;
728
}
729
ImGuiMedia:: printRomInfo ( manager, info, targetStr, romType ) ;
730
} ) ;
731
}
732
}
733
}
734
} else if ( mode != Mode:: EDIT || media. name . starts_with ( " disk" ) || media. name . starts_with ( " cassette" ) ) {
735
if ( ImGui:: TableNextColumn ( ) ) {
736
ImGui:: TextUnformatted ( formatMediaName ( media. name ) ) ;
737
}
738
if ( ImGui:: TableNextColumn ( ) ) {
739
if ( mode == Mode:: EDIT) {
740
if ( ImGui:: Selectable ( strCat ( FileOperations:: getFilename ( targetStr ) , " ##" , media. name ) . c_str ( ) , false ) ) {
741
manager. media -> showMediaWindow ( media. name ) ;
742
}
743
} else {
744
ImGui:: TextUnformatted ( FileOperations:: getFilename ( targetStr ) ) ;
745
}
746
if ( ! isEmpty) {
747
simpleToolTip ( targetStr ) ;
748
}
749
}
750
} else {
751
auto formattedMediaName = formatMediaName ( media. name ) ;
752
if ( ImGui:: TableNextColumn ( ) ) {
753
ImGui:: TextUnformatted ( formattedMediaName ) ;
754
}
755
if ( ImGui:: TableNextColumn ( ) ) {
756
im:: Menu ( strCat ( FileOperations:: getFilename ( targetStr ) , " ##" , media. name ) . c_str ( ) , [ & ] {
757
if ( media. name . starts_with ( " laserdisc" ) ) {
758
manager. media -> paintLaserDiscMenuContent ( media. name , formattedMediaName, * target ) ;
759
} else if ( media. name . starts_with ( " hd" ) ) {
760
manager. media -> paintHardDiskMenuContent ( media. name , formattedMediaName, * target, motherBoard ) ;
761
} else if ( media. name . starts_with ( " cd" ) ) {
762
manager. media -> paintCDROMMenuContent ( media. name , formattedMediaName, * target ) ;
763
}
764
} ) ;
765
}
766
}
767
}
768
}
769
} ) ;
770
} ) ;
771
}
772
773
void ImGuiMachine:: showSetupOverviewState ( MSXMotherBoard& motherBoard , ImGuiTreeNodeFlags flags )
774
{
775
if ( auto time = motherBoard. getCurrentTime ( ) . toDouble ( ) ; time > 0.0 ) {
776
777
im:: TreeNode ( depthNodeNames[ SetupDepth:: COMPLETE_STATE] . c_str ( ) , flags, [ & ] {
778
ImGui:: StrCat ( " Machine time: " , formatTime ( time ) ) ;
779
} ) ;
780
}
781
}
782
783
784
void ImGuiMachine:: paint ( MSXMotherBoard* motherBoard )
785
{
786
if ( showSelectMachine) {
787
paintSelectMachine ( motherBoard ) ;
788
}
789
if ( showTestHardware) {
790
paintTestHardware ( ) ;
791
}
792
if ( showQuickSetupEditor) {
793
ImGui:: SetNextWindowSize ( ImVec2 ( 0 , 0 ) ) ;
794
im:: Window ( " Quick Setup Editor" , & showQuickSetupEditor, [ & ] {
795
showSetupOverviewEdit ( * motherBoard) ;
796
} ) ;
797
}
798
}
799
800
void ImGuiMachine:: paintSelectMachine ( const MSXMotherBoard* motherBoard )
801
{
802
ImGui:: SetNextWindowSize ( gl:: vec2{ 29 , 26 } * ImGui:: GetFontSize ( ) , ImGuiCond_FirstUseEver ) ;
803
im:: Window ( " Select MSX machine" , & showSelectMachine, [ & ] {
804
auto & reactor = manager. getReactor ( ) ;
805
auto instances = reactor. getMachineIDs ( ) ;
806
auto currentInstance = reactor. getMachineID ( ) ;
807
if ( instances. size ( ) > 1 || currentInstance. empty ( ) ) {
808
ImGui:: TextUnformatted ( " Instances:" sv) ;
809
HelpMarker ( " Switch between different machine instances. Right-click to delete an instance." ) ;
810
im:: Indent ( [ & ] {
811
float height = ( std:: min ( 4.0f , float ( instances. size ( ) ) ) + 0.25f ) * ImGui:: GetTextLineHeightWithSpacing ( ) ;
812
im:: ListBox ( " ##empty" , { - FLT_MIN, height} , [ & ] {
813
im:: ID_for_range ( instances. size ( ) , [ & ] ( int i) {
814
const auto & name = instances[ i] ;
815
bool isCurrent = name == currentInstance;
816
auto board = reactor. getMachine ( name ) ;
817
std:: string display = [ & ] {
818
if ( board) {
819
auto configName = board-> getMachineName ( ) ;
820
const auto * info = findMachineInfo ( configName ) ;
821
auto time = board-> getCurrentTime ( ) . toDouble ( ) ;
822
return strCat ( info ? info-> displayName : configName, " (" , formatTime ( time ) , ' )' ) ;
823
} else {
824
return std:: string ( name ) ;
825
}
826
} ( ) ;
827
if ( ImGui:: Selectable ( display. c_str ( ) , isCurrent ) ) {
828
manager. executeDelayed ( makeTclList ( " activate_machine" , name) ) ;
829
}
830
im:: PopupContextItem ( " instance context menu" , [ & ] {
831
if ( ImGui:: Selectable ( " Delete instance" ) ) {
832
manager. executeDelayed ( makeTclList ( " delete_machine" , name) ) ;
833
}
834
} ) ;
835
} ) ;
836
} ) ;
837
} ) ;
838
ImGui:: Separator ( ) ;
839
}
840
841
if ( motherBoard) {
842
auto configName = motherBoard-> getMachineName ( ) ;
843
if ( auto * info = findMachineInfo ( configName ) ) {
844
std:: string display = strCat ( " Current machine: " , info-> displayName ) ;
845
im:: TreeNode ( display. c_str ( ) , [ & ] {
846
printConfigInfo ( * info) ;
847
} ) ;
848
}
849
if ( newMachineConfig. empty ( ) ) newMachineConfig = configName;
850
ImGui:: Separator ( ) ;
851
}
852
853
auto showMachine = [ & ] ( MachineInfo& info, bool doubleClickToSelect) {
854
bool ok = getTestResult ( info ) . empty ( ) ;
855
im:: StyleColor ( ! ok, ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
856
bool selected = info. configName == newMachineConfig;
857
if ( ImGui:: Selectable ( info. displayName . c_str ( ) , selected,
858
doubleClickToSelect ? ImGuiSelectableFlags_AllowDoubleClick: ImGuiSelectableFlags_None ) ) {
859
newMachineConfig = info. configName ;
860
if ( ok && ( doubleClickToSelect ? ImGui:: IsMouseDoubleClicked ( ImGuiMouseButton_Left ) : true ) ) {
861
showSelectMachine = false ;
862
manager. executeDelayed ( makeTclList ( " machine" , newMachineConfig) ) ;
863
addRecentItem ( recentMachines, newMachineConfig ) ;
864
}
865
}
866
if ( selected) {
867
if ( ImGui:: IsWindowAppearing ( ) ) ImGui:: SetScrollHereY ( ) ;
868
}
869
if ( ImGui:: IsItemHovered ( ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_Stationary ) ) {
870
im:: ItemTooltip ( [ & ] {
871
printConfigInfo ( info ) ;
872
} ) ;
873
}
874
} ) ;
875
} ;
876
877
im:: TreeNode ( " Recently used" , recentMachines. empty ( ) ? ImGuiTreeNodeFlags_None : ImGuiTreeNodeFlags_DefaultOpen, [ & ] {
878
if ( recentMachines. empty ( ) ) {
879
ImGui:: TextUnformatted ( " (none)" sv) ;
880
} else {
881
im:: Combo ( " ##recent" , " Switch to recently used machine:" , [ & ] {
882
for ( const auto & item : recentMachines) {
883
if ( auto * info = findMachineInfo ( item ) ) {
884
showMachine ( * info, false ) ;
885
}
886
}
887
} ) ;
888
simpleToolTip ( " Replace the current with the selected machine." ) ;
889
}
890
} ) ;
891
ImGui:: Separator ( ) ;
892
893
ImGui:: TextUnformatted ( " Available machines:" sv) ;
894
auto & allMachines = getAllMachines ( ) ;
895
auto filterDisplay = tmpStrCat (
896
" filter" ,
897
strCat_if ( ! filterType. empty ( ) || ! filterRegion. empty ( ) || ! filterString. empty ( ) , ' :' ) ,
898
strCat_if ( ! filterType. empty ( ) , ' ' , filterType) ,
899
strCat_if ( ! filterRegion. empty ( ) , ' ' , filterRegion) ,
900
strCat_if ( ! filterString. empty ( ) , ' ' , filterString) ,
901
" ###filter" ) ;
902
im:: TreeNode ( filterDisplay. c_str ( ) , ImGuiTreeNodeFlags_DefaultOpen, [ & ] {
903
displayFilterCombo ( filterType, " Type" , allMachines ) ;
904
displayFilterCombo ( filterRegion, " Region" , allMachines ) ;
905
if ( ImGui:: IsWindowAppearing ( ) ) ImGui:: SetKeyboardFocusHere ( ) ;
906
ImGui:: InputText ( ICON_IGFD_FILTER, & filterString ) ;
907
simpleToolTip ( " A list of substrings that must be part of the machine name.\n "
908
" \n "
909
" For example: enter 'pa' to search for 'Panasonic' machines. "
910
" Then refine the search by appending '<space>st' to find the 'Panasonic FS-A1ST' machine." ) ;
911
} ) ;
912
913
914
915
auto filteredMachines = to_vector ( xrange ( allMachines. size ( ) ) ) ;
916
917
bool anyNonWorking = [ & ] {
918
919
920
921
922
auto testNonWorking = [ ] ( const MachineInfo& info) {
923
return info. testResult
924
&& ! info. testResult -> empty ( ) ;
925
} ;
926
if ( hideNonWorking) {
927
928
std:: erase_if ( filteredMachines, [ & ] ( auto idx) {
929
return testNonWorking ( allMachines[ idx] ) ;
930
} ) ;
931
return filteredMachines. size ( ) != allMachines. size ( ) ;
932
} else {
933
934
return std:: ranges:: any_of ( allMachines, testNonWorking ) ;
935
}
936
} ( ) ;
937
938
applyComboFilter ( " Type" , filterType, allMachines, filteredMachines) ;
939
applyComboFilter ( " Region" , filterRegion, allMachines, filteredMachines) ;
940
applyDisplayNameFilter ( filterString, allMachines, filteredMachines ) ;
941
942
auto it = std:: ranges:: find ( filteredMachines, newMachineConfig,
943
[ & ] ( auto idx) { return allMachines[ idx] . configName ; } ) ;
944
bool inFilteredList = it != filteredMachines. end ( ) ;
945
int selectedIdx = inFilteredList ? narrow < int > ( * it) : - 1 ;
946
947
auto bottomSpacing = ( anyNonWorking ? 2 : 1 ) * ImGui:: GetFrameHeightWithSpacing ( ) ;
948
im:: ListBox ( " ##list" , { - FLT_MIN, - bottomSpacing} , [ & ] {
949
im:: ListClipper ( filteredMachines. size ( ) , selectedIdx, [ & ] ( int i) {
950
auto idx = filteredMachines[ i] ;
951
auto & info = allMachines[ idx] ;
952
showMachine ( info, true ) ;
953
} ) ;
954
} ) ;
955
956
if ( anyNonWorking) {
957
ImGui:: Checkbox ( " Hide non-working machines" , & hideNonWorking) ;
958
HelpMarker ( " When non-working machines are hidden, you can still use 'Machine > Test MSX hardware' to see more details about (non-)working machines, instead of hovering the cursor on the red item." ) ;
959
}
960
961
bool ok = [ & ] {
962
if ( ! inFilteredList) return false ;
963
auto * info = findMachineInfo ( newMachineConfig ) ;
964
if ( ! info) return false ;
965
const auto & test = getTestResult ( * info) ;
966
return test. empty ( ) ;
967
} ( ) ;
968
im:: Disabled ( ! ok, [ & ] {
969
if ( ImGui:: Button ( " Replace current machine" ) ) {
970
manager. executeDelayed ( makeTclList ( " machine" , newMachineConfig) ) ;
971
}
972
simpleToolTip ( " Replace the current machine with the selected machine. "
973
" Alternatively you can also double click in the list above (in addition that also closes this window)." ) ;
974
ImGui:: SameLine ( 0.0f , 10.0f ) ;
975
if ( ImGui:: Button ( " New machine instance" ) ) {
976
std:: string script = strCat (
977
" set id [create_machine]\n "
978
" set err [catch {${id}::load_machine " , newMachineConfig, " } error_result]\n "
979
" if {$err} {\n "
980
" delete_machine $id\n "
981
" error \" Error activating new machine: $error_result\" \n "
982
" } else {\n "
983
" activate_machine $id\n "
984
" }\n " ) ;
985
manager. executeDelayed ( TclObject ( script ) ) ;
986
}
987
simpleToolTip ( " Create a new machine instance (next to the current machine). "
988
" Later you can switch between the different instances (like different tabs in a web browser)." ) ;
989
} ) ;
990
} ) ;
991
}
992
993
void ImGuiMachine:: paintTestHardware ( )
994
{
995
ImGui:: SetNextWindowSize ( gl:: vec2{ 41.0f , 32.5f } * ImGui:: GetFontSize ( ) , ImGuiCond_FirstUseEver ) ;
996
im:: Window ( " Test MSX hardware" , & showTestHardware, [ & ] {
997
auto formatNum = [ ] ( size_t num, std:: string_view text) {
998
if ( num == 0 ) {
999
return strCat ( " No " , text, " ###" , text) ;
1000
} else {
1001
return strCat ( num, ' ' , text, " ###" , text ) ;
1002
}
1003
} ;
1004
auto printList = [ ] ( const auto & indices, const auto & infos) {
1005
auto n = narrow < int > ( indices. size ( ) ) ;
1006
auto clamped = std:: clamp ( narrow_cast < float > ( n ) , 2.5f , 7.5f ) ;
1007
gl:: vec2 listSize{ 0.0f , clamped * ImGui:: GetTextLineHeightWithSpacing ( ) } ;
1008
1009
ImGui:: SetNextItemWidth ( - FLT_MIN) ;
1010
im:: ListBox ( " ##workingList" , listSize, [ & ] {
1011
im:: ListClipper ( n, [ & ] ( int i) {
1012
auto & info = infos[ indices[ i] ] ;
1013
ImGui:: TextUnformatted ( info. displayName ) ;
1014
assert ( info. testResult ) ;
1015
simpleToolTip ( * info. testResult ) ;
1016
} ) ;
1017
} ) ;
1018
} ;
1019
1020
bool doTest = true ;
1021
1022
auto & allMachines = getAllMachines ( ) ;
1023
std:: vector< size_t > workingMachines, nonWorkingMachines;
1024
for ( auto [ idx, info] : enumerate ( allMachines ) ) {
1025
if ( ! info. testResult . has_value ( ) ) {
1026
if ( ! doTest) continue ;
1027
doTest = false ;
1028
}
1029
const auto & result = getTestResult ( info ) ;
1030
( result. empty ( ) ? workingMachines : nonWorkingMachines) . push_back ( idx ) ;
1031
}
1032
bool allMachinesTested = allMachines. size ( ) == ( workingMachines. size ( ) + nonWorkingMachines. size ( ) ) ;
1033
1034
im:: VisuallyDisabled ( ! allMachinesTested, [ & ] {
1035
im:: TreeNode ( " Machines" , ImGuiTreeNodeFlags_DefaultOpen, [ & ] {
1036
auto workingText = formatNum ( workingMachines. size ( ) , " working machines" ) ;
1037
im:: TreeNode ( workingText. c_str ( ) , [ & ] {
1038
printList ( workingMachines, allMachines ) ;
1039
} ) ;
1040
auto nonWorkingText = formatNum ( nonWorkingMachines. size ( ) , " non-working machines" ) ;
1041
int flags = nonWorkingMachines. empty ( ) ? 0 : ImGuiTreeNodeFlags_DefaultOpen;
1042
im:: TreeNode ( nonWorkingText. c_str ( ) , flags, [ & ] {
1043
printList ( nonWorkingMachines, allMachines ) ;
1044
} ) ;
1045
} ) ;
1046
} ) ;
1047
1048
auto & allExtensions = manager. media -> getAllExtensions ( ) ;
1049
std:: vector< size_t > workingExtensions, nonWorkingExtensions;
1050
for ( auto [ idx, info] : enumerate ( allExtensions ) ) {
1051
if ( ! info. testResult . has_value ( ) ) {
1052
if ( ! doTest) continue ;
1053
doTest = false ;
1054
}
1055
const auto & result = manager. media -> getTestResult ( info ) ;
1056
( result. empty ( ) ? workingExtensions : nonWorkingExtensions) . push_back ( idx ) ;
1057
}
1058
bool allExtensionsTested = allExtensions. size ( ) == ( workingExtensions. size ( ) + nonWorkingExtensions. size ( ) ) ;
1059
1060
im:: VisuallyDisabled ( ! allExtensionsTested, [ & ] {
1061
im:: TreeNode ( " Extensions" , ImGuiTreeNodeFlags_DefaultOpen, [ & ] {
1062
auto workingText = formatNum ( workingExtensions. size ( ) , " working extensions" ) ;
1063
im:: TreeNode ( workingText. c_str ( ) , [ & ] {
1064
printList ( workingExtensions, allExtensions ) ;
1065
} ) ;
1066
auto nonWorkingText = formatNum ( nonWorkingExtensions. size ( ) , " non-working extensions" ) ;
1067
int flags = nonWorkingExtensions. empty ( ) ? 0 : ImGuiTreeNodeFlags_DefaultOpen;
1068
im:: TreeNode ( nonWorkingText. c_str ( ) , flags, [ & ] {
1069
printList ( nonWorkingExtensions, allExtensions ) ;
1070
} ) ;
1071
1072
} ) ;
1073
} ) ;
1074
1075
if ( ! nonWorkingMachines. empty ( ) || ! nonWorkingExtensions. empty ( ) ) {
1076
im:: Disabled ( ! allMachinesTested || ! allExtensionsTested, [ & ] {
1077
if ( ImGui:: Button ( " Copy list of non-working hardware to clipboard" ) ) {
1078
std:: string result;
1079
auto print = [ & ] ( std:: string_view label, const auto & indices, const auto & infos) {
1080
if ( ! indices. empty ( ) ) {
1081
strAppend ( result, " Non-working " , label, " :\n " ) ;
1082
for ( auto idx : indices) {
1083
const auto & info = infos[ idx] ;
1084
strAppend ( result, ' \t ' , info. displayName , " (" , info. configName , " )\n " ,
1085
" \t \t " , * info. testResult , ' \n ' ) ;
1086
}
1087
strAppend ( result, ' \n ' ) ;
1088
}
1089
} ;
1090
print ( " machines" , nonWorkingMachines, allMachines) ;
1091
print ( " extensions" , nonWorkingExtensions, allExtensions) ;
1092
ImGui:: SetClipboardText ( result. c_str ( ) ) ;
1093
}
1094
} ) ;
1095
}
1096
1097
im:: Disabled ( ! allMachinesTested || ! allExtensionsTested, [ & ] {
1098
if ( ImGui:: Button ( " Rerun test" ) ) {
1099
manager. media -> resetExtensionInfo ( ) ;
1100
machineInfo. clear ( ) ;
1101
}
1102
} ) ;
1103
ImGui:: Separator ( ) ;
1104
1105
1106
1107
1108
if ( ImGui:: Button ( " Open user system ROMs folder" ) ) {
1109
SDL_OpenURL ( strCat ( " file://" , FileOperations:: getUserDataDir ( ) , " /systemroms" ) . c_str ( ) ) ;
1110
}
1111
if ( ImGui:: Button ( " Open system wide system ROMs folder" ) ) {
1112
SDL_OpenURL ( strCat ( " file://" , FileOperations:: getSystemDataDir ( ) , " /systemroms" ) . c_str ( ) ) ;
1113
}
1114
} ) ;
1115
}
1116
1117
std:: vector< ImGuiMachine:: MachineInfo> & ImGuiMachine:: getAllMachines ( )
1118
{
1119
if ( machineInfo. empty ( ) ) {
1120
machineInfo = parseAllConfigFiles < MachineInfo> ( manager, " machines" , { " Manufacturer" sv, " Product code" sv} ) ;
1121
}
1122
return machineInfo;
1123
}
1124
1125
static void amendConfigInfo ( MSXMotherBoard& mb , ImGuiMachine:: MachineInfo& info )
1126
{
1127
auto & configInfo = info. configInfo ;
1128
1129
const auto & debugger = mb. getDebugger ( ) ;
1130
unsigned ramSize = 0 ;
1131
for ( const auto & [ name, debuggable] : debugger. getDebuggables ( ) ) {
1132
if ( debuggable-> getDescription ( ) == one_of ( " memory mapper" , " ram" ) ) {
1133
ramSize += debuggable-> getSize ( ) ;
1134
}
1135
}
1136
configInfo. emplace_back ( " RAM size" , strCat ( ramSize / 1024 , " kB" ) ) ;
1137
1138
if ( auto * vdp = dynamic_cast < VDP* > ( mb. findDevice ( " VDP" ) ) ) {
1139
configInfo. emplace_back ( " VRAM size" , strCat ( vdp-> getVRAM ( ) . getSize ( ) / 1024 , " kB" ) ) ;
1140
configInfo. emplace_back ( " VDP version" , vdp-> getVersionString ( ) ) ;
1141
}
1142
1143
if ( auto drives = RealDrive:: getDrivesInUse ( mb ) ) {
1144
configInfo. emplace_back ( " Disk drives" , strCat ( narrow < int > ( drives-> count ( ) ) ) ) ;
1145
}
1146
1147
const auto & carts = mb. getSlotManager ( ) ;
1148
configInfo. emplace_back ( " Cartridge slots" , strCat ( carts. getNumberOfSlots ( ) ) ) ;
1149
}
1150
1151
const std:: string& ImGuiMachine:: getTestResult ( MachineInfo& info )
1152
{
1153
if ( ! info. testResult ) {
1154
info. testResult . emplace ( ) ;
1155
1156
auto & reactor = manager. getReactor ( ) ;
1157
std:: string configName = info. configName ;
1158
manager. executeDelayed ( [ & reactor, configName, this ] ( ) mutable {
1159
1160
try {
1161
auto * currentInfo = findMachineInfo ( configName ) ;
1162
if ( ! currentInfo) return ;
1163
1164
MSXMotherBoard mb ( reactor ) ;
1165
mb. getMSXCliComm ( ) . setSuppressMessages ( true ) ;
1166
mb. loadMachine ( configName ) ;
1167
1168
amendConfigInfo ( mb, * currentInfo ) ;
1169
} catch ( const MSXException& e) {
1170
if ( auto * currentInfo = findMachineInfo ( configName ) ) {
1171
currentInfo-> testResult = e. getMessage ( ) ;
1172
}
1173
} catch ( const std:: exception& e) {
1174
if ( auto * currentInfo = findMachineInfo ( configName ) ) {
1175
currentInfo-> testResult = strCat ( " std::exception: " , e. what ( ) ) ;
1176
}
1177
}
1178
} ) ;
1179
}
1180
return info. testResult . value ( ) ;
1181
}
1182
1183
bool ImGuiMachine:: printConfigInfo ( MachineInfo& info )
1184
{
1185
const auto & test = getTestResult ( info ) ;
1186
bool ok = test. empty ( ) ;
1187
if ( ok) {
1188
im:: Table ( " ##machine-info" , 2 , ImGuiTableFlags_SizingFixedFit, [ & ] {
1189
float maxValueWidth = 0.0f ;
1190
for ( const auto & [ desc, value] : info. configInfo ) {
1191
maxValueWidth = std:: max ( maxValueWidth, ImGui:: CalcTextSize ( value ) . x ) ;
1192
}
1193
auto width = std:: min ( ImGui:: GetFontSize ( ) * 20.0f , maxValueWidth) ;
1194
1195
ImGui:: TableSetupColumn ( " dummy" ) ;
1196
ImGui:: TableSetupColumn ( " dummy" , ImGuiTableColumnFlags_WidthFixed, width) ;
1197
1198
for ( const auto & [ desc, value] : info. configInfo ) {
1199
if ( ImGui:: TableNextColumn ( ) ) {
1200
ImGui:: TextUnformatted ( desc ) ;
1201
}
1202
if ( ImGui:: TableNextColumn ( ) ) {
1203
im:: TextWrapPos ( 0.0f , [ & ] {
1204
ImGui:: TextUnformatted ( value ) ;
1205
} ) ;
1206
}
1207
}
1208
} ) ;
1209
} else {
1210
im:: StyleColor ( ImGuiCol_Text, getColor ( imColor:: ERROR ) , [ & ] {
1211
im:: TextWrapPos ( ImGui:: GetFontSize ( ) * 35.0f , [ & ] {
1212
ImGui:: TextUnformatted ( test ) ;
1213
} ) ;
1214
} ) ;
1215
}
1216
return ok;
1217
}
1218
1219
ImGuiMachine:: MachineInfo* ImGuiMachine:: findMachineInfo ( std:: string_view config )
1220
{
1221
auto & allMachines = getAllMachines ( ) ;
1222
auto it = std:: ranges:: find ( allMachines, config, & MachineInfo:: configName ) ;
1223
return ( it != allMachines. end ( ) ) ? std:: to_address ( it ) : nullptr ;
1224
}
1225
1226
}