1 #include <cppurses/terminal/input.hpp>
2
3 #include <cstddef>
4 #include <memory>
5 #include <utility>
6
7 #include <ncurses.h>
8
9 #include <cppurses/system/detail/find_widget_at.hpp>
10 #include <cppurses/system/event.hpp>
11 #include <cppurses/system/events/key.hpp>
12 #include <cppurses/system/events/mouse.hpp>
13 #include <cppurses/system/events/resize_event.hpp>
14 #include <cppurses/system/focus.hpp>
15 #include <cppurses/system/shortcuts.hpp>
16 #include <cppurses/system/system.hpp>
17 #include <cppurses/widget/area.hpp>
18 #include <cppurses/widget/point.hpp>
19 #include <cppurses/widget/widget.hpp>
20
21 namespace {
22 using namespace cppurses;
23
24 /// Check if mouse_event is a button_mask type of event.
25 template <typename Mask_t>
is(Mask_t button_mask,const::MEVENT & mouse_event)26 auto is(Mask_t button_mask, const ::MEVENT& mouse_event) -> bool
27 {
28 return static_cast<bool>(mouse_event.bstate & button_mask);
29 }
30
31 /// Extract the Event type and Mouse::Button from a given MEVENT object.
extract_info(const::MEVENT & mouse_event)32 auto extract_info(const ::MEVENT& mouse_event)
33 -> std::pair<Event::Type, Mouse::Button>
34 {
35 auto type_button = std::make_pair(Event::None, Mouse::Button::None);
36 auto& type = type_button.first;
37 auto& button = type_button.second;
38 // Button 1 / Left Button
39 if (is(BUTTON1_PRESSED, mouse_event)) {
40 type = Event::MouseButtonPress;
41 button = Mouse::Button::Left;
42 }
43 else if (is(BUTTON1_RELEASED, mouse_event)) {
44 type = Event::MouseButtonRelease;
45 button = Mouse::Button::Left;
46 }
47 // Button 2 / Middle Button
48 else if (is(BUTTON2_PRESSED, mouse_event)) {
49 type = Event::MouseButtonPress;
50 button = Mouse::Button::Middle;
51 }
52 else if (is(BUTTON2_RELEASED, mouse_event)) {
53 type = Event::MouseButtonRelease;
54 button = Mouse::Button::Middle;
55 }
56 // Button 3 / Right Button
57 else if (is(BUTTON3_PRESSED, mouse_event)) {
58 type = Event::MouseButtonPress;
59 button = Mouse::Button::Right;
60 }
61 else if (is(BUTTON3_RELEASED, mouse_event)) {
62 type = Event::MouseButtonRelease;
63 button = Mouse::Button::Right;
64 }
65 // Button 4 / Scroll Up
66 else if (is(BUTTON4_PRESSED, mouse_event)) {
67 type = Event::MouseButtonPress;
68 button = Mouse::Button::ScrollUp;
69 }
70 else if (is(BUTTON4_RELEASED, mouse_event)) {
71 type = Event::MouseButtonRelease;
72 button = Mouse::Button::ScrollUp;
73 }
74 // Button 5 / Scroll Down
75 #if defined(BUTTON5_PRESSED) && defined(BUTTON5_RELEASED)
76 else if (is(BUTTON5_PRESSED, mouse_event)) {
77 type = Event::MouseButtonPress;
78 button = Mouse::Button::ScrollDown;
79 }
80 else if (is(BUTTON5_RELEASED, mouse_event)) {
81 type = Event::MouseButtonRelease;
82 button = Mouse::Button::ScrollDown;
83 }
84 #endif
85 return type_button;
86 }
87
make_mouse_event()88 auto make_mouse_event() -> std::unique_ptr<Event>
89 {
90 auto mouse_event = ::MEVENT{};
91 if (::getmouse(&mouse_event) != OK)
92 return nullptr;
93 Widget* receiver = detail::find_widget_at(mouse_event.x, mouse_event.y);
94 if (receiver == nullptr)
95 return nullptr;
96
97 // Coordinates
98 const auto global = Point{static_cast<std::size_t>(mouse_event.x),
99 static_cast<std::size_t>(mouse_event.y)};
100 const auto local =
101 Point{global.x - receiver->inner_x(), global.y - receiver->inner_y()};
102
103 // Create Event
104 const auto type_button = extract_info(mouse_event);
105 const auto type = type_button.first;
106 const auto button = type_button.second;
107 if (type == Event::MouseButtonPress) {
108 return std::make_unique<Mouse::Press>(
109 *receiver, Mouse::State{button, global, local, mouse_event.id});
110 }
111 if (type == Event::MouseButtonRelease) {
112 return std::make_unique<Mouse::Release>(
113 *receiver, Mouse::State{button, global, local, mouse_event.id});
114 }
115 return nullptr;
116 }
117
make_resize_event()118 auto make_resize_event() -> std::unique_ptr<Event>
119 {
120 Widget* const receiver = System::head();
121 if (receiver != nullptr) {
122 const auto width = System::terminal.width();
123 const auto height = System::terminal.height();
124 return std::make_unique<Resize_event>(*receiver, Area{width, height});
125 }
126 return nullptr;
127 }
128
make_keyboard_event(int input)129 auto make_keyboard_event(int input) -> std::unique_ptr<Event>
130 {
131 const auto code = static_cast<Key::Code>(input);
132 Widget* const receiver =
133 Shortcuts::send_key(code) ? nullptr : Focus::focus_widget();
134 return receiver == nullptr ? nullptr
135 : std::make_unique<Key::Press>(*receiver, code);
136 }
137 } // namespace
138
139 namespace cppurses {
140 namespace input {
141
get()142 auto get() -> std::unique_ptr<Event>
143 {
144 const auto input = ::getch();
145 switch (input) {
146 case ERR: return nullptr; // Timeout and no event.
147 case KEY_MOUSE: return make_mouse_event();
148 case KEY_RESIZE: return make_resize_event();
149 default: return make_keyboard_event(input); // Key_event
150 }
151 }
152
153 } // namespace input
154 } // namespace cppurses
155