1 /+
2               Copyright Elias Batek 2017 - 2018.
3      Distributed under the Boost Software License, Version 1.0.
4         (See accompanying file LICENSE_1_0.txt or copy at
5               https://www.boost.org/LICENSE_1_0.txt)
6  +/
7 module midigamepad.shell.tui;
8 
9 import core.thread;
10 import core.sys.windows.mmsystem : HMIDIIN, midiInStart, MIM_OPEN, MMRESULT,
11     MMSYSERR_NOERROR;
12 
13 //import core.sys.windows.winuser;
14 import std.conv : ConvException, to;
15 import std.file : exists;
16 import std.json : JSONException;
17 import std.stdio;
18 import std.string : chomp;
19 
20 import midigamepad.lib.keyboard;
21 import midigamepad.lib.midi.input;
22 import midigamepad.lib.translation;
23 import midigamepad.shell.program : runMIDIGamePad;
24 import midigamepad.shell.texts;
25 
26 /++
27     Launches MIDI GamePad's text-based user interface (TUI)
28 
29     NOTE: At the moment it's not a real TUI
30  +/
31 int launchTUI()
32 {
33     printHeader();
34 
35     MIDIDeviceInfo selectedDevice = void;
36     MappingsCollection mappings = void;
37 
38     while (true)
39     {
40         const uint devicesCount = getMIDIInputDevicesCount();
41 
42         if (devicesCount == 0)
43         {
44             // No suitable devices connected
45             writeln("E:\tNo MIDI Input Devices connected.\n\tExiting...  :(");
46             return 1;
47         }
48 
49         write("MIDI Input Devices connected: ");
50         writeln(devicesCount);
51 
52         if (devicesCount == 1)
53         {
54             // Only 1 suitable devices connected, skipping selection
55             selectedDevice = getMIDIInputDeviceInfo(0);
56             break;
57         }
58 
59         // Retrieving all suitable devices
60         // Repeat this every iteration because the user might have (dis-)connected a device
61         MIDIDeviceInfo[] devices = getAllMIDIInputDevices();
62 
63         foreach (dev; devices)
64         {
65             write(dev.id);
66             write(" ... ");
67             writeln(dev.name);
68         }
69 
70         write("Select the input device to use [0]: ");
71         write(" $> ");
72 
73         uint selection = 0;
74 
75         const string userInput = readln().chomp;
76 
77         if (userInput.length == 0)
78         {
79             // Empty selection, use default
80             selectedDevice = devices[0];
81         }
82         else
83         {
84             try
85             {
86                 selection = userInput.to!uint();
87             }
88             catch (ConvException)
89             {
90                 writeln("\nE:\tInvalid device selection  :(\n");
91             }
92 
93             if (selection >= devicesCount)
94             {
95                 // invalid number (no such device)
96                 writeln("\nE:\tUnknown device :(\n");
97             }
98         }
99 
100         selectedDevice = devices[selection];
101         break;
102     }
103 
104     // Show the selection
105     write("\nSelected device:\t(");
106     write(selectedDevice.id);
107     write(") ");
108     writeln(selectedDevice.name);
109 
110     while (true)
111     {
112         writeln("\nWhich mappings file should be loaded: ");
113         write(" $> ");
114         immutable string selectedMappingsFile = readln().chomp;
115 
116         if (!selectedMappingsFile.exists)
117         {
118             writeln("\nE:\tCannot find the specified path  :(\n");
119             continue;
120         }
121 
122         try
123         {
124             mappings = parseFile(selectedMappingsFile);
125             writeln("Mappings file successfully loaded  :D\n");
126             break;
127         }
128         catch (MappingsParserException ex)
129         {
130             writeln();
131             printMappingsParserException(ex);
132             writeln("\n");
133             continue;
134         }
135     }
136 
137     writeln("Bootstrapping ...");
138     return runMIDIGamePad(selectedDevice, mappings, true);
139 }