1 | #include "taichi/ui/common/window_base.h" |
2 | |
3 | namespace 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 | |
9 | WindowBase::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 | |
20 | void 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 | |
51 | CanvasBase *WindowBase::get_canvas() { |
52 | return nullptr; |
53 | } |
54 | |
55 | void 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 | |
75 | bool 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 | |
87 | bool WindowBase::is_running() { |
88 | if (config_.show_window) { |
89 | return !glfwWindowShouldClose(glfw_window_); |
90 | } |
91 | return true; |
92 | } |
93 | |
94 | void WindowBase::set_is_running(bool value) { |
95 | if (config_.show_window) { |
96 | glfwSetWindowShouldClose(glfw_window_, !value); |
97 | } |
98 | } |
99 | |
100 | std::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 | |
110 | std::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 | |
126 | bool 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 |
150 | Event WindowBase::get_current_event() { |
151 | CHECK_WINDOW_SHOWING; |
152 | return current_event_; |
153 | } |
154 | void WindowBase::set_current_event(const Event &event) { |
155 | CHECK_WINDOW_SHOWING; |
156 | current_event_ = event; |
157 | } |
158 | |
159 | WindowBase::~WindowBase() { |
160 | if (config_.show_window) { |
161 | glfwDestroyWindow(glfw_window_); |
162 | taichi::lang::window_system::glfw_context_release(); |
163 | } |
164 | } |
165 | |
166 | GuiBase *WindowBase::gui() { |
167 | return nullptr; |
168 | } |
169 | |
170 | void 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 | |
180 | void 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 | |
188 | void 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 | |