39 |
static const String g_prompt = "lscp=# "; |
static const String g_prompt = "lscp=# "; |
40 |
static std::vector<String> g_commandHistory; |
static std::vector<String> g_commandHistory; |
41 |
static int g_commandHistoryIndex = -1; |
static int g_commandHistoryIndex = -1; |
42 |
|
static String g_doc; |
43 |
|
|
44 |
static void printUsage() { |
static void printUsage() { |
45 |
cout << "lscp - The LinuxSampler Control Protocol (LSCP) Shell." << endl; |
cout << "lscp - The LinuxSampler Control Protocol (LSCP) Shell." << endl; |
52 |
cout << endl; |
cout << endl; |
53 |
cout << " --no-auto-correct Don't perform auto correction of obvious syntax errors." << endl; |
cout << " --no-auto-correct Don't perform auto correction of obvious syntax errors." << endl; |
54 |
cout << endl; |
cout << endl; |
55 |
|
cout << " --no-doc Don't show LSCP reference documentation on screen." << endl; |
56 |
|
cout << endl; |
57 |
} |
} |
58 |
|
|
59 |
static void printWelcome() { |
static void printWelcome() { |
134 |
g_commandHistory.push_back(sCommand); |
g_commandHistory.push_back(sCommand); |
135 |
} |
} |
136 |
|
|
137 |
|
/// Splits the given string into individual lines for the given screen resolution. |
138 |
|
static std::vector<String> splitForScreen(const String& s, int cols, int rows) { |
139 |
|
std::vector<String> lines; |
140 |
|
if (rows <= 0 || cols <= 0) return lines; |
141 |
|
String line; |
142 |
|
for (int i = 0; i < s.size(); ++i) { |
143 |
|
char c = s[i]; |
144 |
|
if (c == '\r') continue; |
145 |
|
if (c == '\n') { |
146 |
|
lines.push_back(line); |
147 |
|
if (lines.size() >= rows) return lines; |
148 |
|
line.clear(); |
149 |
|
continue; |
150 |
|
} |
151 |
|
line += c; |
152 |
|
if (line.size() >= cols) { |
153 |
|
lines.push_back(line); |
154 |
|
if (lines.size() >= rows) return lines; |
155 |
|
line.clear(); |
156 |
|
} |
157 |
|
} |
158 |
|
return lines; |
159 |
|
} |
160 |
|
|
161 |
|
/** |
162 |
|
* Will be called whenever the LSCP documentation reference to be shown, has |
163 |
|
* been changed. This call will accordingly update the screen with the new |
164 |
|
* documentation text received from LSCP server. |
165 |
|
*/ |
166 |
|
static void updateDoc() { |
167 |
|
const int vOffset = 2; |
168 |
|
|
169 |
|
CCursor originalCursor = CCursor::now(); |
170 |
|
CCursor cursor = originalCursor; |
171 |
|
cursor.toColumn(0).down(vOffset); |
172 |
|
|
173 |
|
// wipe out current documentation off screen |
174 |
|
cursor.clearVerticalToBottom(); |
175 |
|
|
176 |
|
if (g_doc.empty()) { |
177 |
|
// restore original cursor position |
178 |
|
cursor.up(vOffset).toColumn(originalCursor.column()); |
179 |
|
return; |
180 |
|
} |
181 |
|
|
182 |
|
// get screen size (in characters) |
183 |
|
const int cols = TerminalCtrl::columns(); |
184 |
|
const int rows = TerminalCtrl::rows(); |
185 |
|
|
186 |
|
// convert the string block into individual lines according to screen resolution |
187 |
|
std::vector<String> lines = splitForScreen(g_doc, cols - 1, rows); |
188 |
|
|
189 |
|
// print lines onto screen |
190 |
|
for (int row = 0; row < lines.size(); ++row) |
191 |
|
std::cout << lines[row] << std::endl; |
192 |
|
|
193 |
|
// restore original cursor position |
194 |
|
cursor.up(vOffset + lines.size()).toColumn(originalCursor.column()); |
195 |
|
} |
196 |
|
|
197 |
/** |
/** |
198 |
* This LSCP shell application is designed as thin client. That means the heavy |
* This LSCP shell application is designed as thin client. That means the heavy |
199 |
* LSCP grammar evaluation tasks are peformed by the LSCP server and the shell |
* LSCP grammar evaluation tasks are peformed by the LSCP server and the shell |
258 |
String host = LSCP_DEFAULT_HOST; |
String host = LSCP_DEFAULT_HOST; |
259 |
int port = LSCP_DEFAULT_PORT; |
int port = LSCP_DEFAULT_PORT; |
260 |
bool autoCorrect = true; |
bool autoCorrect = true; |
261 |
|
bool showDoc = true; |
262 |
|
|
263 |
// parse command line arguments |
// parse command line arguments |
264 |
for (int i = 0; i < argc; ++i) { |
for (int i = 0; i < argc; ++i) { |
281 |
} |
} |
282 |
} else if (s == "--no-auto-correct") { |
} else if (s == "--no-auto-correct") { |
283 |
autoCorrect = false; |
autoCorrect = false; |
284 |
|
} else if (s == "--no-doc") { |
285 |
|
showDoc = false; |
286 |
} else if (s[0] == '-') { // invalid / unknown command line argument ... |
} else if (s[0] == '-') { // invalid / unknown command line argument ... |
287 |
printUsage(); |
printUsage(); |
288 |
return -1; |
return -1; |
292 |
// try to connect to the sampler's LSCP server and start a thread for |
// try to connect to the sampler's LSCP server and start a thread for |
293 |
// receiving incoming network data from the sampler's LSCP server |
// receiving incoming network data from the sampler's LSCP server |
294 |
g_client = new LSCPClient; |
g_client = new LSCPClient; |
|
g_client->setCallback(onLSCPClientNewInputAvailable); |
|
295 |
g_client->setErrorCallback(onLSCPClientErrorOccured); |
g_client->setErrorCallback(onLSCPClientErrorOccured); |
296 |
if (!g_client->connect(host, port)) return -1; |
if (!g_client->connect(host, port)) return -1; |
297 |
|
g_client->sendCommandSync( |
298 |
|
(showDoc) ? "SET SHELL DOC 1" : "SET SHELL DOC 0" |
299 |
|
); |
300 |
String sResponse = g_client->sendCommandSync( |
String sResponse = g_client->sendCommandSync( |
301 |
(autoCorrect) ? "SET SHELL AUTO_CORRECT 1" : "SET SHELL AUTO_CORRECT 0" |
(autoCorrect) ? "SET SHELL AUTO_CORRECT 1" : "SET SHELL AUTO_CORRECT 0" |
302 |
); |
); |
305 |
cerr << "Error: sampler too old, it does not support shell instructions\n"; |
cerr << "Error: sampler too old, it does not support shell instructions\n"; |
306 |
return -1; |
return -1; |
307 |
} |
} |
308 |
|
g_client->setCallback(onLSCPClientNewInputAvailable); |
309 |
|
|
310 |
printWelcome(); |
printWelcome(); |
311 |
printPrompt(); |
printPrompt(); |
312 |
|
|
434 |
if (p.linesAdvanced()) cursor.up(p.linesAdvanced()); |
if (p.linesAdvanced()) cursor.up(p.linesAdvanced()); |
435 |
cursor.toColumn(cursorColumn + promptOffset()); |
cursor.toColumn(cursorColumn + promptOffset()); |
436 |
} |
} |
437 |
|
} else if (line.substr(0,4) == "SHD:") { // new LSCP doc reference section received ... |
438 |
|
int code = LSCP_SHD_NO_MATCH; |
439 |
|
int res = sscanf(line.c_str(), "SHD:%d", &code); |
440 |
|
g_doc.clear(); |
441 |
|
if (code == LSCP_SHD_MATCH) { |
442 |
|
while (true) { // multi-line response expected (terminated by dot line) ... |
443 |
|
if (line.substr(0, 1) == ".") break; |
444 |
|
if (!g_client->lineAvailable()) break; |
445 |
|
line = *g_client->popLine(); |
446 |
|
g_doc += line; |
447 |
|
} |
448 |
|
} |
449 |
|
updateDoc(); |
450 |
} else if (line.substr(0,2) == "OK") { // single-line response expected ... |
} else if (line.substr(0,2) == "OK") { // single-line response expected ... |
451 |
cout << endl << flush; |
cout << endl << flush; |
452 |
|
|
453 |
|
// wipe out potential current documentation off screen |
454 |
|
CCursor cursor = CCursor::now(); |
455 |
|
cursor.clearVerticalToBottom(); |
456 |
|
|
457 |
CFmt cfmt; |
CFmt cfmt; |
458 |
cfmt.green(); |
cfmt.green(); |
459 |
cout << line.substr(0,2) << flush; |
cout << line.substr(0,2) << flush; |
462 |
printPrompt(); |
printPrompt(); |
463 |
} else if (line.substr(0,3) == "WRN") { // single-line response expected ... |
} else if (line.substr(0,3) == "WRN") { // single-line response expected ... |
464 |
cout << endl << flush; |
cout << endl << flush; |
465 |
|
|
466 |
|
// wipe out potential current documentation off screen |
467 |
|
CCursor cursor = CCursor::now(); |
468 |
|
cursor.clearVerticalToBottom(); |
469 |
|
|
470 |
CFmt cfmt; |
CFmt cfmt; |
471 |
cfmt.yellow(); |
cfmt.yellow(); |
472 |
cout << line.substr(0,3) << flush; |
cout << line.substr(0,3) << flush; |
475 |
printPrompt(); |
printPrompt(); |
476 |
} else if (line.substr(0,3) == "ERR") { // single-line response expected ... |
} else if (line.substr(0,3) == "ERR") { // single-line response expected ... |
477 |
cout << endl << flush; |
cout << endl << flush; |
478 |
|
|
479 |
|
// wipe out potential current documentation off screen |
480 |
|
CCursor cursor = CCursor::now(); |
481 |
|
cursor.clearVerticalToBottom(); |
482 |
|
|
483 |
CFmt cfmt; |
CFmt cfmt; |
484 |
cfmt.bold().red(); |
cfmt.bold().red(); |
485 |
cout << line.substr(0,3) << flush; |
cout << line.substr(0,3) << flush; |
488 |
printPrompt(); |
printPrompt(); |
489 |
} else if (g_client->multiLine()) { // multi-line response expected ... |
} else if (g_client->multiLine()) { // multi-line response expected ... |
490 |
cout << endl << flush; |
cout << endl << flush; |
491 |
|
|
492 |
|
// wipe out potential current documentation off screen |
493 |
|
CCursor cursor = CCursor::now(); |
494 |
|
cursor.clearVerticalToBottom(); |
495 |
|
|
496 |
while (true) { |
while (true) { |
497 |
cout << line << endl << flush; |
cout << line << endl << flush; |
498 |
if (line.substr(0, 1) == ".") break; |
if (line.substr(0, 1) == ".") break; |
501 |
} |
} |
502 |
printPrompt(); |
printPrompt(); |
503 |
} else { |
} else { |
504 |
cout << endl << line << endl << flush; |
cout << endl << flush; |
505 |
|
|
506 |
|
// wipe out potential current documentation off screen |
507 |
|
CCursor cursor = CCursor::now(); |
508 |
|
cursor.clearVerticalToBottom(); |
509 |
|
|
510 |
|
cout << line << endl << flush; |
511 |
printPrompt(); |
printPrompt(); |
512 |
} |
} |
513 |
} |
} |