ST_engine  0.3-ALPHA
console.cpp
1 /* This file is part of the "ST" project.
2  * You may use, distribute or modify this code under the terms
3  * of the GNU General Public License version 2.
4  * See LICENCE.txt in the root directory of the project.
5  *
6  * Author: Maxim Atanasov
7  * E-mail: maxim.atanasov@protonmail.com
8  */
9 
10 #include <console.hpp>
11 #include <key_definitions.hpp>
12 
13 static bool singleton_initialized = false;
14 
19 console::console(message_bus &gMessageBus) : gMessage_bus(gMessageBus) {
20  if (singleton_initialized) {
21  throw std::runtime_error("The dev console cannot be initialized more than once!");
22  } else {
23  singleton_initialized = true;
24  }
25 
26  color = {50, 50, 50, 100};
27  color_text = {255, 255, 255, 255};
28  color_info = {10, 50, 255, 255};
29  color_error = {255, 0, 0, 255};
30  color_success = {50, 255, 10, 255};
31  shown = false;
32  scroll_offset = 0;
33  gMessage_bus.subscribe(LOG_ERROR, &msg_sub);
34  gMessage_bus.subscribe(LOG_SUCCESS, &msg_sub);
35  gMessage_bus.subscribe(LOG_INFO, &msg_sub);
36  gMessage_bus.subscribe(CONSOLE_TOGGLE, &msg_sub);
37  gMessage_bus.subscribe(MOUSE_SCROLL, &msg_sub);
38  gMessage_bus.subscribe(KEY_PRESSED, &msg_sub);
39  gMessage_bus.subscribe(KEY_HELD, &msg_sub);
40  gMessage_bus.subscribe(KEY_RELEASED, &msg_sub);
41  gMessage_bus.subscribe(TEXT_STREAM, &msg_sub);
42  gMessage_bus.subscribe(CONSOLE_CLEAR, &msg_sub);
43 }
44 
45 void console::post_init() const {
46  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::ENTER)));
47  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::TILDE)));
48  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::LEFT)));
49  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::RIGHT)));
50  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::UP)));
51  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::DOWN)));
52  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::BACKSPACE)));
53  gMessage_bus.send_msg(new message(REGISTER_KEY, static_cast<uint8_t>(ST::key::DELETE)));
54 }
55 
56 
60 void console::scroll(int32_t scroll_y) {
61  scroll_offset = scroll_y * 20;
62 }
63 
68 void console::handle_messages() {
69  message *temp = msg_sub.get_next_message();
70  while (temp != nullptr) {
71  switch (temp->msg_name) {
72  case LOG_ERROR: {
73  auto log = static_cast<std::string *>(temp->get_data());
74  if (log_level == 0x07 || log_level == 0x01 || log_level == 0x03 || log_level == 0x05) {
75  write(*log, ST::log_type::ERROR);
76  }
77  break;
78  }
79  case LOG_INFO: {
80  auto log = static_cast<std::string *>(temp->get_data());
81  if (log_level >= 0x04) {
82  write(*log, ST::log_type::INFO);
83  }
84  break;
85  }
86  case LOG_SUCCESS: {
87  auto log = static_cast<std::string *>(temp->get_data());
88  if (log_level >= 0x06 || log_level == 0x02 || log_level == 0x03) {
89  write(*log, ST::log_type::SUCCESS);
90  }
91  break;
92  }
93  case CONSOLE_TOGGLE:
94  toggle();
95  break;
96  case CONSOLE_CLEAR:
97  this->entries.clear();
98  break;
99  case MOUSE_SCROLL:
100  scroll(static_cast<int32_t>(temp->base_data0));
101  break;
102  case KEY_HELD: {
103  auto key_val = static_cast<ST::key>(temp->base_data0);
104  if (is_open()) {
105  cursor_timer = 0; //Cursor won't blink when holding these keys
106  if (hold_counter > 10) { //These values can be changed to adjust the key hold delay.
107  hold_counter = 9;
108  if (key_val == ST::key::ENTER) {
109  enterKeyAction();
110  } else if (key_val == ST::key::LEFT) {
111  leftKeyAction();
112  } else if (key_val == ST::key::RIGHT) {
113  rightKeyAction();
114  } else if (key_val == ST::key::UP) {
115  upKeyAction();
116  } else if (key_val == ST::key::DOWN) {
117  downKeyAction();
118  } else if (key_val == ST::key::BACKSPACE) {
119  backspaceAction();
120  } else if (key_val == ST::key::DELETE) {
121  deleteKeyAction();
122  }
123  } else {
124  ++hold_counter;
125  }
126  }
127  break;
128  }
129  case KEY_PRESSED: {
130  auto key_val = static_cast<ST::key>(temp->base_data0);
131  if (key_val == ST::key::TILDE) {
132  toggle();
133  } else if (is_open()) {
134  if (key_val == ST::key::ENTER) {
135  enterKeyAction();
136  } else if (key_val == ST::key::LEFT) {
137  leftKeyAction();
138  } else if (key_val == ST::key::RIGHT) {
139  rightKeyAction();
140  } else if (key_val == ST::key::UP) {
141  upKeyAction();
142  } else if (key_val == ST::key::DOWN) {
143  downKeyAction();
144  } else if (key_val == ST::key::BACKSPACE) {
145  backspaceAction();
146  } else if (key_val == ST::key::DELETE) {
147  deleteKeyAction();
148  }
149  hold_counter = 0;
150  }
151  break;
152  }
153  case TEXT_STREAM: {
154  std::string received_data = *static_cast<std::string *>(temp->get_data());
155  for (char const &c: received_data) {
156  if (c > 126 || c < 0) {
157  received_data.clear();
158  }
159  }
160  if (received_data == "(") {
161  received_data += ")";
162  composition.insert(cursor_position--, received_data);
163  } else if (received_data == "\"") {
164  received_data += received_data;
165  composition.insert(cursor_position--, received_data);
166  } else if (received_data == "[" || received_data == "{") {
167  received_data += static_cast<char>(received_data.at(0) + 2);
168  composition.insert(cursor_position--, received_data);
169  } else {
170  composition.insert(cursor_position, received_data);
171  }
172  cursor_position += static_cast<uint16_t>(received_data.size());
173  gMessage_bus.send_msg(new message(CLEAR_TEXT_STREAM));
174  break;
175  }
176  }
177  delete temp;
178  temp = msg_sub.get_next_message();
179  }
180 }
181 
190 void console::set_log_level(ST::log_type arg) {
191  log_level = static_cast<uint8_t>(arg);
192 }
193 
197 void console::toggle() {
198  if (is_open()) {
199  hide();
200  gMessage_bus.send_msg(new message(STOP_TEXT_INPUT));
201  } else {
202  show();
203  gMessage_bus.send_msg(new message(START_TEXT_INPUT));
204  }
205 }
206 
210 void console::write(const std::string &arg, ST::log_type type) {
211  if (type == ST::log_type::ERROR) {
212  fprintf(stderr, "%s\n", arg.c_str());
213  } else {
214  fprintf(stdout, "%s\n", arg.c_str());
215  }
216  entries.emplace_back(type, arg);
217  //remove an entry if there are too many
218  if (entries.size() > 1000) {
219  entries.erase(entries.begin());
220  }
221 }
222 
227 bool console::is_open() const {
228  return shown;
229 }
230 
234 void console::hide() {
235  shown = false;
236 }
237 
241 void console::show() {
242  shown = true;
243 }
244 
249  singleton_initialized = false;
250 }
251 
256 void console::backspaceAction() {
257  if (cursor_position > 0) {
258  composition.erase(static_cast<uint16_t>(cursor_position - 1), 1);
259  cursor_position -= 1;
260  }
261 }
262 
267 void console::rightKeyAction() {
268  if (cursor_position < composition.size()) {
269  cursor_position += 1;
270  }
271 }
272 
277 void console::leftKeyAction() {
278  if (cursor_position > 0) {
279  cursor_position -= 1;
280  }
281 }
282 
287 void console::downKeyAction() {
288  if (entries_history_index < command_entries.size() - 1) {
289  entries_history_index++;
290  composition = command_entries.at(static_cast<uint64_t>(entries_history_index));
291  } else {
292  entries_history_index = -1;
293  composition = composition_history_temp;
294  }
295  gMessage_bus.send_msg(new message(CLEAR_TEXT_STREAM));
296  cursor_position = static_cast<uint16_t>(composition.size());
297 }
298 
303 void console::upKeyAction() {
304  if (entries_history_index == -1 && !command_entries.empty()) {
305  composition_history_temp = composition;
306  composition = command_entries.back();
307  entries_history_index = static_cast<int16_t>(command_entries.size() - 1);
308  } else if (entries_history_index > 0) {
309  entries_history_index--;
310  composition = command_entries.at(static_cast<uint64_t>(entries_history_index));
311  }
312  gMessage_bus.send_msg(new message(CLEAR_TEXT_STREAM));
313  cursor_position = static_cast<uint16_t>(composition.size());
314 }
315 
323 void console::enterKeyAction() {
324  if (!composition.empty()) {
325  write(composition, ST::log_type::INFO);
326  command_entries.emplace_back(composition);
327  gMessage_bus.send_msg(new message(EXECUTE_SCRIPT, make_data(composition)));
328  }
329  composition.clear();
330  cursor_position = 0;
331  entries_history_index = -1;
332  gMessage_bus.send_msg(new message(CLEAR_TEXT_STREAM));
333 }
334 
339 void console::deleteKeyAction() {
340  composition.erase(cursor_position, 1);
341 }
342 
347  handle_messages();
348 }
349 
~console()
Definition: console.cpp:248
void update()
Definition: console.cpp:346
void set_log_level(ST::log_type arg)
Definition: console.cpp:190
bool is_open() const
Definition: console.cpp:227
console(message_bus &gMessageBus)
Definition: console.cpp:19
The central messaging system of the engine. All subsystem make extensive use of it.
Definition: message_bus.hpp:29
void subscribe(uint8_t msg, subscriber *sub)
Definition: message_bus.cpp:71
void send_msg(message *msg)
Definition: message_bus.cpp:19
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
void * get_data() const
Definition: message.hpp:80
message * get_next_message()
Definition: subscriber.hpp:39