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
17class LogWindow {
18public:
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
118private:
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
223int 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