1#include "taichi/ui/common/window_base.h"
2
3namespace taichi::ui {
4
5#define CHECK_WINDOW_SHOWING \
6 TI_ERROR_IF(!config_.show_window, \
7 "show_window must be True to use this method")
8
9WindowBase::WindowBase(AppConfig config) : config_(config) {
10 if (config_.show_window) {
11 glfw_window_ = create_glfw_window_(config_.name, config_.width,
12 config_.height, config_.window_pos_x,
13 config_.window_pos_y, config_.vsync);
14 glfwSetWindowUserPointer(glfw_window_, this);
15 set_callbacks();
16 last_record_time_ = glfwGetTime();
17 }
18}
19
20void WindowBase::set_callbacks() {
21 glfwSetKeyCallback(glfw_window_, key_callback);
22 glfwSetCursorPosCallback(glfw_window_, mouse_pos_callback);
23 glfwSetMouseButtonCallback(glfw_window_, mouse_button_callback);
24
25 input_handler_.add_key_callback([&](int key, int action) {
26 // Catch exception from button_id_to_name().
27 try {
28 if (action == GLFW_PRESS) {
29 events_.push_back({EventType::Press, button_id_to_name(key)});
30 } else if (action == GLFW_RELEASE) {
31 events_.push_back({EventType::Release, button_id_to_name(key)});
32 }
33 } catch (const std::runtime_error &e) {
34 TI_TRACE("Input: {}.", e.what());
35 }
36 });
37 input_handler_.add_mouse_button_callback([&](int key, int action) {
38 // Catch exception from button_id_to_name().
39 try {
40 if (action == GLFW_PRESS) {
41 events_.push_back({EventType::Press, button_id_to_name(key)});
42 } else if (action == GLFW_RELEASE) {
43 events_.push_back({EventType::Release, button_id_to_name(key)});
44 }
45 } catch (const std::runtime_error &e) {
46 TI_TRACE("Input: {}.", e.what());
47 }
48 });
49}
50
51CanvasBase *WindowBase::get_canvas() {
52 return nullptr;
53}
54
55void WindowBase::show() {
56 CHECK_WINDOW_SHOWING;
57 ++frames_since_last_record_;
58
59 double current_time = glfwGetTime();
60
61 if (current_time - last_record_time_ >= 1) {
62 double FPS =
63 (double)frames_since_last_record_ / (current_time - last_record_time_);
64 std::string glfw_window_text =
65 config_.name + " " + std::to_string(FPS) + " FPS";
66
67 glfwSetWindowTitle(glfw_window_, glfw_window_text.c_str());
68 last_record_time_ = current_time;
69 frames_since_last_record_ = 0;
70 }
71
72 glfwPollEvents();
73}
74
75bool WindowBase::is_pressed(std::string button) {
76 int button_id;
77 // Catch exception from buttom_name_to_id().
78 try {
79 button_id = buttom_name_to_id(button);
80 } catch (const std::runtime_error &e) {
81 TI_TRACE("Pressed: {}.", e.what());
82 return false;
83 }
84 return input_handler_.is_pressed(button_id) > 0;
85}
86
87bool WindowBase::is_running() {
88 if (config_.show_window) {
89 return !glfwWindowShouldClose(glfw_window_);
90 }
91 return true;
92}
93
94void WindowBase::set_is_running(bool value) {
95 if (config_.show_window) {
96 glfwSetWindowShouldClose(glfw_window_, !value);
97 }
98}
99
100std::pair<float, float> WindowBase::get_cursor_pos() {
101 CHECK_WINDOW_SHOWING;
102 float x = input_handler_.last_x();
103 float y = input_handler_.last_y();
104
105 x = x / (float)config_.width;
106 y = (config_.height - y) / (float)config_.height;
107 return std::make_pair(x, y);
108}
109
110std::vector<Event> WindowBase::get_events(EventType tag) {
111 CHECK_WINDOW_SHOWING;
112 glfwPollEvents();
113 std::vector<Event> result;
114 std::list<Event>::iterator i = events_.begin();
115 while (i != events_.end()) {
116 if (i->tag == tag || tag == EventType::Any) {
117 result.push_back(*i);
118 i = events_.erase(i);
119 } else {
120 ++i;
121 }
122 }
123 return result;
124}
125
126bool WindowBase::get_event(EventType tag) {
127 CHECK_WINDOW_SHOWING;
128 glfwPollEvents();
129 if (events_.size() == 0) {
130 return false;
131 }
132 if (tag == EventType::Any) {
133 current_event_ = events_.front();
134 events_.pop_front();
135 return true;
136 } else {
137 std::list<Event>::iterator it;
138 for (it = events_.begin(); it != events_.end(); ++it) {
139 if (it->tag == tag) {
140 current_event_ = *it;
141 events_.erase(it);
142 return true;
143 }
144 }
145 return false;
146 }
147}
148
149// these 2 are used to export the `current_event` field to python
150Event WindowBase::get_current_event() {
151 CHECK_WINDOW_SHOWING;
152 return current_event_;
153}
154void WindowBase::set_current_event(const Event &event) {
155 CHECK_WINDOW_SHOWING;
156 current_event_ = event;
157}
158
159WindowBase::~WindowBase() {
160 if (config_.show_window) {
161 glfwDestroyWindow(glfw_window_);
162 taichi::lang::window_system::glfw_context_release();
163 }
164}
165
166GuiBase *WindowBase::gui() {
167 return nullptr;
168}
169
170void WindowBase::key_callback(GLFWwindow *glfw_window,
171 int key,
172 int scancode,
173 int action,
174 int mode) {
175 auto window =
176 reinterpret_cast<WindowBase *>(glfwGetWindowUserPointer(glfw_window));
177 window->input_handler_.key_callback(glfw_window, key, scancode, action, mode);
178}
179
180void WindowBase::mouse_pos_callback(GLFWwindow *glfw_window,
181 double xpos,
182 double ypos) {
183 auto window =
184 reinterpret_cast<WindowBase *>(glfwGetWindowUserPointer(glfw_window));
185 window->input_handler_.mouse_pos_callback(glfw_window, xpos, ypos);
186}
187
188void WindowBase::mouse_button_callback(GLFWwindow *glfw_window,
189 int button,
190 int action,
191 int modifier) {
192 auto window =
193 reinterpret_cast<WindowBase *>(glfwGetWindowUserPointer(glfw_window));
194 window->input_handler_.mouse_button_callback(glfw_window, button, action,
195 modifier);
196}
197
198} // namespace taichi::ui
199