diff --git a/docs/integration/driver/opengles.rst b/docs/integration/driver/opengles.rst index 254e31dd7..bcf63dc4d 100644 --- a/docs/integration/driver/opengles.rst +++ b/docs/integration/driver/opengles.rst @@ -26,8 +26,8 @@ Configure OpenGL driver #define LV_USE_OPENGLES 1 -Usage ------ +Basic usage +----------- .. code:: c @@ -35,31 +35,163 @@ Usage #include "lvgl/examples/lv_examples.h" #include "lvgl/demos/lv_demos.h" + #define WIDTH 640 + #define HEIGHT 480 + int main() { + /* initialize lvgl */ lv_init(); - lv_display_t * disp = lv_glfw_window_create(480, 272); + /* create a window and initialize OpenGL */ + lv_glfw_window_t * window = lv_glfw_window_create(WIDTH, HEIGHT, true); - lv_indev_t * mouse = lv_glfw_mouse_create(); - lv_indev_set_group(mouse, lv_group_get_default()); - lv_indev_set_display(mouse, disp); + /* create a display that flushes to a texture */ + lv_display_t * texture = lv_opengles_texture_create(WIDTH, HEIGHT); + lv_display_set_default(texture); - lv_display_set_default(disp); + /* add the texture to the window */ + unsigned int texture_id = lv_opengles_texture_get_texture_id(texture); + lv_glfw_texture_t * window_texture = lv_glfw_window_add_texture(window, texture_id, WIDTH, HEIGHT); - LV_IMAGE_DECLARE(mouse_cursor_icon); /*Declare the image file.*/ - lv_obj_t * cursor_obj; - cursor_obj = lv_image_create(lv_screen_active()); /*Create an image object for the cursor */ - lv_image_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/ - lv_indev_set_cursor(mouse, cursor_obj); /*Connect the image object to the driver*/ + /* get the mouse indev of the window texture */ + lv_indev_t * mouse = lv_glfw_texture_get_mouse_indev(window_texture); + /* add a cursor to the mouse indev */ + LV_IMAGE_DECLARE(mouse_cursor_icon); + lv_obj_t * cursor_obj = lv_image_create(lv_screen_active()); + lv_image_set_src(cursor_obj, &mouse_cursor_icon); + lv_indev_set_cursor(mouse, cursor_obj); + + /* create objects on the screen */ lv_demo_widgets(); while (1) { - uint32_t time_till_next = lv_timer_handler(); - lv_delay_ms(time_till_next); + uint32_t time_until_next = lv_timer_handler(); + lv_delay_ms(time_until_next); } return 0; } + +Advanced usage +-------------- + +The OpenGL driver can draw textures from the user. A third-party library could be +used to add content to a texture and the driver will draw the texture in the window. + +.. code:: c + + #include "lvgl/lvgl.h" + #include + #include + + #define WIDTH 640 + #define HEIGHT 480 + + void custom_texture_example(void) + { + /***************** + * MAIN WINDOW + *****************/ + + /* create a window and initialize OpenGL */ + /* multiple windows can be created */ + lv_glfw_window_t * window = lv_glfw_window_create(WIDTH, HEIGHT, true); + + /**************************** + * OPTIONAL MAIN TEXTURE + ****************************/ + + /* create a main display that flushes to a texture */ + lv_display_t * main_texture = lv_opengles_texture_create(WIDTH, HEIGHT); + lv_display_set_default(main_texture); + + /* add the main texture to the window */ + unsigned int main_texture_id = lv_opengles_texture_get_texture_id(main_texture); + lv_glfw_texture_t * window_main_texture = lv_glfw_window_add_texture(window, main_texture_id, WIDTH, HEIGHT); + + /* get the mouse indev of this main texture */ + lv_indev_t * main_texture_mouse = lv_glfw_texture_get_mouse_indev(window_main_texture); + + /* add a cursor to the mouse indev */ + LV_IMAGE_DECLARE(mouse_cursor_icon); + lv_obj_t * cursor_obj = lv_image_create(lv_screen_active()); + lv_image_set_src(cursor_obj, &mouse_cursor_icon); + lv_indev_set_cursor(main_texture_mouse, cursor_obj); + + /* create objects on the screen of the main texture */ + lv_demo_widgets(); + + /********************** + * ANOTHER TEXTURE + **********************/ + + /* create a sub display that flushes to a texture */ + const int32_t sub_texture_w = 300; + const int32_t sub_texture_h = 300; + lv_display_t * sub_texture = lv_opengles_texture_create(sub_texture_w, sub_texture_h); + + /* add the sub texture to the window */ + unsigned int sub_texture_id = lv_opengles_texture_get_texture_id(sub_texture); + lv_glfw_texture_t * window_sub_texture = lv_glfw_window_add_texture(window, sub_texture_id, sub_texture_w, sub_texture_h); + + /* create objects on the screen of the sub texture */ + lv_display_set_default(sub_texture); + lv_obj_set_style_bg_color(lv_screen_active(), lv_color_black(), 0); + lv_example_anim_2(); + lv_display_set_default(main_texture); + + /* position the sub texture within the window */ + lv_glfw_texture_set_x(window_sub_texture, 250); + lv_glfw_texture_set_y(window_sub_texture, 150); + + /* optionally change the opacity of the sub texture */ + lv_glfw_texture_set_opa(window_sub_texture, LV_OPA_80); + + /********************************************* + * USE AN EXTERNAL OPENGL TEXTURE IN LVGL + *********************************************/ + + unsigned int external_texture_id; + glGenTextures(1, &external_texture_id); + glBindTexture(GL_TEXTURE_2D, external_texture_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + LV_IMAGE_DECLARE(img_cogwheel_argb); + #if LV_COLOR_DEPTH == 8 + const int texture_format = GL_R8; + #elif LV_COLOR_DEPTH == 16 + const int texture_format = GL_RGB565; + #elif LV_COLOR_DEPTH == 24 + const int texture_format = GL_RGB; + #elif LV_COLOR_DEPTH == 32 + const int texture_format = GL_RGBA; + #else + #error("Unsupported color format") + #endif + glTexImage2D(GL_TEXTURE_2D, 0, texture_format, img_cogwheel_argb.header.w, img_cogwheel_argb.header.h, 0, GL_BGRA, GL_UNSIGNED_BYTE, img_cogwheel_argb.data); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + /* add the external texture to the window */ + lv_glfw_texture_t * window_external_texture = lv_glfw_window_add_texture(window, external_texture_id, img_cogwheel_argb.header.w, img_cogwheel_argb.header.h); + + /* set the position and opacity of the external texture within the window */ + lv_glfw_texture_set_x(window_external_texture, 20); + lv_glfw_texture_set_y(window_external_texture, 20); + lv_glfw_texture_set_opa(window_external_texture, LV_OPA_70); + + /********************************************* + * USE AN LVGL TEXTURE IN ANOTHER LIBRARY + *********************************************/ + + lv_refr_now(sub_texture); + + /* the texture is drawn on by LVGL and can be used by anything that uses OpenGL textures */ + third_party_lib_use_texture(sub_texture_id); + } diff --git a/src/drivers/glfw/lv_glfw_mouse.c b/src/drivers/glfw/lv_glfw_mouse.c deleted file mode 100644 index d4078b5fa..000000000 --- a/src/drivers/glfw/lv_glfw_mouse.c +++ /dev/null @@ -1,87 +0,0 @@ -/** - * @file lv_glfw_mouse.c - * - */ - -/********************* - * INCLUDES - *********************/ -#include "lv_glfw_mouse.h" -#include "lv_glfw_mouse_private.h" -#if LV_USE_OPENGLES - -#include -#include -#include "../../core/lv_group.h" -#include "../../stdlib/lv_string.h" - -/********************* - * DEFINES - *********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ -static void opengles_mouse_read(lv_indev_t * indev, lv_indev_data_t * data); -static void release_indev_cb(lv_event_t * e); - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -lv_indev_t * lv_glfw_mouse_create(void) -{ - lv_glfw_mouse_t * dsc = lv_malloc_zeroed(sizeof(lv_glfw_mouse_t)); - LV_ASSERT_MALLOC(dsc); - if(dsc == NULL) return NULL; - - lv_indev_t * indev = lv_indev_create(); - LV_ASSERT_MALLOC(indev); - if(indev == NULL) { - lv_free(dsc); - return NULL; - } - - lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); - lv_indev_set_read_cb(indev, opengles_mouse_read); - lv_indev_set_driver_data(indev, dsc); - - lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT); - lv_indev_add_event_cb(indev, release_indev_cb, LV_EVENT_DELETE, indev); - - return indev; -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -static void opengles_mouse_read(lv_indev_t * indev, lv_indev_data_t * data) -{ - lv_glfw_mouse_t * dsc = lv_indev_get_driver_data(indev); - - /*Store the collected data*/ - data->point.x = dsc->last_x; - data->point.y = dsc->last_y; - data->state = dsc->left_button_down ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - data->enc_diff = dsc->diff; - dsc->diff = 0; -} - -static void release_indev_cb(lv_event_t * e) -{ - lv_indev_t * indev = (lv_indev_t *) lv_event_get_user_data(e); - lv_glfw_mouse_t * dsc = lv_indev_get_driver_data(indev); - if(dsc) { - lv_indev_set_driver_data(indev, NULL); - lv_indev_set_read_cb(indev, NULL); - lv_free(dsc); - LV_LOG_INFO("done"); - } -} - -#endif /* LV_USE_OPENGLES */ diff --git a/src/drivers/glfw/lv_glfw_mouse.h b/src/drivers/glfw/lv_glfw_mouse.h deleted file mode 100644 index 979fee964..000000000 --- a/src/drivers/glfw/lv_glfw_mouse.h +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @file lv_glfw_mouse.h - * - */ - -#ifndef LV_GLFW_MOUSE_H -#define LV_GLFW_MOUSE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ -#include "lv_glfw_window.h" -#if LV_USE_OPENGLES - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -/********************** - * GLOBAL PROTOTYPES - **********************/ - -lv_indev_t * lv_glfw_mouse_create(void); - -/********************** - * MACROS - **********************/ - -#endif /*LV_USE_OPENGLES*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV_GLFW_MOUSE_H */ diff --git a/src/drivers/glfw/lv_glfw_mouse_private.c b/src/drivers/glfw/lv_glfw_mouse_private.c deleted file mode 100644 index b8cbe95eb..000000000 --- a/src/drivers/glfw/lv_glfw_mouse_private.c +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @file lv_glfw_mouse_private.c - * - */ - -/********************* - * INCLUDES - *********************/ -#include "../../display/lv_display.h" -#include "../../indev/lv_indev.h" - -#include "lv_glfw_mouse_private.h" -#if LV_USE_OPENGLES - -#include -#include -#include "../../core/lv_group.h" -#include "../../stdlib/lv_string.h" - -/********************* - * DEFINES - *********************/ - -/********************** - * STATIC PROTOTYPES - **********************/ - -/********************** - * STATIC VARIABLES - **********************/ - -/********************** - * GLOBAL FUNCTIONS - **********************/ - -void lv_glfw_mouse_move_handler(lv_display_t * disp, int x, int y) -{ - if(disp == NULL) { - return; - } - - /*Find a suitable indev*/ - lv_indev_t * indev = lv_indev_get_next(NULL); - while(indev) { - if(lv_indev_get_display(indev) == disp && lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER) { - break; - } - indev = lv_indev_get_next(indev); - } - - if(indev == NULL) return; - lv_glfw_mouse_t * indev_dev = lv_indev_get_driver_data(indev); - if(indev_dev == NULL) return; - - indev_dev->last_x = x; - indev_dev->last_y = y; - - lv_indev_read(indev); -} - -void lv_glfw_mouse_btn_handler(lv_display_t * disp, int btn_down) -{ - if(disp == NULL) { - return; - } - - /*Find a suitable indev*/ - lv_indev_t * indev = lv_indev_get_next(NULL); - while(indev) { - if(lv_indev_get_display(indev) == disp && lv_indev_get_type(indev) == LV_INDEV_TYPE_POINTER) { - break; - } - indev = lv_indev_get_next(indev); - } - - if(indev == NULL) return; - lv_glfw_mouse_t * indev_dev = lv_indev_get_driver_data(indev); - if(indev_dev == NULL) return; - - indev_dev->left_button_down = btn_down; - - lv_indev_read(indev); -} - -/********************** - * STATIC FUNCTIONS - **********************/ - -#endif /* LV_USE_OPENGLES */ diff --git a/src/drivers/glfw/lv_glfw_mouse_private.h b/src/drivers/glfw/lv_glfw_mouse_private.h deleted file mode 100644 index b34e155a9..000000000 --- a/src/drivers/glfw/lv_glfw_mouse_private.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * @file lv_glfw_mouse_private.h - * - */ - -#ifndef LV_GLFW_MOUSE_PRIVATE_H -#define LV_GLFW_MOUSE_PRIVATE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/********************* - * INCLUDES - *********************/ - -#if LV_USE_OPENGLES - -/********************* - * DEFINES - *********************/ - -/********************** - * TYPEDEFS - **********************/ - -typedef struct { - int16_t last_x; - int16_t last_y; - bool left_button_down; - int32_t diff; -} lv_glfw_mouse_t; - -/********************** - * GLOBAL PROTOTYPES - **********************/ - -void lv_glfw_mouse_move_handler(lv_display_t * disp, int x, int y); - -void lv_glfw_mouse_btn_handler(lv_display_t * disp, int btn_down); - -/********************** - * MACROS - **********************/ - -#endif /*LV_USE_OPENGLES*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* LV_GLFW_MOUSE_PRIVATE_H */ diff --git a/src/drivers/glfw/lv_glfw_window.c b/src/drivers/glfw/lv_glfw_window.c index 7b26b9d46..849cbf58b 100644 --- a/src/drivers/glfw/lv_glfw_window.c +++ b/src/drivers/glfw/lv_glfw_window.c @@ -6,23 +6,22 @@ /********************* * INCLUDES *********************/ -#include "lv_glfw_window.h" -#include "lv_glfw_mouse_private.h" +#include "lv_glfw_window_private.h" #if LV_USE_OPENGLES -#include #include -#include -#include #include "../../core/lv_refr.h" #include "../../stdlib/lv_string.h" #include "../../core/lv_global.h" #include "../../display/lv_display_private.h" +#include "../../indev/lv_indev.h" #include "../../lv_init.h" +#include "../../misc/lv_area_private.h" #include #include #include "lv_opengles_driver.h" +#include "lv_opengles_texture.h" /********************* * DEFINES @@ -31,45 +30,34 @@ /********************** * TYPEDEFS **********************/ -typedef struct { - GLFWwindow * window; - - uint8_t * fb1; - uint8_t * fb2; - uint8_t * fb_act; - uint8_t * buf1; - uint8_t * buf2; - uint8_t * rotated_buf; - size_t rotated_buf_size; - - uint8_t zoom; - uint8_t ignore_size_chg; -} lv_glfw_window_t; /********************** * STATIC PROTOTYPES **********************/ -static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * color_p); -static int window_create(lv_display_t * disp); -static void window_update(lv_display_t * disp); -static void texture_resize(lv_display_t * disp); static void window_update_handler(lv_timer_t * t); -static void window_event_handler(lv_timer_t * t); -static void release_disp_cb(lv_event_t * e); -static void res_chg_event_cb(lv_event_t * e); static uint32_t lv_glfw_tick_count_callback(void); - -/*********************** - * GLOBAL PROTOTYPES - ***********************/ -static bool inited = false; -bool deiniting = false; +static lv_glfw_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window); +static void glfw_error_cb(int error, const char * description); +static int lv_glfw_init(void); +static int lv_glew_init(void); +static void lv_glfw_timer_init(void); +static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev); +static void lv_glfw_window_quit(void); +static void window_close_callback(GLFWwindow * window); +static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods); +static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods); +static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos); +static void proc_mouse(lv_glfw_window_t * window); +static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data); +static void framebuffer_size_callback(GLFWwindow * window, int width, int height); /********************** * STATIC VARIABLES **********************/ +static bool glfw_inited; +static bool glew_inited; static lv_timer_t * update_handler_timer; -static lv_timer_t * event_handler_timer; +static lv_ll_t glfw_window_ll; /********************** * MACROS @@ -79,301 +67,325 @@ static lv_timer_t * event_handler_timer; * GLOBAL FUNCTIONS **********************/ -lv_display_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res) +lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev) { - if(!inited) { - update_handler_timer = lv_timer_create(window_update_handler, 5, NULL); - event_handler_timer = lv_timer_create(window_event_handler, 5, NULL); - - lv_tick_set_cb(lv_glfw_tick_count_callback); - - inited = true; - } - - lv_glfw_window_t * dsc = lv_malloc_zeroed(sizeof(lv_glfw_window_t)); - LV_ASSERT_MALLOC(dsc); - if(dsc == NULL) return NULL; - - lv_display_t * disp = lv_display_create(hor_res, ver_res); - if(disp == NULL) { - lv_free(dsc); + if(lv_glfw_init() != 0) { return NULL; } - lv_display_add_event_cb(disp, release_disp_cb, LV_EVENT_DELETE, disp); + lv_glfw_window_t * window = lv_ll_ins_tail(&glfw_window_ll); + LV_ASSERT_MALLOC(window); + if(window == NULL) return NULL; + lv_memzero(window, sizeof(*window)); - lv_display_set_driver_data(disp, dsc); - - int ret = window_create(disp); - if(ret != 0) { - lv_display_send_event(disp, LV_EVENT_DELETE, NULL); + /* Create window with graphics context */ + lv_glfw_window_t * existing_window = lv_ll_get_head(&glfw_window_ll); + window->window = glfwCreateWindow(hor_res, ver_res, "LVGL Simulator", NULL, + existing_window ? existing_window->window : NULL); + if(window->window == NULL) { + LV_LOG_ERROR("glfwCreateWindow fail."); + lv_ll_remove(&glfw_window_ll, window); + lv_free(window); return NULL; } - lv_display_set_flush_cb(disp, flush_cb); - uint32_t stride = lv_draw_buf_width_to_stride(lv_display_get_horizontal_resolution(disp), - lv_display_get_color_format(disp)); - lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * disp->ver_res, - LV_DISPLAY_RENDER_MODE_DIRECT); - lv_display_add_event_cb(disp, res_chg_event_cb, LV_EVENT_RESOLUTION_CHANGED, NULL); + window->hor_res = hor_res; + window->ver_res = ver_res; + lv_ll_init(&window->textures, sizeof(lv_glfw_texture_t)); + window->use_indev = use_mouse_indev; - lv_opengles_init(dsc->fb1, hor_res, ver_res); + glfwSetWindowUserPointer(window->window, window); + lv_glfw_timer_init(); + lv_glfw_window_config(window->window, use_mouse_indev); + lv_glew_init(); + glfwMakeContextCurrent(window->window); + lv_opengles_init(); - return disp; + return window; +} + +void lv_glfw_window_delete(lv_glfw_window_t * window) +{ + glfwDestroyWindow(window->window); + if(window->use_indev) { + lv_glfw_texture_t * texture; + LV_LL_READ(&window->textures, texture) { + lv_indev_delete(texture->indev); + } + } + lv_ll_clear(&window->textures); + lv_ll_remove(&glfw_window_ll, window); + lv_free(window); + + if(lv_ll_is_empty(&glfw_window_ll)) { + lv_glfw_window_quit(); + } +} + +lv_glfw_texture_t * lv_glfw_window_add_texture(lv_glfw_window_t * window, unsigned int texture_id, int32_t w, int32_t h) +{ + lv_glfw_texture_t * texture = lv_ll_ins_tail(&window->textures); + LV_ASSERT_MALLOC(texture); + if(texture == NULL) return NULL; + lv_memzero(texture, sizeof(*texture)); + texture->window = window; + texture->texture_id = texture_id; + lv_area_set(&texture->area, 0, 0, w - 1, h - 1); + texture->opa = LV_OPA_COVER; + + if(window->use_indev) { + lv_display_t * texture_disp = lv_opengles_texture_get_from_texture_id(texture_id); + if(texture_disp != NULL) { + lv_indev_t * indev = lv_indev_create(); + if(indev == NULL) { + lv_ll_remove(&window->textures, texture); + lv_free(texture); + return NULL; + } + texture->indev = indev; + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, indev_read_cb); + lv_indev_set_driver_data(indev, texture); + lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT); + lv_indev_set_display(indev, texture_disp); + } + } + + return texture; +} + +void lv_glfw_texture_remove(lv_glfw_texture_t * texture) +{ + if(texture->indev != NULL) { + lv_indev_delete(texture->indev); + } + lv_ll_remove(&texture->window->textures, texture); + lv_free(texture); +} + +void lv_glfw_texture_set_x(lv_glfw_texture_t * texture, int32_t x) +{ + lv_area_set_pos(&texture->area, x, texture->area.y1); +} + +void lv_glfw_texture_set_y(lv_glfw_texture_t * texture, int32_t y) +{ + lv_area_set_pos(&texture->area, texture->area.x1, y); +} + +void lv_glfw_texture_set_opa(lv_glfw_texture_t * texture, lv_opa_t opa) +{ + texture->opa = opa; +} + +lv_indev_t * lv_glfw_texture_get_mouse_indev(lv_glfw_texture_t * texture) +{ + return texture->indev; } /********************** * STATIC FUNCTIONS **********************/ -static void lv_glfw_window_quit(lv_display_t * disp) +static int lv_glfw_init(void) { - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - if(inited) { - lv_timer_delete(update_handler_timer); - update_handler_timer = NULL; + if(glfw_inited) { + return 0; + } - glfwDestroyWindow(dsc->window); - glfwTerminate(); + glfwSetErrorCallback(glfw_error_cb); - inited = false; + int ret = glfwInit(); + if(ret == 0) { + LV_LOG_ERROR("glfwInit fail."); + return 1; + } + + lv_ll_init(&glfw_window_ll, sizeof(lv_glfw_window_t)); + + glfw_inited = true; + return 0; +} + +static int lv_glew_init(void) +{ + if(glew_inited) { + return 0; + } + + GLenum ret = glewInit(); + if(ret != GLEW_OK) { + LV_LOG_ERROR("glewInit fail: %d.", ret); + return ret; + } + + LV_LOG_INFO("GL version: %s", glGetString(GL_VERSION)); + LV_LOG_INFO("GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); + + glew_inited = true; + + return 0; +} + +static void lv_glfw_timer_init(void) +{ + if(update_handler_timer == NULL) { + update_handler_timer = lv_timer_create(window_update_handler, LV_DEF_REFR_PERIOD, NULL); + lv_tick_set_cb(lv_glfw_tick_count_callback); } } -static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) +static void lv_glfw_window_config(GLFWwindow * window, bool use_mouse_indev) { - LV_UNUSED(area); - LV_UNUSED(px_map); - if(lv_display_flush_is_last(disp)) { - window_update(disp); + glfwMakeContextCurrent(window); + + glfwSwapInterval(1); + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + if(use_mouse_indev) { + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, mouse_move_callback); } - /*IMPORTANT! It must be called to tell the system the flush is ready*/ - lv_display_flush_ready(disp); + glfwSetKeyCallback(window, key_callback); + + glfwSetWindowCloseCallback(window, window_close_callback); } -/** - * Handler for glfw events - */ -static void window_event_handler(lv_timer_t * t) +static void lv_glfw_window_quit(void) { - LV_UNUSED(t); - if(deiniting == false) { - return; - } + lv_timer_delete(update_handler_timer); + update_handler_timer = NULL; - lv_display_t * disp = lv_display_get_default(); - if(disp == NULL) { - return; - } - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - if(dsc == NULL) { - return; - } + glfwTerminate(); + glfw_inited = false; - glfwSetWindowShouldClose(dsc->window, GLFW_TRUE); - lv_display_send_event(disp, LV_EVENT_DELETE, NULL); + lv_deinit(); + + exit(0); } -/** - * Handler to update texture - */ static void window_update_handler(lv_timer_t * t) { LV_UNUSED(t); - lv_display_t * disp = lv_display_get_default(); - if(disp == NULL) { - return; - } - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - if(dsc == NULL) { - return; + + lv_glfw_window_t * window; + + glfwPollEvents(); + + /* delete windows that are ready to close */ + window = lv_ll_get_head(&glfw_window_ll); + while(window) { + lv_glfw_window_t * window_to_delete = window->closing ? window : NULL; + window = lv_ll_get_next(&glfw_window_ll, window); + if(window_to_delete) { + glfwSetWindowShouldClose(window_to_delete->window, GLFW_TRUE); + lv_glfw_window_delete(window_to_delete); + } } - if(!glfwWindowShouldClose(dsc->window)) { - lv_opengles_update(dsc->fb1, disp->hor_res, disp->ver_res); + /* render each window */ + LV_LL_READ(&glfw_window_ll, window) { + glfwMakeContextCurrent(window->window); + lv_opengles_viewport(0, 0, window->hor_res, window->ver_res); + lv_opengles_render_clear(); + + /* render each texture in the window */ + lv_glfw_texture_t * texture; + LV_LL_READ(&window->textures, texture) { + /* if the added texture is an LVGL opengles texture display, refresh it before rendering it */ + lv_display_t * texture_disp = lv_opengles_texture_get_from_texture_id(texture->texture_id); + if(texture_disp != NULL) { + lv_refr_now(texture_disp); + } + + lv_opengles_render_texture(texture->texture_id, &texture->area, texture->opa, window->hor_res, window->ver_res); + } /* Swap front and back buffers */ - glfwSwapBuffers(dsc->window); - - glfwPollEvents(); + glfwSwapBuffers(window->window); } } static void glfw_error_cb(int error, const char * description) { - fprintf(stderr, "GLFW Error %d: %s\n", error, description); + LV_LOG_ERROR("GLFW Error %d: %s", error, description); +} + +static lv_glfw_window_t * lv_glfw_get_lv_window_from_window(GLFWwindow * window) +{ + return glfwGetWindowUserPointer(window); } static void window_close_callback(GLFWwindow * window) { - LV_UNUSED(window); - deiniting = true; + lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->closing = 1; } static void key_callback(GLFWwindow * window, int key, int scancode, int action, int mods) { - LV_UNUSED(window); LV_UNUSED(scancode); LV_UNUSED(mods); if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - deiniting = true; + lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->closing = 1; } } static void mouse_button_callback(GLFWwindow * window, int button, int action, int mods) { - LV_UNUSED(window); LV_UNUSED(mods); if(button == GLFW_MOUSE_BUTTON_LEFT) { - lv_display_t * disp = lv_display_get_default(); - if(action == GLFW_PRESS) { - lv_glfw_mouse_btn_handler(disp, 1); - } - else if(action == GLFW_RELEASE) { - lv_glfw_mouse_btn_handler(disp, 0); - } + lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->mouse_last_state = action == GLFW_PRESS ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + proc_mouse(lv_window); } } static void mouse_move_callback(GLFWwindow * window, double xpos, double ypos) { - LV_UNUSED(window); - lv_display_t * disp = lv_display_get_default(); - lv_glfw_mouse_move_handler(disp, (int)xpos, (int)ypos); + lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->mouse_last_point.x = (int32_t)xpos; + lv_window->mouse_last_point.y = (int32_t)ypos; + proc_mouse(lv_window); +} + +static void proc_mouse(lv_glfw_window_t * window) +{ + /* mouse activity will affect the topmost LVGL display texture */ + lv_glfw_texture_t * texture; + LV_LL_READ_BACK(&window->textures, texture) { + if(lv_area_is_point_on(&texture->area, &window->mouse_last_point, 0)) { + /* adjust the mouse pointer coordinates so that they are relative to the texture */ + texture->indev_last_point.x = window->mouse_last_point.x - texture->area.x1; + texture->indev_last_point.y = window->mouse_last_point.y - texture->area.y1; + texture->indev_last_state = window->mouse_last_state; + lv_indev_read(texture->indev); + break; + } + } +} + +static void indev_read_cb(lv_indev_t * indev, lv_indev_data_t * data) +{ + lv_glfw_texture_t * texture = lv_indev_get_driver_data(indev); + data->point = texture->indev_last_point; + data->state = texture->indev_last_state; } static void framebuffer_size_callback(GLFWwindow * window, int width, int height) { - LV_UNUSED(window); - glViewport(0, 0, width, height); - lv_display_t * disp = lv_display_get_default(); - if(disp == NULL) { - return; - } - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - dsc->ignore_size_chg = 1; - lv_display_set_resolution(disp, width / dsc->zoom, height / dsc->zoom); - dsc->ignore_size_chg = 0; - lv_refr_now(disp); -} - -static void texture_resize(lv_display_t * disp) -{ - int32_t hor_res = lv_display_get_horizontal_resolution(disp); - int32_t ver_res = lv_display_get_vertical_resolution(disp); - uint32_t stride = lv_draw_buf_width_to_stride(hor_res, lv_display_get_color_format(disp)); - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - - dsc->fb1 = realloc(dsc->fb1, stride * ver_res); - lv_memzero(dsc->fb1, stride * ver_res); - - lv_display_set_buffers(disp, dsc->fb1, dsc->fb2, stride * ver_res, LV_DISPLAY_RENDER_MODE_DIRECT); -} - -static int window_create(lv_display_t * disp) -{ - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - dsc->zoom = 1; - - glfwSetErrorCallback(glfw_error_cb); - if(!glfwInit()) { - LV_LOG_ERROR("glfwInit fail.\n"); - return 1; - } - - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - - int32_t hor_res = disp->hor_res; - int32_t ver_res = disp->ver_res; - - /* Create window with graphics context */ - dsc->window = glfwCreateWindow(hor_res * dsc->zoom, ver_res * dsc->zoom, "LVGL Simulator", NULL, NULL); - if(dsc->window == NULL) { - LV_LOG_ERROR("glfwCreateWindow fail.\n"); - return 2; - } - - glfwMakeContextCurrent(dsc->window); - if(glewInit() != GLEW_OK) { - LV_LOG_ERROR("glewInit fail.\n"); - return 3; - } - - LV_LOG_INFO("GL version: %s\n", glGetString(GL_VERSION)); - LV_LOG_INFO("GLSL version: %s\n", glGetString(GL_SHADING_LANGUAGE_VERSION)); - - glfwSwapInterval(1); - - glfwSetFramebufferSizeCallback(dsc->window, framebuffer_size_callback); - - glfwSetMouseButtonCallback(dsc->window, mouse_button_callback); - glfwSetCursorPosCallback(dsc->window, mouse_move_callback); - - glfwSetKeyCallback(dsc->window, key_callback); - - glfwSetWindowCloseCallback(dsc->window, window_close_callback); - - texture_resize(disp); - - uint32_t px_size = lv_color_format_get_size(lv_display_get_color_format(disp)); - lv_memset(dsc->fb1, 0xff, hor_res * ver_res * px_size); - - return 0; -} - -static void window_update(lv_display_t * disp) -{ - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - if(dsc->fb_act) { - LV_LOG_INFO("current pixel: %d\n", ((uint16_t *)(dsc->fb_act))[0]); - } - if(dsc->fb1) { - LV_LOG_INFO("fb1 pixel: %d\n", ((uint16_t *)(dsc->fb1))[0]); - } -} - -static void res_chg_event_cb(lv_event_t * e) -{ - lv_display_t * disp = lv_event_get_current_target(e); - texture_resize(disp); -} - -static void release_disp_cb(lv_event_t * e) -{ - lv_display_t * disp = (lv_display_t *) lv_event_get_user_data(e); - lv_glfw_window_t * dsc = lv_display_get_driver_data(disp); - - lv_glfw_window_quit(disp); - - if(dsc->fb1) { - free(dsc->fb1); - dsc->fb1 = NULL; - } - if(dsc->fb2) { - free(dsc->fb2); - dsc->fb2 = NULL; - } - if(dsc->buf1) { - free(dsc->buf1); - dsc->buf1 = NULL; - } - if(dsc->buf2) { - free(dsc->buf2); - dsc->buf2 = NULL; - } - - lv_deinit(); - lv_free(dsc); - - exit(0); + lv_glfw_window_t * lv_window = lv_glfw_get_lv_window_from_window(window); + lv_window->hor_res = width; + lv_window->ver_res = height; } static uint32_t lv_glfw_tick_count_callback(void) { - int milliseconds = (int)(glfwGetTime() * 1000); - return milliseconds; + double tick = glfwGetTime() * 1000.0; + return (uint32_t)tick; } #endif /*LV_USE_OPENGLES*/ diff --git a/src/drivers/glfw/lv_glfw_window.h b/src/drivers/glfw/lv_glfw_window.h index 000d1d618..e8e516101 100644 --- a/src/drivers/glfw/lv_glfw_window.h +++ b/src/drivers/glfw/lv_glfw_window.h @@ -14,11 +14,12 @@ extern "C" { * INCLUDES *********************/ -#include "../../display/lv_display.h" -#include "../../indev/lv_indev.h" - +#include "../../lv_conf_internal.h" #if LV_USE_OPENGLES +#include "../../misc/lv_types.h" +#include "../../display/lv_display.h" + /********************* * DEFINES *********************/ @@ -31,7 +32,68 @@ extern "C" { * GLOBAL PROTOTYPES **********************/ -lv_display_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res); +/** + * Create a GLFW window with no textures and initialize OpenGL + * @param hor_res width in pixels of the window + * @param ver_res height in pixels of the window + * @param use_mouse_indev send pointer indev input to LVGL display textures + * @return the new GLFW window handle + */ +lv_glfw_window_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res, bool use_mouse_indev); + +/** + * Delete a GLFW window. If it is the last one, the process will exit + * @param window GLFW window to delete + */ +void lv_glfw_window_delete(lv_glfw_window_t * window); + +/** + * Add a texture to the GLFW window. It can be an LVGL display texture, or any OpenGL texture + * @param window GLFW window + * @param texture_id OpenGL texture ID + * @param w width in pixels of the texture + * @param h height in pixels of the texture + * @return the new texture handle + */ +lv_glfw_texture_t * lv_glfw_window_add_texture(lv_glfw_window_t * window, unsigned int texture_id, int32_t w, + int32_t h); + +/** + * Remove a texture from its GLFW window and delete it + * @param texture handle of a GLFW window texture + */ +void lv_glfw_texture_remove(lv_glfw_texture_t * texture); + +/** + * Set the x position of a texture within its GLFW window + * @param texture handle of a GLFW window texture + * @param x new x position of the texture + */ +void lv_glfw_texture_set_x(lv_glfw_texture_t * texture, int32_t x); + +/** + * Set the y position of a texture within its GLFW window + * @param texture handle of a GLFW window texture + * @param y new y position of the texture + */ +void lv_glfw_texture_set_y(lv_glfw_texture_t * texture, int32_t y); + +/** + * Set the opacity of a texture in a GLFW window + * @param texture handle of a GLFW window texture + * @param opa new opacity of the texture + */ +void lv_glfw_texture_set_opa(lv_glfw_texture_t * texture, lv_opa_t opa); + +/** + * Get the mouse indev associated with a texture in a GLFW window, if it exists + * @param texture handle of a GLFW window texture + * @return the indev or `NULL` + * @note there will only be an indev if the texture is based on an + * LVGL display texture and the window was created with + * `use_mouse_indev` as `true` + */ +lv_indev_t * lv_glfw_texture_get_mouse_indev(lv_glfw_texture_t * texture); /********************** * MACROS diff --git a/src/drivers/glfw/lv_glfw_window_private.h b/src/drivers/glfw/lv_glfw_window_private.h new file mode 100644 index 000000000..a8149c292 --- /dev/null +++ b/src/drivers/glfw/lv_glfw_window_private.h @@ -0,0 +1,70 @@ +/** + * @file lv_glfw_window_private.h + * + */ + +#ifndef LV_GLFW_WINDOW_PRIVATE_H +#define LV_GLFW_WINDOW_PRIVATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "lv_glfw_window.h" +#if LV_USE_OPENGLES + +#include +#include + +#include "../../misc/lv_area.h" +#include "../../display/lv_display.h" +#include "../../indev/lv_indev.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct lv_glfw_window_t { + GLFWwindow * window; + int32_t hor_res; + int32_t ver_res; + lv_ll_t textures; + lv_point_t mouse_last_point; + lv_indev_state_t mouse_last_state; + uint8_t use_indev : 1; + uint8_t closing : 1; +}; + +struct lv_glfw_texture_t { + lv_glfw_window_t * window; + unsigned int texture_id; + lv_area_t area; + lv_opa_t opa; + lv_indev_t * indev; + lv_point_t indev_last_point; + lv_indev_state_t indev_last_state; +}; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_OPENGLES*/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_GLFW_WINDOW_PRIVATE_H*/ diff --git a/src/drivers/glfw/lv_opengles_debug.c b/src/drivers/glfw/lv_opengles_debug.c index 3e52e3125..5bf6d0227 100644 --- a/src/drivers/glfw/lv_opengles_debug.c +++ b/src/drivers/glfw/lv_opengles_debug.c @@ -6,11 +6,12 @@ /********************* * INCLUDES *********************/ -#include "../../display/lv_display.h" -#include "lv_opengles_debug.h" +#include "lv_opengles_debug.h" #if LV_USE_OPENGLES +#include "../../misc/lv_log.h" + /********************* * DEFINES *********************/ @@ -23,10 +24,6 @@ * STATIC PROTOTYPES **********************/ -/*********************** - * GLOBAL PROTOTYPES - ***********************/ - /********************** * STATIC VARIABLES **********************/ @@ -48,10 +45,14 @@ bool GLLogCall(const char * function, const char * file, int line) { GLenum error; while((error = glGetError()) != GL_NO_ERROR) { - LV_LOG_ERROR("[OpenGL Error] (%d) %s %s:%d\n", error, function, file, line); + LV_LOG_ERROR("[OpenGL Error] (%d) %s %s:%d", error, function, file, line); return false; } return true; } +/********************** + * STATIC FUNCTIONS + **********************/ + #endif /* LV_USE_OPENGLES */ diff --git a/src/drivers/glfw/lv_opengles_debug.h b/src/drivers/glfw/lv_opengles_debug.h index 2d1fa31ac..ddde0eb53 100644 --- a/src/drivers/glfw/lv_opengles_debug.h +++ b/src/drivers/glfw/lv_opengles_debug.h @@ -10,10 +10,10 @@ extern "C" { #endif -#include - +#include "../../lv_conf_internal.h" #if LV_USE_OPENGLES +#include #include #include diff --git a/src/drivers/glfw/lv_opengles_driver.c b/src/drivers/glfw/lv_opengles_driver.c index cb8901070..110e7140f 100644 --- a/src/drivers/glfw/lv_opengles_driver.c +++ b/src/drivers/glfw/lv_opengles_driver.c @@ -10,10 +10,6 @@ #if LV_USE_OPENGLES -#include -#include -#include -#include #include "lv_opengles_debug.h" #include "lv_opengles_driver.h" @@ -28,6 +24,7 @@ /********************** * STATIC PROTOTYPES **********************/ +static void lv_opengles_enable_blending(void); static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size); static void lv_opengles_vertex_buffer_deinit(void); static void lv_opengles_vertex_buffer_bind(void); @@ -42,7 +39,6 @@ static void lv_opengles_index_buffer_deinit(void); static unsigned int lv_opengles_index_buffer_get_count(void); static void lv_opengles_index_buffer_bind(void); static void lv_opengles_index_buffer_unbind(void); -static void lv_opengles_render_clear(void); static unsigned int lv_opengles_shader_compile(unsigned int type, const char * source); static unsigned int lv_opengles_shader_create(const char * vertexShader, const char * fragmentShader); static void lv_opengles_shader_init(void); @@ -51,12 +47,9 @@ static void lv_opengles_shader_bind(void); static void lv_opengles_shader_unbind(void); static int lv_opengles_shader_get_uniform_location(const char * name); static void lv_opengles_shader_set_uniform1i(const char * name, int value); -static void lv_opengles_shader_set_uniform4f(const char * name, float v0, float v1, float v2, float v3); +static void lv_opengles_shader_set_uniformmatrix3fv(const char * name, int count, bool transpose, const float * values); +static void lv_opengles_shader_set_uniform1f(const char * name, float value); static void lv_opengles_render_draw(void); -static void lv_opengles_texture_init(void * buffer, int width, int height); -static void lv_opengles_texture_deinit(void); -static void lv_opengles_texture_bind(unsigned int slot); -static void lv_opengles_texture_update(void * buffer, int width, int height); /*********************** * GLOBAL PROTOTYPES @@ -65,6 +58,8 @@ static void lv_opengles_texture_update(void * buffer, int width, int height); /********************** * STATIC VARIABLES **********************/ +static bool is_init; + static unsigned int vertex_buffer_id = 0; static unsigned int vertex_array_id = 0; @@ -72,12 +67,10 @@ static unsigned int vertex_array_id = 0; static unsigned int index_buffer_id = 0; static unsigned int index_buffer_count = 0; -static unsigned int texture_id = 0; - static unsigned int shader_id; -static const char * shader_names[] = { "u_Color", "u_Texture", "u_ColorDepth" }; -static int shader_location[] = { 0, 0, 0 }; +static const char * shader_names[] = { "u_Texture", "u_ColorDepth", "u_VertexTransform", "u_Opa" }; +static int shader_location[] = { 0, 0, 0, 0 }; static const char * vertex_shader = "#version 300 es\n" @@ -87,9 +80,11 @@ static const char * vertex_shader = "\n" "out vec2 v_TexCoord;\n" "\n" + "uniform mat3 u_VertexTransform;\n" + "\n" "void main()\n" "{\n" - " gl_Position = position;\n" + " gl_Position = vec4((u_VertexTransform * vec3(position.xy, 1)).xy, position.zw);\n" " v_TexCoord = texCoord;\n" "};\n"; @@ -102,18 +97,18 @@ static const char * fragment_shader = "\n" "in vec2 v_TexCoord;\n" "\n" - "uniform vec4 u_Color;\n" "uniform sampler2D u_Texture;\n" "uniform int u_ColorDepth;\n" + "uniform float u_Opa;\n" "\n" "void main()\n" "{\n" " vec4 texColor = texture(u_Texture, v_TexCoord);\n" " if (u_ColorDepth == 8) {\n" " float gray = texColor.r;\n" - " color = vec4(gray, gray, gray, 1.0);\n" + " color = vec4(gray, gray, gray, u_Opa);\n" " } else {\n" - " color = texColor;\n" + " color = vec4(texColor.rgb, texColor.a * u_Opa);\n" " }\n" "};\n"; @@ -125,8 +120,12 @@ static const char * fragment_shader = * GLOBAL FUNCTIONS **********************/ -void lv_opengles_init(uint8_t * frame_buffer, int32_t hor, int32_t ver) +void lv_opengles_init(void) { + if(is_init) return; + + lv_opengles_enable_blending(); + float positions[] = { -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, @@ -139,7 +138,7 @@ void lv_opengles_init(uint8_t * frame_buffer, int32_t hor, int32_t ver) 2, 3, 0 }; - lv_opengles_vertex_buffer_init(positions, 4 * 4 * sizeof(float)); + lv_opengles_vertex_buffer_init(positions, sizeof(positions)); lv_opengles_vertex_array_init(); lv_opengles_vertex_array_add_buffer(); @@ -148,46 +147,72 @@ void lv_opengles_init(uint8_t * frame_buffer, int32_t hor, int32_t ver) lv_opengles_shader_init(); lv_opengles_shader_bind(); - lv_opengles_shader_set_uniform1i("u_ColorDepth", LV_COLOR_DEPTH); - lv_opengles_shader_set_uniform4f("u_Color", 0.8f, 0.3f, 0.8f, 1.0f); - int slot = 0; - lv_opengles_texture_init(frame_buffer, hor, ver); - lv_opengles_texture_bind(slot); - - lv_opengles_shader_set_uniform1i("u_Texture", slot); - - /* unbound everything */ + /* unbind everything */ lv_opengles_vertex_array_unbind(); lv_opengles_vertex_buffer_unbind(); lv_opengles_index_buffer_unbind(); lv_opengles_shader_unbind(); + + is_init = true; } void lv_opengles_deinit(void) { - lv_opengles_texture_deinit(); + if(!is_init) return; + lv_opengles_shader_deinit(); lv_opengles_index_buffer_deinit(); lv_opengles_vertex_buffer_deinit(); lv_opengles_vertex_array_deinit(); + + is_init = false; } -void lv_opengles_update(uint8_t * frame_buffer, int32_t hor, int32_t ver) +void lv_opengles_render_texture(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, int32_t disp_w, + int32_t disp_h) { - lv_opengles_render_clear(); - lv_opengles_texture_update(frame_buffer, hor, ver); + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, texture)); + + float hor_scale = (float)lv_area_get_width(texture_area) / (float)disp_w; + float ver_scale = (float)lv_area_get_height(texture_area) / (float)disp_h; + float hor_translate = (float)texture_area->x1 / (float)disp_w * 2.0f - (1.0f - hor_scale); + float ver_translate = -((float)texture_area->y1 / (float)disp_h * 2.0f - (1.0f - ver_scale)); + float matrix[9] = { + hor_scale, 0.0f, hor_translate, + 0.0f, ver_scale, ver_translate, + 0.0f, 0.0f, 1.0f + }; lv_opengles_shader_bind(); lv_opengles_shader_set_uniform1i("u_ColorDepth", LV_COLOR_DEPTH); - lv_opengles_shader_set_uniform4f("u_Color", 0.0f, 0.3f, 0.8f, 1.0f); + lv_opengles_shader_set_uniform1i("u_Texture", 0); + lv_opengles_shader_set_uniformmatrix3fv("u_VertexTransform", 1, true, matrix); + lv_opengles_shader_set_uniform1f("u_Opa", (float)opa / (float)LV_OPA_100); lv_opengles_render_draw(); } +void lv_opengles_render_clear(void) +{ + GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); +} + +void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h) +{ + glViewport(x, y, w, h); +} + /********************** * STATIC FUNCTIONS **********************/ +static void lv_opengles_enable_blending(void) +{ + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size) { GL_CALL(glGenBuffers(1, &vertex_buffer_id)); @@ -195,42 +220,42 @@ static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size) GL_CALL(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW)); } -static void lv_opengles_vertex_buffer_deinit() +static void lv_opengles_vertex_buffer_deinit(void) { GL_CALL(glDeleteBuffers(1, &vertex_buffer_id)); } -static void lv_opengles_vertex_buffer_bind() +static void lv_opengles_vertex_buffer_bind(void) { GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id)); } -static void lv_opengles_vertex_buffer_unbind() +static void lv_opengles_vertex_buffer_unbind(void) { GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); } -static void lv_opengles_vertex_array_init() +static void lv_opengles_vertex_array_init(void) { GL_CALL(glGenVertexArrays(1, &vertex_array_id)); } -static void lv_opengles_vertex_array_deinit() +static void lv_opengles_vertex_array_deinit(void) { GL_CALL(glDeleteVertexArrays(1, &vertex_array_id)); } -static void lv_opengles_vertex_array_bind() +static void lv_opengles_vertex_array_bind(void) { GL_CALL(glBindVertexArray(vertex_array_id)); } -static void lv_opengles_vertex_array_unbind() +static void lv_opengles_vertex_array_unbind(void) { GL_CALL(glBindVertexArray(0)); } -static void lv_opengles_vertex_array_add_buffer() +static void lv_opengles_vertex_array_add_buffer(void) { lv_opengles_vertex_buffer_bind(); intptr_t offset = 0; @@ -253,31 +278,26 @@ static void lv_opengles_index_buffer_init(const unsigned int * data, unsigned in GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(GLuint), data, GL_STATIC_DRAW)); } -static void lv_opengles_index_buffer_deinit() +static void lv_opengles_index_buffer_deinit(void) { GL_CALL(glDeleteBuffers(1, &index_buffer_id)); } -static unsigned int lv_opengles_index_buffer_get_count() +static unsigned int lv_opengles_index_buffer_get_count(void) { return index_buffer_count; } -static void lv_opengles_index_buffer_bind() +static void lv_opengles_index_buffer_bind(void) { GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id)); } -static void lv_opengles_index_buffer_unbind() +static void lv_opengles_index_buffer_unbind(void) { GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); } -static void lv_opengles_render_clear() -{ - GL_CALL(glClear(GL_COLOR_BUFFER_BIT)); -} - static unsigned int lv_opengles_shader_compile(unsigned int type, const char * source) { GL_CALL(unsigned int id = glCreateShader(type)); @@ -292,8 +312,8 @@ static unsigned int lv_opengles_shader_compile(unsigned int type, const char * s GL_CALL(glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length)); char * message = lv_malloc_zeroed(length * sizeof(char)); GL_CALL(glGetShaderInfoLog(id, length, &length, message)); - LV_LOG_ERROR("Failed to compile %s shader!\n", type == GL_VERTEX_SHADER ? "vertex" : "fragment"); - LV_LOG_ERROR("%s\n", message); + LV_LOG_ERROR("Failed to compile %s shader!", type == GL_VERTEX_SHADER ? "vertex" : "fragment"); + LV_LOG_ERROR("%s", message); GL_CALL(glDeleteShader(id)); return 0; } @@ -318,22 +338,22 @@ static unsigned int lv_opengles_shader_create(const char * vertexShader, const c return program; } -static void lv_opengles_shader_init() +static void lv_opengles_shader_init(void) { shader_id = lv_opengles_shader_create(vertex_shader, fragment_shader); } -static void lv_opengles_shader_deinit() +static void lv_opengles_shader_deinit(void) { GL_CALL(glDeleteProgram(shader_id)); } -static void lv_opengles_shader_bind() +static void lv_opengles_shader_bind(void) { GL_CALL(glUseProgram(shader_id)); } -static void lv_opengles_shader_unbind() +static void lv_opengles_shader_unbind(void) { GL_CALL(glUseProgram(0)); } @@ -342,7 +362,7 @@ static int lv_opengles_shader_get_uniform_location(const char * name) { int id = -1; for(size_t i = 0; i < sizeof(shader_location) / sizeof(int); i++) { - if(strcmp(shader_names[i], name) == 0) { + if(lv_strcmp(shader_names[i], name) == 0) { id = i; } } @@ -356,7 +376,7 @@ static int lv_opengles_shader_get_uniform_location(const char * name) GL_CALL(int location = glGetUniformLocation(shader_id, name)); if(location == -1) - LV_LOG_WARN("Warning: uniform '%s' doesn't exist!\n", name); + LV_LOG_WARN("Warning: uniform '%s' doesn't exist!", name); shader_location[id] = location; return location; @@ -367,12 +387,17 @@ static void lv_opengles_shader_set_uniform1i(const char * name, int value) GL_CALL(glUniform1i(lv_opengles_shader_get_uniform_location(name), value)); } -static void lv_opengles_shader_set_uniform4f(const char * name, float v0, float v1, float v2, float v3) +static void lv_opengles_shader_set_uniformmatrix3fv(const char * name, int count, bool transpose, const float * values) { - GL_CALL(glUniform4f(lv_opengles_shader_get_uniform_location(name), v0, v1, v2, v3)); + GL_CALL(glUniformMatrix3fv(lv_opengles_shader_get_uniform_location(name), count, transpose, values)); } -static void lv_opengles_render_draw() +static void lv_opengles_shader_set_uniform1f(const char * name, float value) +{ + GL_CALL(glUniform1f(lv_opengles_shader_get_uniform_location(name), value)); +} + +static void lv_opengles_render_draw(void) { lv_opengles_shader_bind(); lv_opengles_vertex_array_bind(); @@ -381,67 +406,4 @@ static void lv_opengles_render_draw() GL_CALL(glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, NULL)); } -static void lv_opengles_texture_init(void * buffer, int width, int height) -{ - if(buffer == NULL) { - return; - } - - GL_CALL(glGenTextures(1, &texture_id)); - GL_CALL(glBindTexture(GL_TEXTURE_2D, texture_id)); - - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - - GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - - /*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ -#if LV_COLOR_DEPTH == 8 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, buffer)); -#elif LV_COLOR_DEPTH == 16 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buffer)); -#elif LV_COLOR_DEPTH == 24 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, buffer)); -#elif LV_COLOR_DEPTH == 32 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer)); -#else -#error("Unsupported color format") -#endif - - GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); -} - -static void lv_opengles_texture_deinit() -{ - GL_CALL(glDeleteTextures(1, &texture_id)); - texture_id = 0; -} - -static void lv_opengles_texture_bind(unsigned int slot) -{ - GL_CALL(glActiveTexture(GL_TEXTURE0 + slot)); - GL_CALL(glBindTexture(GL_TEXTURE_2D, texture_id)); -} - -static void lv_opengles_texture_update(void * buffer, int width, int height) -{ - GL_CALL(glBindTexture(GL_TEXTURE_2D, texture_id)); - - GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - /*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ -#if LV_COLOR_DEPTH == 8 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, buffer)); -#elif LV_COLOR_DEPTH == 16 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, width, height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, buffer)); -#elif LV_COLOR_DEPTH == 24 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, buffer)); -#elif LV_COLOR_DEPTH == 32 - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer)); -#else -#error("Unsupported color format") -#endif -} - #endif /* LV_USE_OPENGLES */ diff --git a/src/drivers/glfw/lv_opengles_driver.h b/src/drivers/glfw/lv_opengles_driver.h index c6fe31228..e539febb9 100644 --- a/src/drivers/glfw/lv_opengles_driver.h +++ b/src/drivers/glfw/lv_opengles_driver.h @@ -10,14 +10,69 @@ extern "C" { #endif +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" #if LV_USE_OPENGLES -void lv_opengles_init(uint8_t * frame_buffer, int32_t hor, int32_t ver); +#include "../../misc/lv_area.h" +#include "../../misc/lv_color.h" -void lv_opengles_update(uint8_t * frame_buffer, int32_t hor, int32_t ver); +/********************* + * DEFINES + *********************/ +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize OpenGL + * @note it is not necessary to call this if you use `lv_glfw_window_create` + */ +void lv_opengles_init(void); + +/** + * Deinitialize OpenGL + * @note it is not necessary to call this if you use `lv_glfw_window_create` + */ void lv_opengles_deinit(void); +/** + * Render a texture + * @param texture OpenGL texture ID + * @param texture_area the area in the window to render the texture in + * @param opa opacity to blend the texture with existing contents + * @param disp_w width of the window being rendered to + * @param disp_h height of the window being rendered to + */ +void lv_opengles_render_texture(unsigned int texture, const lv_area_t * texture_area, lv_opa_t opa, int32_t disp_w, + int32_t disp_h); + +/** + * Clear the window/display + */ +void lv_opengles_render_clear(void); + +/** + * Set the OpenGL viewport + * @param x x position of the viewport + * @param y y position of the viewport + * @param w width of the viewport + * @param h height of the viewport + */ +void lv_opengles_viewport(int32_t x, int32_t y, int32_t w, int32_t h); + +/********************** + * MACROS + **********************/ + #endif /* LV_USE_OPENGLES */ #ifdef __cplusplus diff --git a/src/drivers/glfw/lv_opengles_texture.c b/src/drivers/glfw/lv_opengles_texture.c new file mode 100644 index 000000000..42e6a9f70 --- /dev/null +++ b/src/drivers/glfw/lv_opengles_texture.c @@ -0,0 +1,151 @@ +/** + * @file lv_opengles_texture.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_opengles_texture.h" +#if LV_USE_OPENGLES + +#include "lv_opengles_debug.h" +#include "../../display/lv_display_private.h" +#include +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + unsigned int texture_id; + uint8_t * fb1; +} lv_opengles_texture_t; + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); +static void release_disp_cb(lv_event_t * e); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_display_t * lv_opengles_texture_create(int32_t w, int32_t h) +{ + lv_display_t * disp = lv_display_create(w, h); + if(disp == NULL) { + return NULL; + } + lv_opengles_texture_t * dsc = lv_malloc_zeroed(sizeof(lv_opengles_texture_t)); + LV_ASSERT_MALLOC(dsc); + if(dsc == NULL) { + lv_display_delete(disp); + return NULL; + } + uint32_t stride = lv_draw_buf_width_to_stride(w, lv_display_get_color_format(disp)); + uint32_t buf_size = stride * w; + dsc->fb1 = malloc(buf_size); + if(dsc->fb1 == NULL) { + lv_free(dsc); + lv_display_delete(disp); + return NULL; + } + + GL_CALL(glGenTextures(1, &dsc->texture_id)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, dsc->texture_id)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + + lv_display_set_buffers(disp, dsc->fb1, NULL, buf_size, LV_DISPLAY_RENDER_MODE_DIRECT); + lv_display_set_flush_cb(disp, flush_cb); + lv_display_set_driver_data(disp, dsc); + lv_display_add_event_cb(disp, release_disp_cb, LV_EVENT_DELETE, disp); + + return disp; +} + +unsigned int lv_opengles_texture_get_texture_id(lv_display_t * disp) +{ + if(disp->flush_cb != flush_cb) { + return 0; + } + lv_opengles_texture_t * dsc = lv_display_get_driver_data(disp); + return dsc->texture_id; +} + +lv_display_t * lv_opengles_texture_get_from_texture_id(unsigned int texture_id) +{ + lv_display_t * disp = NULL; + while(NULL != (disp = lv_display_get_next(disp))) { + unsigned int disp_texture_id = lv_opengles_texture_get_texture_id(disp); + if(disp_texture_id == texture_id) { + return disp; + } + } + return NULL; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map) +{ + LV_UNUSED(area); + LV_UNUSED(px_map); + + if(lv_display_flush_is_last(disp)) { + + lv_opengles_texture_t * dsc = lv_display_get_driver_data(disp); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, dsc->texture_id)); + + GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); + /*Color depth: 8 (A8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888)*/ +#if LV_COLOR_DEPTH == 8 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, disp->hor_res, disp->ver_res, 0, GL_RED, GL_UNSIGNED_BYTE, dsc->fb1)); +#elif LV_COLOR_DEPTH == 16 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB565, disp->hor_res, disp->ver_res, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + dsc->fb1)); +#elif LV_COLOR_DEPTH == 24 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, disp->hor_res, disp->ver_res, 0, GL_BGR, GL_UNSIGNED_BYTE, dsc->fb1)); +#elif LV_COLOR_DEPTH == 32 + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, disp->hor_res, disp->ver_res, 0, GL_BGRA, GL_UNSIGNED_BYTE, dsc->fb1)); +#else +#error("Unsupported color format") +#endif + } + + lv_display_flush_ready(disp); +} + +static void release_disp_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_user_data(e); + lv_opengles_texture_t * dsc = lv_display_get_driver_data(disp); + free(dsc->fb1); + lv_free(dsc); +} + +#endif /*LV_USE_OPENGLES*/ diff --git a/src/drivers/glfw/lv_opengles_texture.h b/src/drivers/glfw/lv_opengles_texture.h new file mode 100644 index 000000000..0d53b2b16 --- /dev/null +++ b/src/drivers/glfw/lv_opengles_texture.h @@ -0,0 +1,66 @@ +/** + * @file lv_opengles_texture.h + * + */ + +#ifndef LV_OPENGLES_TEXTURE_H +#define LV_OPENGLES_TEXTURE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../lv_conf_internal.h" +#if LV_USE_OPENGLES + +#include "../../display/lv_display.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a display that flushes to an OpenGL texture + * @param w width in pixels of the texture + * @param h height in pixels of the texture + * @return the new display + */ +lv_display_t * lv_opengles_texture_create(int32_t w, int32_t h); + +/** + * Get the OpenGL texture ID of the display + * @param disp display + * @return texture ID + */ +unsigned int lv_opengles_texture_get_texture_id(lv_display_t * disp); + +/** + * Get the display of an OpenGL texture if it is associated with one + * @param texture_id OpenGL texture ID + * @return display or `NULL` if there no display with that texture ID + */ +lv_display_t * lv_opengles_texture_get_from_texture_id(unsigned int texture_id); + +/********************** + * MACROS + **********************/ + +#endif /* LV_USE_OPENGLES */ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_OPENGLES_TEXTURE_H*/ diff --git a/src/drivers/lv_drivers.h b/src/drivers/lv_drivers.h index 675a51f66..935292947 100644 --- a/src/drivers/lv_drivers.h +++ b/src/drivers/lv_drivers.h @@ -38,7 +38,8 @@ extern "C" { #include "windows/lv_windows_display.h" #include "glfw/lv_glfw_window.h" -#include "glfw/lv_glfw_mouse.h" +#include "glfw/lv_opengles_texture.h" +#include "glfw/lv_opengles_driver.h" #include "qnx/lv_qnx.h" diff --git a/src/misc/lv_types.h b/src/misc/lv_types.h index 159b8b73d..2b3222224 100644 --- a/src/misc/lv_types.h +++ b/src/misc/lv_types.h @@ -321,6 +321,9 @@ typedef struct lv_rlottie_t lv_rlottie_t; typedef struct lv_ffmpeg_player_t lv_ffmpeg_player_t; +typedef struct lv_glfw_window_t lv_glfw_window_t; +typedef struct lv_glfw_texture_t lv_glfw_texture_t; + typedef uint32_t lv_prop_id_t; #if LV_USE_OBJ_PROPERTY