1 | // LAF Library |
2 | // Copyright (c) 2019-2022 Igara Studio S.A. |
3 | // |
4 | // This file is released under the terms of the MIT license. |
5 | // Read LICENSE.txt for more information. |
6 | |
7 | #include "gfx/hsv.h" |
8 | #include "gfx/rgb.h" |
9 | #include "os/os.h" |
10 | |
11 | #include <algorithm> |
12 | #include <cstdarg> |
13 | #include <cstdlib> |
14 | #include <string> |
15 | #include <vector> |
16 | |
17 | class LogWindow { |
18 | public: |
19 | LogWindow(os::System* system) |
20 | : m_window(system->makeWindow(800, 600)) { |
21 | m_window->setTitle("All Events" ); |
22 | |
23 | recalcMaxLines(); |
24 | |
25 | logLine("-- Events Log --" ); |
26 | } |
27 | |
28 | bool processEvent(const os::Event& ev) { |
29 | switch (ev.type()) { |
30 | |
31 | case os::Event::CloseApp: |
32 | case os::Event::CloseWindow: |
33 | return false; |
34 | |
35 | case os::Event::ResizeWindow: |
36 | logLine("ResizeWindow size=%d,%d" , |
37 | m_window->width(), |
38 | m_window->height()); |
39 | recalcMaxLines(); |
40 | break; |
41 | |
42 | case os::Event::DropFiles: |
43 | logLine("DropFiles files={" ); |
44 | for (const auto& file : ev.files()) { |
45 | logLine(" \"%s\"" , file.c_str()); |
46 | } |
47 | logLine("}" ); |
48 | break; |
49 | |
50 | case os::Event::MouseEnter: logMouseEvent(ev, "MouseEnter" ); break; |
51 | case os::Event::MouseLeave: logMouseEvent(ev, "MouseLeave" ); break; |
52 | case os::Event::MouseMove: logMouseEvent(ev, "MouseMove" ); break; |
53 | case os::Event::MouseDown: logMouseEvent(ev, "MouseDown" ); break; |
54 | case os::Event::MouseUp: logMouseEvent(ev, "MouseUp" ); break; |
55 | case os::Event::MouseDoubleClick: logMouseEvent(ev, "MouseDoubleClick" ); break; |
56 | |
57 | case os::Event::MouseWheel: |
58 | m_mousePos = ev.position(); |
59 | logLine("MouseWheel pos=%d,%d %s=%d,%d%s" , |
60 | ev.position().x, |
61 | ev.position().y, |
62 | ev.preciseWheel() ? " preciseWheel" : "wheel" , |
63 | ev.wheelDelta().x, |
64 | ev.wheelDelta().y, |
65 | modifiersToString(ev.modifiers()).c_str()); |
66 | m_hue += double(ev.wheelDelta().x + ev.wheelDelta().y); |
67 | break; |
68 | |
69 | case os::Event::KeyDown: |
70 | if (ev.scancode() == os::kKeyEsc) { |
71 | if (m_nextEscCloses) |
72 | return false; |
73 | else |
74 | m_nextEscCloses = true; |
75 | logLine("-- Next KeyDown with kKeyEsc will close the window --" ); |
76 | } |
77 | else { |
78 | m_nextEscCloses = false; |
79 | } |
80 | //[[fallthrough]]; |
81 | case os::Event::KeyUp: { |
82 | wchar_t wideUnicode[2] = { ev.unicodeChar(), 0 }; |
83 | logLine("%s scancode=%d unicode=%d (%s)%s" , |
84 | (ev.type() == os::Event::KeyDown ? "KeyDown" : "KeyUp" ), |
85 | ev.scancode(), |
86 | ev.unicodeChar(), |
87 | base::to_utf8(wideUnicode).c_str(), |
88 | modifiersToString(ev.modifiers()).c_str()); |
89 | break; |
90 | } |
91 | |
92 | case os::Event::TouchMagnify: |
93 | logLine("TouchMagnify %.4g" , |
94 | ev.magnification()); |
95 | m_brushSize += 32*ev.magnification(); |
96 | m_brushSize = std::clamp(m_brushSize, 1.0, 500.0); |
97 | break; |
98 | |
99 | default: |
100 | // Do nothing |
101 | break; |
102 | } |
103 | return true; |
104 | } |
105 | |
106 | void flush() { |
107 | if (m_oldLogSize != m_textLog.size()) { |
108 | int newlines = m_textLog.size() - m_oldLogSize; |
109 | while (m_textLog.size() > m_maxlines) |
110 | m_textLog.erase(m_textLog.begin()); |
111 | |
112 | scrollAndDrawLog(newlines); |
113 | |
114 | m_oldLogSize = m_textLog.size(); |
115 | } |
116 | } |
117 | |
118 | private: |
119 | void recalcMaxLines() { |
120 | m_maxlines = (m_window->height() - m_lineHeight) / m_lineHeight; |
121 | } |
122 | |
123 | void scrollAndDrawLog(const int newlines) { |
124 | os::Surface* surface = m_window->surface(); |
125 | os::SurfaceLock lock(surface); |
126 | const gfx::Rect rc = surface->bounds(); |
127 | |
128 | os::Paint p; |
129 | p.style(os::Paint::Fill); |
130 | p.color(gfx::rgba(0, 0, 0, 8)); |
131 | |
132 | // Scroll old lines |
133 | int i; |
134 | if (m_textLog.size() >= m_maxlines) { |
135 | int h = m_lineHeight*newlines; |
136 | surface->scrollTo(rc, 0, -h); |
137 | |
138 | surface->drawRect(gfx::Rect(rc.x, rc.y, rc.w, rc.h-h), p); |
139 | p.color(gfx::rgba(0, 0, 0)); |
140 | surface->drawRect(gfx::Rect(rc.x, rc.y+rc.h-h, rc.w, h), p); |
141 | |
142 | i = (m_textLog.size()-newlines); |
143 | } |
144 | // First lines without scroll |
145 | else { |
146 | i = m_oldLogSize; |
147 | surface->drawRect(gfx::Rect(rc.x, rc.y, rc.w, i*m_lineHeight), p); |
148 | } |
149 | |
150 | os::Paint paint; |
151 | paint.color(gfx::rgba(255, 255, 255)); |
152 | for (; i<m_textLog.size(); ++i) |
153 | os::draw_text(surface, nullptr, m_textLog[i], |
154 | gfx::Point(0, (1+i)*m_lineHeight), &paint); |
155 | |
156 | gfx::Rgb rgb(gfx::Hsv(m_hue, 1.0, 1.0)); |
157 | paint.color(gfx::rgba(rgb.red(), rgb.green(), rgb.blue())); |
158 | paint.antialias(true); |
159 | surface->drawCircle(m_mousePos.x, m_mousePos.y, m_brushSize, paint); |
160 | |
161 | // Invalidates the whole window to show it on the screen. |
162 | if (m_window->isVisible()) |
163 | m_window->invalidateRegion(gfx::Region(rc)); |
164 | else |
165 | m_window->setVisible(true); |
166 | } |
167 | |
168 | void logMouseEvent(const os::Event& ev, const char* eventName) { |
169 | const os::Event::MouseButton mb = ev.button(); |
170 | const os::PointerType pt = ev.pointerType(); |
171 | |
172 | m_mousePos = ev.position(); |
173 | logLine("%s pos=%d,%d%s%s%s" , |
174 | eventName, |
175 | ev.position().x, |
176 | ev.position().y, |
177 | (mb == os::Event::LeftButton ? " LeftButton" : |
178 | mb == os::Event::RightButton ? " RightButton" : |
179 | mb == os::Event::MiddleButton ? " MiddleButton" : |
180 | mb == os::Event::X1Button ? " X1Button" : |
181 | mb == os::Event::X2Button ? " X2Button" : "" ), |
182 | (pt == os::PointerType::Mouse ? " Mouse" : |
183 | pt == os::PointerType::Touchpad ? " Touchpad" : |
184 | pt == os::PointerType::Touch ? " Touch" : |
185 | pt == os::PointerType::Pen ? " Pen" : |
186 | pt == os::PointerType::Cursor ? " Cursor" : |
187 | pt == os::PointerType::Eraser ? " Eraser" : "" ), |
188 | modifiersToString(ev.modifiers()).c_str()); |
189 | } |
190 | |
191 | void logLine(const char* str, ...) { |
192 | va_list ap; |
193 | va_start(ap, str); |
194 | char buf[4096]; |
195 | vsprintf(buf, str, ap); |
196 | va_end(ap); |
197 | |
198 | m_textLog.push_back(buf); |
199 | } |
200 | |
201 | static std::string modifiersToString(os::KeyModifiers mods) { |
202 | std::string s; |
203 | if (mods & os::kKeyShiftModifier) s += " Shift" ; |
204 | if (mods & os::kKeyCtrlModifier ) s += " Ctrl" ; |
205 | if (mods & os::kKeyAltModifier ) s += " Alt" ; |
206 | if (mods & os::kKeyCmdModifier ) s += " Command" ; |
207 | if (mods & os::kKeySpaceModifier) s += " Space" ; |
208 | if (mods & os::kKeyWinModifier ) s += " Win" ; |
209 | return s; |
210 | } |
211 | |
212 | os::WindowRef m_window; |
213 | std::vector<std::string> m_textLog; |
214 | size_t m_oldLogSize = 0; |
215 | int m_lineHeight = 12; |
216 | int m_maxlines = 0; |
217 | gfx::Point m_mousePos; |
218 | double m_brushSize = 4; |
219 | double m_hue = 0.0; |
220 | bool m_nextEscCloses = false; |
221 | }; |
222 | |
223 | int app_main(int argc, char* argv[]) |
224 | { |
225 | auto system = os::make_system(); |
226 | system->setAppMode(os::AppMode::GUI); |
227 | |
228 | LogWindow window(system.get()); |
229 | |
230 | system->finishLaunching(); |
231 | system->activateApp(); |
232 | |
233 | os::EventQueue* queue = system->eventQueue(); |
234 | while (true) { |
235 | window.flush(); |
236 | |
237 | os::Event ev; |
238 | queue->getEvent(ev); |
239 | if (!window.processEvent(ev)) |
240 | break; |
241 | } |
242 | |
243 | return 0; |
244 | } |
245 | |