1 /**
2 * @file lv_glfw_window.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_glfw_window_private.h"
10 #if LV_USE_OPENGLES
11 #include <stdlib.h>
12 #include "../../core/lv_refr.h"
13 #include "../../stdlib/lv_string.h"
14 #include "../../core/lv_global.h"
15 #include "../../display/lv_display_private.h"
16 #include "../../indev/lv_indev.h"
17 #include "../../lv_init.h"
18 #include "../../misc/lv_area_private.h"
19
20 #include <GL/glew.h>
21 #include <GLFW/glfw3.h>
22
23 #include "lv_opengles_driver.h"
24 #include "lv_opengles_texture.h"
25
26 /*********************
27 * DEFINES
28 *********************/
29
30 /**********************
31 * TYPEDEFS
32 **********************/
33
34 /**********************
35 * STATIC PROTOTYPES
36 **********************/
37 static void window_update_handler(lv_timer_t * t);
38 static uint32_t lv_glfw_tick_count_callback(void);
39 static lv_glfw_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window);
40 static void glfw_error_cb(int error, const char * description);
41 static int lv_glfw_init(void);
42 static int lv_glew_init(void);
43 static void lv_glfw_timer_init(void);
44 static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev);
45 static void lv_glfw_window_quit(void);
46 static void window_close_callback(GLFWwindow * window);
47 static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods);
48 static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods);
49 static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos);
50 static void proc_mouse(lv_glfw_window_t * window);
51 static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data);
52 static void framebuffer_size_callback(GLFWwindow * window, int width, int height);
53
54 /**********************
55 * STATIC VARIABLES
56 **********************/
57 static bool glfw_inited;
58 static bool glew_inited;
59 static lv_timer_t * update_handler_timer;
60 static lv_ll_t glfw_window_ll;
61
62 /**********************
63 * MACROS
64 **********************/
65
66 /**********************
67 * GLOBAL FUNCTIONS
68 **********************/
69
lv_glfw_window_create(int32_t hor_res,int32_t ver_res,bool use_mouse_indev)70 lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev)
71 {
72 if(lv_glfw_init() != 0) {
73 return NULL;
74 }
75
76 lv_glfw_window_t * window = lv_ll_ins_tail(&glfw_window_ll);
77 LV_ASSERT_MALLOC(window);
78 if(window == NULL) return NULL;
79 lv_memzero(window, sizeof(*window));
80
81 /* Create window with graphics context */
82 lv_glfw_window_t * existing_window = lv_ll_get_head(&glfw_window_ll);
83 window->window = glfwCreateWindow(hor_res, ver_res, "LVGL Simulator", NULL,
84 existing_window ? existing_window->window : NULL);
85 if(window->window == NULL) {
86 LV_LOG_ERROR("glfwCreateWindow fail.");
87 lv_ll_remove(&glfw_window_ll, window);
88 lv_free(window);
89 return NULL;
90 }
91
92 window->hor_res = hor_res;
93 window->ver_res = ver_res;
94 lv_ll_init(&window->textures, sizeof(lv_glfw_texture_t));
95 window->use_indev = use_mouse_indev;
96
97 glfwSetWindowUserPointer(window->window, window);
98 lv_glfw_timer_init();
99 lv_glfw_window_config(window->window, use_mouse_indev);
100 lv_glew_init();
101 glfwMakeContextCurrent(window->window);
102 lv_opengles_init();
103
104 return window;
105 }
106
lv_glfw_window_delete(lv_glfw_window_t * window)107 void lv_glfw_window_delete(lv_glfw_window_t * window)
108 {
109 glfwDestroyWindow(window->window);
110 if(window->use_indev) {
111 lv_glfw_texture_t * texture;
112 LV_LL_READ(&window->textures, texture) {
113 if(texture->indev != NULL) lv_indev_delete(texture->indev);
114 }
115 }
116 lv_ll_clear(&window->textures);
117 lv_ll_remove(&glfw_window_ll, window);
118 lv_free(window);
119
120 if(lv_ll_is_empty(&glfw_window_ll)) {
121 lv_glfw_window_quit();
122 }
123 }
124
lv_glfw_window_add_texture(lv_glfw_window_t * window,unsigned int texture_id,int32_t w,int32_t h)125 lv_glfw_texture_t * lv_glfw_window_add_texture(lv_glfw_window_t * window, unsigned int texture_id, int32_t w, int32_t h)
126 {
127 lv_glfw_texture_t * texture = lv_ll_ins_tail(&window->textures);
128 LV_ASSERT_MALLOC(texture);
129 if(texture == NULL) return NULL;
130 lv_memzero(texture, sizeof(*texture));
131 texture->window = window;
132 texture->texture_id = texture_id;
133 lv_area_set(&texture->area, 0, 0, w - 1, h - 1);
134 texture->opa = LV_OPA_COVER;
135
136 if(window->use_indev) {
137 lv_display_t * texture_disp = lv_opengles_texture_get_from_texture_id(texture_id);
138 if(texture_disp != NULL) {
139 lv_indev_t * indev = lv_indev_create();
140 if(indev == NULL) {
141 lv_ll_remove(&window->textures, texture);
142 lv_free(texture);
143 return NULL;
144 }
145 texture->indev = indev;
146 lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
147 lv_indev_set_read_cb(indev, indev_read_cb);
148 lv_indev_set_driver_data(indev, texture);
149 lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
150 lv_indev_set_display(indev, texture_disp);
151 }
152 }
153
154 return texture;
155 }
156
lv_glfw_texture_remove(lv_glfw_texture_t * texture)157 void lv_glfw_texture_remove(lv_glfw_texture_t * texture)
158 {
159 if(texture->indev != NULL) {
160 lv_indev_delete(texture->indev);
161 }
162 lv_ll_remove(&texture->window->textures, texture);
163 lv_free(texture);
164 }
165
lv_glfw_texture_set_x(lv_glfw_texture_t * texture,int32_t x)166 void lv_glfw_texture_set_x(lv_glfw_texture_t * texture, int32_t x)
167 {
168 lv_area_set_pos(&texture->area, x, texture->area.y1);
169 }
170
lv_glfw_texture_set_y(lv_glfw_texture_t * texture,int32_t y)171 void lv_glfw_texture_set_y(lv_glfw_texture_t * texture, int32_t y)
172 {
173 lv_area_set_pos(&texture->area, texture->area.x1, y);
174 }
175
lv_glfw_texture_set_opa(lv_glfw_texture_t * texture,lv_opa_t opa)176 void lv_glfw_texture_set_opa(lv_glfw_texture_t * texture, lv_opa_t opa)
177 {
178 texture->opa = opa;
179 }
180
lv_glfw_texture_get_mouse_indev(lv_glfw_texture_t * texture)181 lv_indev_t * lv_glfw_texture_get_mouse_indev(lv_glfw_texture_t * texture)
182 {
183 return texture->indev;
184 }
185
186 /**********************
187 * STATIC FUNCTIONS
188 **********************/
189
lv_glfw_init(void)190 static int lv_glfw_init(void)
191 {
192 if(glfw_inited) {
193 return 0;
194 }
195
196 glfwSetErrorCallback(glfw_error_cb);
197
198 int ret = glfwInit();
199 if(ret == 0) {
200 LV_LOG_ERROR("glfwInit fail.");
201 return 1;
202 }
203
204 lv_ll_init(&glfw_window_ll, sizeof(lv_glfw_window_t));
205
206 glfw_inited = true;
207 return 0;
208 }
209
lv_glew_init(void)210 static int lv_glew_init(void)
211 {
212 if(glew_inited) {
213 return 0;
214 }
215
216 GLenum ret = glewInit();
217 if(ret != GLEW_OK) {
218 LV_LOG_ERROR("glewInit fail: %d.", ret);
219 return ret;
220 }
221
222 LV_LOG_INFO("GL version: %s", glGetString(GL_VERSION));
223 LV_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
224
225 glew_inited = true;
226
227 return 0;
228 }
229
lv_glfw_timer_init(void)230 static void lv_glfw_timer_init(void)
231 {
232 if(update_handler_timer == NULL) {
233 update_handler_timer = lv_timer_create(window_update_handler, LV_DEF_REFR_PERIOD, NULL);
234 lv_tick_set_cb(lv_glfw_tick_count_callback);
235 }
236 }
237
lv_glfw_window_config(GLFWwindow * window,bool use_mouse_indev)238 static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev)
239 {
240 glfwMakeContextCurrent(window);
241
242 glfwSwapInterval(1);
243
244 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
245
246 if(use_mouse_indev) {
247 glfwSetMouseButtonCallback(window, mouse_button_callback);
248 glfwSetCursorPosCallback(window, mouse_move_callback);
249 }
250
251 glfwSetKeyCallback(window, key_callback);
252
253 glfwSetWindowCloseCallback(window, window_close_callback);
254 }
255
lv_glfw_window_quit(void)256 static void lv_glfw_window_quit(void)
257 {
258 lv_timer_delete(update_handler_timer);
259 update_handler_timer = NULL;
260
261 glfwTerminate();
262 glfw_inited = false;
263
264 lv_deinit();
265
266 exit(0);
267 }
268
window_update_handler(lv_timer_t * t)269 static void window_update_handler(lv_timer_t * t)
270 {
271 LV_UNUSED(t);
272
273 lv_glfw_window_t * window;
274
275 glfwPollEvents();
276
277 /* delete windows that are ready to close */
278 window = lv_ll_get_head(&glfw_window_ll);
279 while(window) {
280 lv_glfw_window_t * window_to_delete = window->closing ? window : NULL;
281 window = lv_ll_get_next(&glfw_window_ll, window);
282 if(window_to_delete) {
283 glfwSetWindowShouldClose(window_to_delete->window, GLFW_TRUE);
284 lv_glfw_window_delete(window_to_delete);
285 }
286 }
287
288 /* render each window */
289 LV_LL_READ(&glfw_window_ll, window) {
290 glfwMakeContextCurrent(window->window);
291 lv_opengles_viewport(0, 0, window->hor_res, window->ver_res);
292 lv_opengles_render_clear();
293
294 /* render each texture in the window */
295 lv_glfw_texture_t * texture;
296 LV_LL_READ(&window->textures, texture) {
297 /* if the added texture is an LVGL opengles texture display, refresh it before rendering it */
298 lv_display_t * texture_disp = lv_opengles_texture_get_from_texture_id(texture->texture_id);
299 if(texture_disp != NULL) {
300 lv_refr_now(texture_disp);
301 }
302
303 lv_area_t clip_area = texture->area;
304 #if LV_USE_DRAW_OPENGLES
305 lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res,
306 &clip_area, texture_disp == NULL);
307 #else
308 lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res,
309 &clip_area, true);
310 #endif
311 }
312
313 /* Swap front and back buffers */
314 glfwSwapBuffers(window->window);
315 }
316 }
317
glfw_error_cb(int error,const char * description)318 static void glfw_error_cb(int error, const char * description)
319 {
320 LV_LOG_ERROR("GLFW Error %d: %s", error, description);
321 }
322
lv_glfw_get_lv_window_from_window(GLFWwindow * window)323 static lv_glfw_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window)
324 {
325 return glfwGetWindowUserPointer(window);
326 }
327
window_close_callback(GLFWwindow * window)328 static void window_close_callback(GLFWwindow * window)
329 {
330 lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window);
331 lv_window->closing = 1;
332 }
333
key_callback(GLFWwindow * window,int key,int scancode,int action,int mods)334 static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods)
335 {
336 LV_UNUSED(scancode);
337 LV_UNUSED(mods);
338 if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
339 lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window);
340 lv_window->closing = 1;
341 }
342 }
343
mouse_button_callback(GLFWwindow * window,int button,int action,int mods)344 static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods)
345 {
346 LV_UNUSED(mods);
347 if(button == GLFW_MOUSE_BUTTON_LEFT) {
348 lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window);
349 lv_window->mouse_last_state = action == GLFW_PRESS ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
350 proc_mouse(lv_window);
351 }
352 }
353
mouse_move_callback(GLFWwindow * window,double xpos,double ypos)354 static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos)
355 {
356 lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window);
357 lv_window->mouse_last_point.x = (int32_t)xpos;
358 lv_window->mouse_last_point.y = (int32_t)ypos;
359 proc_mouse(lv_window);
360 }
361
proc_mouse(lv_glfw_window_t * window)362 static void proc_mouse(lv_glfw_window_t * window)
363 {
364 /* mouse activity will affect the topmost LVGL display texture */
365 lv_glfw_texture_t * texture;
366 LV_LL_READ_BACK(&window->textures, texture) {
367 if(lv_area_is_point_on(&texture->area, &window->mouse_last_point, 0)) {
368 /* adjust the mouse pointer coordinates so that they are relative to the texture */
369 texture->indev_last_point.x = window->mouse_last_point.x - texture->area.x1;
370 texture->indev_last_point.y = window->mouse_last_point.y - texture->area.y1;
371 texture->indev_last_state = window->mouse_last_state;
372 lv_indev_read(texture->indev);
373 break;
374 }
375 }
376 }
377
indev_read_cb(lv_indev_t * indev,lv_indev_data_t * data)378 static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data)
379 {
380 lv_glfw_texture_t * texture = lv_indev_get_driver_data(indev);
381 data->point = texture->indev_last_point;
382 data->state = texture->indev_last_state;
383 }
384
framebuffer_size_callback(GLFWwindow * window,int width,int height)385 static void framebuffer_size_callback(GLFWwindow * window, int width, int height)
386 {
387 lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window);
388 lv_window->hor_res = width;
389 lv_window->ver_res = height;
390 }
391
lv_glfw_tick_count_callback(void)392 static uint32_t lv_glfw_tick_count_callback(void)
393 {
394 double tick = glfwGetTime() * 1000.0;
395 return (uint32_t)tick;
396 }
397
398 #endif /*LV_USE_OPENGLES*/
399