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