/[svn]/linuxsampler/trunk/src/shell/lscp.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/shell/lscp.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 2525 by schoenebeck, Mon Feb 24 17:52:51 2014 UTC revision 2532 by schoenebeck, Wed Mar 5 17:29:15 2014 UTC
# Line 17  Line 17 
17  #include "TerminalCtrl.h"  #include "TerminalCtrl.h"
18  #include "CFmt.h"  #include "CFmt.h"
19  #include "CCursor.h"  #include "CCursor.h"
20    #include "TerminalPrinter.h"
21    
22  #include "../common/global.h"  #include "../common/global.h"
23  #include "../common/global_private.h"  #include "../common/global_private.h"
# Line 34  static Condition g_todo; Line 35  static Condition g_todo;
35  static String g_goodPortion;  static String g_goodPortion;
36  static String g_badPortion;  static String g_badPortion;
37  static String g_suggestedPortion;  static String g_suggestedPortion;
38    static int g_linesActive = 0;
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;
# Line 64  static int promptOffset() { Line 66  static int promptOffset() {
66      return g_prompt.size();      return g_prompt.size();
67  }  }
68    
69    static void quitApp(int code = 0) {
70        std::cout << std::endl << std::flush;
71        if (g_client) delete g_client;
72        if (g_keyboardReader) delete g_keyboardReader;
73        exit(code);
74    }
75    
76  // Called by the network reading thread, whenever new data arrived from the  // Called by the network reading thread, whenever new data arrived from the
77  // network connection.  // network connection.
78  static void onLSCPClientNewInputAvailable(LSCPClient* client) {  static void onLSCPClientNewInputAvailable(LSCPClient* client) {
# Line 75  static void onNewKeyboardInputAvailable( Line 84  static void onNewKeyboardInputAvailable(
84      g_todo.Set(true);      g_todo.Set(true);
85  }  }
86    
87    // Called on network error or when server side closed the TCP connection.
88    static void onLSCPClientErrorOccured(LSCPClient* client) {
89        quitApp();
90    }
91    
92    /// Will be called when the user hits tab key to trigger auto completion.
93  static void autoComplete() {  static void autoComplete() {
94      if (g_suggestedPortion.empty()) return;      if (g_suggestedPortion.empty()) return;
95      String s;      String s;
# Line 98  static void commandFromHistory(int offse Line 113  static void commandFromHistory(int offse
113      g_client->send(command);      g_client->send(command);
114  }  }
115    
116    /// Will be called when the user hits arrow up key, to iterate to an older command line.
117  static void previousCommand() {  static void previousCommand() {
118      commandFromHistory(1);      commandFromHistory(1);
119  }  }
120    
121    /// Will be called when the user hits arrow down key, to iterate to a more recent command line.
122  static void nextCommand() {  static void nextCommand() {
123      commandFromHistory(-1);      commandFromHistory(-1);
124  }  }
125    
126    /// Will be called whenever the user hits ENTER, to store the line in the command history.
127  static void storeCommandInHistory(const String& sCommand) {  static void storeCommandInHistory(const String& sCommand) {
128      g_commandHistoryIndex = -1; // reset history index      g_commandHistoryIndex = -1; // reset history index
129      // don't save the command if the previous one was the same      // don't save the command if the previous one was the same
# Line 131  static void storeCommandInHistory(const Line 149  static void storeCommandInHistory(const
149   * @endcode   * @endcode
150   * which will inform the LSCP server that this LSCP client is actually a LSCP   * which will inform the LSCP server that this LSCP client is actually a LSCP
151   * shell application. The shell will then simply forward every single character   * shell application. The shell will then simply forward every single character
152   * typed by the user immediately to the LSCP server, which in turn will evaluate   * typed by the user immediately to the LSCP server. The LSCP server in turn
153   * every single character typed by the user and will return immediately a   * will evaluate every single character received and will return immediately a
154   * specially formatted string to the shell application like (assuming the user's   * specially formatted string to the shell application like (assuming the user's
155   * current command line was "CREATE AUasdf"):   * current command line was "CREATE AUasdf"):
156   * @code   * @code
# Line 158  static void storeCommandInHistory(const Line 176  static void storeCommandInHistory(const
176   *   *
177   * - Right of "{{SB}}" follows the current auto completion suggestion, so that   * - Right of "{{SB}}" follows the current auto completion suggestion, so that
178   *   string portion was not typed by the user yet, but is expected to be typed   *   string portion was not typed by the user yet, but is expected to be typed
179   *   by him next to retain syntax correctness.   *   by him next, to retain syntax correctness. The auto completion portion is
180     *   added by the LSCP server only if there is one unique way to add characters
181     *   to the current command line. If there are multiple possibilities, than this
182     *   portion is missing due to ambiguity.
183     *
184     * - Optionally there might also be a "{{PB}" marker on right hand side of the
185     *   line. The text right to that marker reflects all possibilities at the
186     *   user's current input position (which cannot be auto completed) due to
187     *   ambiguity, including abstract (a.k.a. "non-terminal") symbols like:
188     *   @code
189     *   <digit>, <text>, <number>, etc.
190     *   @endcode
191     *   This portion is added by the LSCP server only if there is not a unique way
192     *   to add characters to the current command line.
193   */   */
194  int main(int argc, char *argv[]) {  int main(int argc, char *argv[]) {
195      String host = LSCP_DEFAULT_HOST;      String host = LSCP_DEFAULT_HOST;
# Line 196  int main(int argc, char *argv[]) { Line 227  int main(int argc, char *argv[]) {
227      // receiving incoming network data from the sampler's LSCP server      // receiving incoming network data from the sampler's LSCP server
228      g_client = new LSCPClient;      g_client = new LSCPClient;
229      g_client->setCallback(onLSCPClientNewInputAvailable);      g_client->setCallback(onLSCPClientNewInputAvailable);
230        g_client->setErrorCallback(onLSCPClientErrorOccured);
231      if (!g_client->connect(host, port)) return -1;      if (!g_client->connect(host, port)) return -1;
232      String sResponse = g_client->sendCommandSync(      String sResponse = g_client->sendCommandSync(
233          (autoCorrect) ? "SET SHELL AUTO_CORRECT 1" : "SET SHELL AUTO_CORRECT 0"          (autoCorrect) ? "SET SHELL AUTO_CORRECT 1" : "SET SHELL AUTO_CORRECT 0"
# Line 233  int main(int argc, char *argv[]) { Line 265  int main(int argc, char *argv[]) {
265      //   FIFO buffer.      //   FIFO buffer.
266      //      //
267      // - Main thread: this thread runs in the loop below. The main thread sleeps      // - Main thread: this thread runs in the loop below. The main thread sleeps
268      //   (by using the "g_todo" semaphore) until either new keys on the keyboard      //   (by using the "g_todo" condition variable) until either new keys on the
269      //   were stroke by the user or until new bytes were received from the LSCP      //   keyboard were stroke by the user or until new bytes were received from
270      //   server. The main thread will then accordingly send the typed characters      //   the LSCP server. The main thread will then accordingly send the typed
271      //   to the LSCP server and/or show the result of the LSCP server's latest      //   characters to the LSCP server and/or show the result of the LSCP
272      //   evaluation to the user on the screen (by pulling those data from the      //   server's latest evaluation to the user on the screen (by pulling those
273      //   other two thread's FIFO buffers).      //   data from the other two thread's FIFO buffers).
274      while (true) {      while (true) {
275          // sleep until either new data from the network or from keyboard arrived          // sleep until either new data from the network or from keyboard arrived
276          g_todo.WaitIf(false);          g_todo.WaitIf(false);
# Line 268  int main(int argc, char *argv[]) { Line 300  int main(int argc, char *argv[]) {
300                          sBad.erase(sBad.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker                          sBad.erase(sBad.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker
301                      if (sBad.find(LSCP_SHK_SUGGEST_BACK) != string::npos)                      if (sBad.find(LSCP_SHK_SUGGEST_BACK) != string::npos)
302                          sBad.erase(sBad.find(LSCP_SHK_SUGGEST_BACK)); // erase auto suggestion portion                          sBad.erase(sBad.find(LSCP_SHK_SUGGEST_BACK)); // erase auto suggestion portion
303                        if (sBad.find(LSCP_SHK_POSSIBILITIES_BACK) != string::npos)
304                            sBad.erase(sBad.find(LSCP_SHK_POSSIBILITIES_BACK)); // erase possibilities portion
305    
306                      // extract portion that is suggested for auto completion                      // extract portion that is suggested for auto completion
307                      String sSuggest;                      String sSuggest;
# Line 275  int main(int argc, char *argv[]) { Line 309  int main(int argc, char *argv[]) {
309                          sSuggest = s.substr(s.find(LSCP_SHK_SUGGEST_BACK) + strlen(LSCP_SHK_SUGGEST_BACK));                          sSuggest = s.substr(s.find(LSCP_SHK_SUGGEST_BACK) + strlen(LSCP_SHK_SUGGEST_BACK));
310                          if (sSuggest.find(LSCP_SHK_CURSOR) != string::npos)                          if (sSuggest.find(LSCP_SHK_CURSOR) != string::npos)
311                              sSuggest.erase(sSuggest.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker                              sSuggest.erase(sSuggest.find(LSCP_SHK_CURSOR), strlen(LSCP_SHK_CURSOR)); // erase cursor marker
312                            if (sSuggest.find(LSCP_SHK_POSSIBILITIES_BACK) != string::npos)
313                                sSuggest.erase(sSuggest.find(LSCP_SHK_POSSIBILITIES_BACK)); // erase possibilities portion
314                        }
315    
316                        // extract portion that provides all current possibilities
317                        // (that is all branches in the current grammar tree)
318                        String sPossibilities;
319                        if (s.find(LSCP_SHK_POSSIBILITIES_BACK) != string::npos) {
320                            sPossibilities = s.substr(s.find(LSCP_SHK_POSSIBILITIES_BACK) + strlen(LSCP_SHK_POSSIBILITIES_BACK));
321                      }                      }
322    
323                      // extract current cursor position                      // extract current cursor position
# Line 295  int main(int argc, char *argv[]) { Line 338  int main(int argc, char *argv[]) {
338    
339                      //printf("line '%s' good='%s' bad='%s' suggested='%s' cursor=%d\n", line.c_str(), sGood.c_str(), sBad.c_str(), sSuggest.c_str(), cursorColumn);                      //printf("line '%s' good='%s' bad='%s' suggested='%s' cursor=%d\n", line.c_str(), sGood.c_str(), sBad.c_str(), sSuggest.c_str(), cursorColumn);
340    
341                        // clear current command line on screen
342                        // (which may have been printed over several lines)
343                      CCursor cursor = CCursor::now().toColumn(0).clearLine();                      CCursor cursor = CCursor::now().toColumn(0).clearLine();
344                        for (int i = 0; i < g_linesActive; ++i)
345                            cursor = cursor.down().clearLine();
346                        if (g_linesActive) cursor = cursor.up(g_linesActive).toColumn(0);
347                      printPrompt();                      printPrompt();
348    
349                        // print out the gathered informations on the screen
350                        TerminalPrinter p;
351                      CFmt cfmt;                      CFmt cfmt;
352                      if (code == LSCP_SHU_COMPLETE) cfmt.bold().green();                      if (code == LSCP_SHU_COMPLETE) cfmt.bold().green();
353                      else cfmt.bold().white();                      else cfmt.bold().white();
354                      cout << sGood << flush;                      p << sGood;
355                      cfmt.reset().red();                      cfmt.reset().red();
356                      cout << sBad << flush;                      p << sBad;
357                      cfmt.bold().yellow();                      cfmt.bold().yellow();
358                      cout << sSuggest << flush;                      p << sSuggest;
359                        if (!sPossibilities.empty())
360                            p << " <- " << sPossibilities;
361    
362                        // move cursor back to the appropriate input position in
363                        // the command line (which may be several lines above)
364                        g_linesActive = p.linesAdvanced();
365                        if (p.linesAdvanced()) cursor.up(p.linesAdvanced());
366                      cursor.toColumn(cursorColumn + promptOffset());                      cursor.toColumn(cursorColumn + promptOffset());
367                  }                  }
368              } else if (line.substr(0,2) == "OK") { // single-line response expected ...              } else if (line.substr(0,2) == "OK") { // single-line response expected ...
# Line 352  int main(int argc, char *argv[]) { Line 408  int main(int argc, char *argv[]) {
408          while (g_keyboardReader->charAvailable()) {          while (g_keyboardReader->charAvailable()) {
409              char c = g_keyboardReader->popChar();              char c = g_keyboardReader->popChar();
410    
             CFmt cfmt;  
             cfmt.white();  
411              //std::cout << c << "(" << int(c) << ")" << std::endl << std::flush;              //std::cout << c << "(" << int(c) << ")" << std::endl << std::flush;
412              if (iKbdEscapeCharsExpected) { // escape sequence (still) expected now ...              if (iKbdEscapeCharsExpected) { // escape sequence (still) expected now ...
413                  iKbdEscapeCharsExpected--;                  iKbdEscapeCharsExpected--;
# Line 363  int main(int argc, char *argv[]) { Line 417  int main(int argc, char *argv[]) {
417                          previousCommand();                          previousCommand();
418                      else if (kbdPrevEscapeChar == 91 && c == 66) // down key                      else if (kbdPrevEscapeChar == 91 && c == 66) // down key
419                          nextCommand();                          nextCommand();
420                      else if (kbdPrevEscapeChar == 91 && c == 68) { // left key                      else if (kbdPrevEscapeChar == 91 && c == 68) // left key
421                          //TODO: move cursor left                          g_client->send(2); // custom usage of this ASCII code
422                      } else if (kbdPrevEscapeChar == 91 && c == 67) { // right key                      else if (kbdPrevEscapeChar == 91 && c == 67) // right key
423                          //TODO: move cursor right                          g_client->send(3); // custom usage of this ASCII code
                     }  
424                  }                  }
425                  continue; // don't send this escape sequence character to LSCP server                  continue; // don't send this escape sequence character to LSCP server
426              } else if (c == KBD_ESCAPE) { // escape sequence for special keys expected next ...              } else if (c == KBD_ESCAPE) { // escape sequence for special keys expected next ...
427                  iKbdEscapeCharsExpected = 2;                  iKbdEscapeCharsExpected = 2;
428                  continue; // don't send ESC character to LSCP server                  continue; // don't send ESC character to LSCP server
429              } else if (c == KBD_BACKSPACE) {              } else if (c == KBD_BACKSPACE) {
430                  if (promptOffset() < CCursor::now().column())                  c = '\b';
                     cout << "\b \b" << flush;  
                 c = '\b';      
431              } else if (c == '\t') { // auto completion ...              } else if (c == '\t') { // auto completion ...
432                  autoComplete();                  autoComplete();
433                  continue; // don't send tab character to LSCP server                  continue; // don't send tab character to LSCP server
434              } else if (c == '\n') {              } else if (c == '\n') {
435                  storeCommandInHistory(g_goodPortion + g_badPortion);                  storeCommandInHistory(g_goodPortion + g_badPortion);
             } else { // don't apply RETURN stroke yet, since the typed command might still be corrected by the sampler  
                 cout << c << flush;  
436              }              }
437    
438              g_client->send(c);              g_client->send(c);

Legend:
Removed from v.2525  
changed lines
  Added in v.2532

  ViewVC Help
Powered by ViewVC