cpp-terminal 1.0.0
Small C++ library for writing multiplatform terminal applications
Loading...
Searching...
No Matches
event.cpp
Go to the documentation of this file.
1/*
2* cpp-terminal
3* C++ library for writing multi-platform terminal applications.
4*
5* SPDX-FileCopyrightText: 2019-2024 cpp-terminal
6*
7* SPDX-License-Identifier: MIT
8*/
9
11
13
14#include <chrono>
15
16#if defined(_MSC_VER)
17 // Disable stupid warnings on Windows
18 #pragma warning(push)
19 #pragma warning(disable : 4582)
20 #pragma warning(disable : 4583)
21#endif
23
25#if defined(_MSC_VER)
26 #pragma warning(pop)
27#endif
28
30{
31 if(m_Type == Type::Key) return &m_container.m_Key;
32 return nullptr;
33}
34
36{
37 if(m_Type == Type::Key) return &m_container.m_Key;
38 return nullptr;
39}
40
42{
44 return nullptr;
45}
46
48{
50 return nullptr;
51}
52
54{
56 return nullptr;
57}
58
60{
62 return nullptr;
63}
64
66{
68 return nullptr;
69}
70
72{
74 return nullptr;
75}
76
78{
80 return nullptr;
81}
82
83const std::string* Term::Event::get_if_copy_paste() const
84{
86 return nullptr;
87}
88
90
92{
93 if(m_Type == Type::Focus) return &m_container.m_Focus;
94 return nullptr;
95}
96
98{
99 if(m_Type == Type::Focus) return &m_container.m_Focus;
100 return nullptr;
101}
102
104{
105 m_Type = event.m_Type;
106 switch(m_Type)
107 {
108 case Type::Empty: break;
109 case Type::Key: m_container.m_Key = event.m_container.m_Key; break;
110 case Type::CopyPaste: new(&this->m_container.m_string) std::string(event.m_container.m_string); break;
111 case Type::Cursor: m_container.m_Cursor = event.m_container.m_Cursor; break;
112 case Type::Screen: m_container.m_Screen = event.m_container.m_Screen; break;
113 case Type::Focus: m_container.m_Focus = event.m_container.m_Focus; break;
114 case Type::Mouse: m_container.m_Mouse = event.m_container.m_Mouse; break;
115 default: break;
116 }
117 return *this;
118}
119
120Term::Event::Event(const Term::Focus& focus) : m_Type(Type::Focus) { m_container.m_Focus = focus; }
121
123{
124 m_Type = event.m_Type;
125 switch(m_Type)
126 {
127 case Type::Empty: break;
128 case Type::Key: m_container.m_Key = event.m_container.m_Key; break;
129 case Type::CopyPaste: new(&this->m_container.m_string) std::string(event.m_container.m_string); break;
130 case Type::Cursor: m_container.m_Cursor = event.m_container.m_Cursor; break;
131 case Type::Screen: m_container.m_Screen = event.m_container.m_Screen; break;
132 case Type::Focus: m_container.m_Focus = event.m_container.m_Focus; break;
133 case Type::Mouse: m_container.m_Mouse = event.m_container.m_Mouse; break;
134 default: break;
135 }
136}
137
139{
140 using std::string;
141 if(m_Type == Type::CopyPaste) { m_container.m_string.~string(); }
142}
143
144Term::Event::Event() = default;
145
146Term::Event::Event(Term::Event&& event) noexcept : m_Type(event.m_Type)
147{
148 switch(m_Type)
149 {
150 case Type::Empty: break;
151 case Type::Key: std::swap(m_container.m_Key, event.m_container.m_Key); break;
152 case Type::CopyPaste: std::swap(m_container.m_string, event.m_container.m_string); break;
153 case Type::Cursor: std::swap(m_container.m_Cursor, event.m_container.m_Cursor); break;
154 case Type::Screen: std::swap(m_container.m_Screen, event.m_container.m_Screen); break;
155 case Type::Focus: std::swap(m_container.m_Focus, event.m_container.m_Focus); break;
156 case Type::Mouse: std::swap(m_container.m_Mouse, event.m_container.m_Mouse); break;
157 default: break;
158 }
159}
160
162{
163 switch(other.m_Type)
164 {
165 case Type::Empty: break;
166 case Type::Key: std::swap(m_container.m_Key, other.m_container.m_Key); break;
167 case Type::CopyPaste: std::swap(m_container.m_string, other.m_container.m_string); break;
168 case Type::Cursor: std::swap(m_container.m_Cursor, other.m_container.m_Cursor); break;
169 case Type::Screen: std::swap(m_container.m_Screen, other.m_container.m_Screen); break;
170 case Type::Focus: std::swap(m_container.m_Focus, other.m_container.m_Focus); break;
171 case Type::Mouse: std::swap(m_container.m_Mouse, other.m_container.m_Mouse); break;
172 default: break;
173 }
174 return *this;
175}
176
177Term::Event::Event(const Term::Mouse& mouse) : m_Type(Type::Mouse) { m_container.m_Mouse = mouse; }
178
179bool Term::Event::empty() const { return m_Type == Type::Empty; }
180
181Term::Event::operator Term::Mouse() const
182{
183 if(m_Type == Type::Mouse) { return m_container.m_Mouse; }
184 return {};
185}
186
187Term::Event::operator std::string() const
188{
189 if(m_Type == Type::CopyPaste) { return m_container.m_string; }
190 return {};
191}
192
193Term::Event::operator Term::Screen() const
194{
195 if(m_Type == Type::Screen) { return m_container.m_Screen; }
196 return {};
197}
198
199Term::Event::Event(const Term::Screen& screen) : m_Type(Type::Screen) { m_container.m_Screen = screen; }
200
201Term::Event::Event(const Term::Key& key) : m_Type(Type::Key) { m_container.m_Key = key; }
202
203Term::Event::Type Term::Event::type() const { return m_Type; }
204
205Term::Event::Event(const std::string& str) { parse(str); }
206
207void Term::Event::parse(const std::string& str)
208{
209 if(str.empty()) m_Type = Type::Empty;
210 else if(str.size() == 1)
211 {
212 m_Type = Type::Key;
213 m_container.m_Key = Key(static_cast<Term::Key>(str[0]));
214 /* Backspace return 127 CTRL+backspace return 8 */
215 if(m_container.m_Key == Term::Key::Del) m_container.m_Key = Key(Term::Key::Backspace);
216 }
217 else if(str == "\033[I")
218 {
219 m_Type = Type::Focus;
220 m_container.m_Focus = Term::Focus(Term::Focus::Type::In);
221 }
222 else if(str == "\033[O")
223 {
224 m_Type = Type::Focus;
225 m_container.m_Focus = Term::Focus(Term::Focus::Type::Out);
226 }
227 else if(str.size() == 2 && str[0] == '\033')
228 {
229 m_container.m_Key = Key(static_cast<Term::Key>(Term::MetaKey::Value::Alt + static_cast<Term::Key>(str[1])));
230 m_Type = Type::Key;
231 }
232 else if(str[0] == '\033' && str[1] == '[' && str[str.size() - 1] == 'R')
233 {
234 std::size_t found = str.find(';', 2);
235 if(found != std::string::npos)
236 {
237 m_Type = Type::Cursor;
238 m_container.m_Cursor = Cursor(static_cast<std::uint16_t>(std::stoi(str.substr(2, found - 2))), static_cast<std::uint16_t>(std::stoi(str.substr(found + 1, str.size() - (found + 2)))));
239 }
240 }
241 else if(str[0] == '\033' && str[1] == '[' && str[2] == '<')
242 {
243 static std::chrono::time_point<std::chrono::system_clock> old;
244 bool not_too_long{false};
245 if(std::chrono::system_clock::now() - old <= std::chrono::milliseconds{120}) not_too_long = true;
246 m_Type = Type::Mouse;
247 std::size_t pos{3};
248 std::size_t pos2{3};
249 std::vector<std::size_t> values;
250 while((pos = str.find(';', pos)) != std::string::npos)
251 {
252 values.push_back(std::stoull(str.substr(pos2, pos - pos2)));
253 pos++;
254 pos2 = pos;
255 }
256 values.push_back(std::stoull(str.substr(pos2, str.size() - pos2 - 1)));
257 static Term::Mouse first;
258 static Term::Mouse second;
260 if(str[str.size() - 1] == 'm') action = Term::Button::Action::Released;
261 else
264 switch(values[0])
265 {
266 case 0:
267 {
269 break;
270 }
271 case 1:
272 {
274 break;
275 }
276 case 2:
277 {
279 break;
280 }
281 case 35:
282 {
285 break;
286 }
287 case 64:
288 {
291 break;
292 }
293 case 65:
294 {
297 break;
298 }
299 default: break;
300 }
301 if(not_too_long && first.row() == second.row() && second.row() == values[1] && first.column() == second.column() && second.column() == values[2] && first.getButton().type() == second.getButton().type() && second.getButton().type() == type && first.getButton().action() == Button::Action::Released && second.getButton().action() == Button::Action::Pressed && action == Button::Action::Pressed) action = Term::Button::Action::DoubleClicked;
302 second = first;
303 first = Term::Mouse(Term::Button(type, action), values[1], values[2]);
304 m_container.m_Mouse = first;
305 old = std::chrono::system_clock::now();
306 }
307 else if(str.size() <= 10)
308 {
309 //https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
310 // CSI = ESC[ SS3 = ESCO
311 /*
312 * Key Normal Application
313 * -------------+----------+-------------
314 * Cursor Up | CSI A | SS3 A
315 * Cursor Down | CSI B | SS3 B
316 * Cursor Right | CSI C | SS3 C
317 * Cursor Left | CSI D | SS3 D
318 * -------------+----------+-------------
319 * Key Normal/Application
320 * -------------+--------------------
321 * Cursor Up | ESC A
322 * Cursor Down | ESC B
323 * Cursor Right | ESC C
324 * Cursor Left | ESC D
325 * -------------+--------------------
326 */
327 if((str == "\u001bOA") || (str == "\u001b[A") || (str == "\u001bA")) { m_container.m_Key = Key(Term::Key::ArrowUp); }
328 else if((str == "\u001bOB") || (str == "\u001b[B") || (str == "\u001bB")) { m_container.m_Key = Key(Term::Key::ArrowDown); }
329 else if((str == "\u001bOC") || (str == "\u001b[C") || (str == "\u001bC")) { m_container.m_Key = Key(Term::Key::ArrowRight); }
330 else if((str == "\u001bOD") || (str == "\u001b[D") || (str == "\u001bD")) { m_container.m_Key = Key(Term::Key::ArrowLeft); }
331 /*
332 * Key Normal Application
333 * ---------+----------+-------------
334 * Home | CSI H | SS3 H
335 * End | CSI F | SS3 F
336 * ---------+----------+-------------
337 */
338 else if((str == "\u001bOH") || (str == "\u001b[H"))
339 m_container.m_Key = Key(Term::Key::Home);
340 else if(str == "\u001bOF" || str == "\u001b[F")
341 m_container.m_Key = Key(Term::Key::End);
342 /*
343 * Key Escape Sequence
344 * ---------+-----------------
345 * F1 | SS3 P
346 * F2 | SS3 Q
347 * F3 | SS3 R
348 * F4 | SS3 S
349 * F1 | CSI 1 1 ~
350 * F2 | CSI 1 2 ~
351 * F3 | CSI 1 3 ~
352 * F4 | CSI 1 4 ~
353 * F5 | CSI 1 5 ~
354 * F6 | CSI 1 7 ~
355 * F7 | CSI 1 8 ~
356 * F8 | CSI 1 9 ~
357 * F9 | CSI 2 0 ~
358 * F10 | CSI 2 1 ~
359 * F11 | CSI 2 3 ~
360 * F12 | CSI 2 4 ~
361 * ---------+-----------------
362 */
363 else if(str == "\u001bOP" || str == "\u001b[11~")
364 m_container.m_Key = Key(Term::Key::F1);
365 else if(str == "\u001bOQ" || str == "\u001b[12~")
366 m_container.m_Key = Key(Term::Key::F2);
367 else if(str == "\u001bOR" || str == "\u001b[13~")
368 m_container.m_Key = Key(Term::Key::F3);
369 else if(str == "\u001bOS" || str == "\u001b[14~")
370 m_container.m_Key = Key(Term::Key::F4);
371 else if(str == "\u001b[15~")
372 m_container.m_Key = Key(Term::Key::F5);
373 else if(str == "\u001b[17~")
374 m_container.m_Key = Key(Term::Key::F6);
375 else if(str == "\u001b[18~")
376 m_container.m_Key = Key(Term::Key::F7);
377 else if(str == "\u001b[19~")
378 m_container.m_Key = Key(Term::Key::F8);
379 else if(str == "\u001b[20~")
380 m_container.m_Key = Key(Term::Key::F9);
381 else if(str == "\u001b[21~")
382 m_container.m_Key = Key(Term::Key::F10);
383 else if(str == "\u001b[23~")
384 m_container.m_Key = Key(Term::Key::F11);
385 else if(str == "\u001b[24~")
386 m_container.m_Key = Key(Term::Key::F12);
387 /*
388 * Key Normal Application
389 * ---------+----------+-------------
390 * Insert | CSI 2 ~ | CSI 2 ~
391 * Delete | CSI 3 ~ | CSI 3 ~
392 * Home | CSI 1 ~ | CSI 1 ~
393 * End | CSI 4 ~ | CSI 4 ~
394 * PageUp | CSI 5 ~ | CSI 5 ~
395 * PageDown | CSI 6 ~ | CSI 6 ~
396 * ---------+----------+-------------
397 */
398 else if(str == "\u001b[2~") { m_container.m_Key = Key(Term::Key::Insert); }
399 else if(str == "\u001b[3~") { m_container.m_Key = Key(Term::Key::Del); }
400 else if(str == "\u001b[1~") { m_container.m_Key = Key(Term::Key::Home); }
401 else if(str == "\u001b[4~") { m_container.m_Key = Key(Term::Key::End); }
402 else if(str == "\u001b[5~") { m_container.m_Key = Key(Term::Key::PageUp); }
403 else if(str == "\u001b[6~") { m_container.m_Key = Key(Term::Key::PageDown); }
404 /*
405 * Key Escape Sequence
406 * ---------+-----------------
407 * F13 | CSI 2 5 ~
408 * F14 | CSI 2 6 ~
409 * F15 | CSI 2 8 ~
410 * F16 | CSI 2 9 ~
411 * F17 | CSI 3 1 ~
412 * F18 | CSI 3 2 ~
413 * F19 | CSI 3 3 ~
414 * F20 | CSI 3 4 ~
415 * ---------+-----------------
416 */
417 else if(str == "\u001b[25~")
418 m_container.m_Key = Key(Term::Key::F13);
419 else if(str == "\u001b[26~")
420 m_container.m_Key = Key(Term::Key::F14);
421 else if(str == "\u001b[28~")
422 m_container.m_Key = Key(Term::Key::F15);
423 else if(str == "\u001b[29~")
424 m_container.m_Key = Key(Term::Key::F16);
425 else if(str == "\u001b[31~")
426 m_container.m_Key = Key(Term::Key::F17);
427 else if(str == "\u001b[32~")
428 m_container.m_Key = Key(Term::Key::F18);
429 else if(str == "\u001b[33~")
430 m_container.m_Key = Key(Term::Key::F19);
431 else if(str == "\u001b[34~")
432 m_container.m_Key = Key(Term::Key::F20);
433 else if(str == "\u001b[G")
434 m_container.m_Key = Key(Term::Key::Value::Numeric5);
436 m_container.m_Key = Key(static_cast<Term::Key::Value>(Term::Private::utf8_to_utf32(str)[0]));
437 else
438 {
439 m_Type = Type::CopyPaste;
440 new(&this->m_container.m_string) std::string(str);
441 return;
442 }
443 m_Type = Type::Key;
444 }
445 else
446 {
447 m_Type = Type::CopyPaste;
448 new(&this->m_container.m_string) std::string(str);
449 }
450}
451
452Term::Event::operator Term::Key() const
453{
454 if(m_Type == Type::Key) { return m_container.m_Key; }
455 return {};
456}
457
458Term::Event::operator Term::Cursor() const
459{
460 if(m_Type == Type::Cursor) { return m_container.m_Cursor; }
461 return {};
462}
463
464Term::Event::operator Term::Focus() const
465{
466 if(m_Type == Type::Focus) { return m_container.m_Focus; }
467 return {};
468}
Term::Button::Type type() const noexcept
Definition mouse.cpp:14
Term::Button::Action action() const noexcept
Definition mouse.cpp:12
container m_container
Definition event.hpp:90
Type type() const
Definition event.cpp:203
Mouse * get_if_mouse()
Definition event.cpp:47
void parse(const std::string &str)
Definition event.cpp:207
bool empty() const
Definition event.cpp:179
Screen * get_if_screen()
Definition event.cpp:41
Key * get_if_key()
Definition event.cpp:29
Type m_Type
Definition event.hpp:89
Cursor * get_if_cursor()
Definition event.cpp:65
Focus * get_if_focus()
Definition event.cpp:91
std::string * get_if_copy_paste()
Definition event.cpp:77
Event & operator=(Event &&other) noexcept
Definition event.cpp:161
Class to return the focus of the terminal.
Definition focus.hpp:22
@ Out
The terminal focus is out.
@ In
The terminal focus is in.
@ ArrowRight
Definition key.hpp:239
@ ArrowLeft
Definition key.hpp:238
@ PageDown
Definition key.hpp:247
@ PageUp
Definition key.hpp:246
@ ArrowUp
Definition key.hpp:240
@ Backspace
Definition key.hpp:229
@ ArrowDown
Definition key.hpp:241
@ Numeric5
Definition key.hpp:242
@ Insert
Definition key.hpp:244
std::size_t row() const noexcept
Definition mouse.cpp:26
std::size_t column() const noexcept
Definition mouse.cpp:28
Term::Button getButton() const noexcept
Definition mouse.cpp:24
std::u32string utf8_to_utf32(const std::string &str)
bool is_valid_utf8_code_unit(const std::string &str)
@ Cursor
Show the cursor.
Term::Screen m_Screen
Definition event.hpp:84
Term::Cursor m_Cursor
Definition event.hpp:83
Term::Focus m_Focus
Definition event.hpp:85
Term::Mouse m_Mouse
Definition event.hpp:86
std::string m_string
Definition event.hpp:87