1 /*
2  * LSCP Shell
3  *
4  * Copyright (c) 2014 - 2020 Christian Schoenebeck
5  *
6  * This program is part of LinuxSampler and released under the same terms.
7  */
8 
9 #include "KeyboardReader.h"
10 
11 static KeyboardReader* g_singleton = NULL;
12 static int g_instanceCount = 0;
13 
KeyboardReader()14 KeyboardReader::KeyboardReader() : Thread(false, false, 1, -1),
15     m_originalTerminalSetting(TerminalCtrl::now()),
16     m_callback(NULL), m_doCallback(true)
17 {
18     if (!g_instanceCount) g_singleton = this;
19     g_instanceCount++;
20     TerminalCtrl::echoInput(false);
21 }
22 
~KeyboardReader()23 KeyboardReader::~KeyboardReader() {
24     StopThread();
25     TerminalCtrl::restore(m_originalTerminalSetting);
26     // ensures that no temporary copy objects delete the global reference
27     g_instanceCount--;
28     if (!g_instanceCount) g_singleton = NULL;
29 }
30 
setCallback(Callback_t fn)31 void KeyboardReader::setCallback(Callback_t fn) {
32     m_callback = fn;
33 }
34 
callback(bool b)35 void KeyboardReader::callback(bool b) {
36     m_doCallback = b;
37 }
38 
Main()39 int KeyboardReader::Main() {
40     #if DEBUG
41     Thread::setNameOfCaller("KeyboardReader");
42     #endif
43 
44     while (true) {
45         std::vector<char> v = TerminalCtrl::getChars(1, 1);
46         if (!v.empty()) {
47             // prevent thread from being cancelled
48             // (e.g. to prevent deadlocks while holding mutex lock(s))
49             pushCancelable(false);
50 
51             m_fifoMutex.Lock();
52             m_fifo.push_back(v[0]);
53             if (m_sync.GetUnsafe()) {
54                 bool delimiterReceived = false;
55                 for (std::list<char>::iterator it = m_fifo.begin(); it != m_fifo.end(); ++it) {
56                     if ((*it) == m_syncDelimiter) {
57                         delimiterReceived = true;
58                         break;
59                     }
60                 }
61                 m_fifoMutex.Unlock();
62                 if (delimiterReceived) m_sync.Set(false);
63             } else {
64                 m_fifoMutex.Unlock();
65                 if (m_callback && m_doCallback) (*m_callback)(this);
66             }
67 
68             // now allow thread being cancelled again
69             // (since all mutexes are now unlocked)
70             popCancelable();
71         }
72         TestCancel();
73     }
74     return 0; // just to avoid a warning with some old compilers
75 }
76 
charAvailable() const77 bool KeyboardReader::charAvailable() const {
78     return !m_fifo.empty(); // is thread safe
79 }
80 
popChar()81 char KeyboardReader::popChar() {
82     LockGuard lock(m_fifoMutex);
83     if (m_fifo.empty()) return 0;
84     char c = m_fifo.front();
85     m_fifo.pop_front();
86     return c;
87 }
88 
popStringToDelimiterSync(char delimiter,bool includeDelimiter)89 std::string KeyboardReader::popStringToDelimiterSync(char delimiter, bool includeDelimiter) {
90     m_syncDelimiter = delimiter;
91 
92     bool alreadyReceived = false;
93     m_fifoMutex.Lock();
94     for (std::list<char>::iterator it = m_fifo.begin(); it != m_fifo.end(); ++it) {
95         if ((*it)== delimiter) {
96             alreadyReceived = true;
97             break;
98         }
99     }
100     if (!alreadyReceived) m_sync.Set(true);
101     m_fifoMutex.Unlock();
102     if (!alreadyReceived) m_sync.WaitAndUnlockIf(true);
103 
104     // if we are here, then there is now a string with the requested delimiter
105     // in the FIFO
106     std::string s;
107     while (charAvailable()) {
108         char c = popChar();
109         if (includeDelimiter || c != delimiter) s += c;
110         if (c == delimiter) return s;
111     }
112     return s;
113 }
114 
startReading()115 void KeyboardReader::startReading() {
116     StartThread();
117 }
118 
stopReading()119 void KeyboardReader::stopReading() {
120     StopThread();
121 }
122 
singleton()123 KeyboardReader* KeyboardReader::singleton() {
124     return g_singleton;
125 }
126