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