feat(opengles): add basic driver for opengles (#6254)

Co-authored-by: Dany Liu <dany.yang.liu@email.com>
Co-authored-by: Gabor Kiss-Vamosi <kisvegabor@gmail.com>
This commit is contained in:
Dany.L
2024-06-08 04:54:52 +08:00
committed by GitHub
parent 9a7639ccea
commit d85a39e78c
20 changed files with 1396 additions and 1 deletions

View File

@@ -1646,6 +1646,15 @@ menu "LVGL configuration"
config LV_USE_WINDOWS config LV_USE_WINDOWS
bool "Use LVGL Windows backend" bool "Use LVGL Windows backend"
default n default n
config LV_USE_OPENGLES
bool "Use GLFW and OpenGL to open window on PC and handle mouse and keyboard"
default n
config LV_USE_OPENGLES_DEBUG
bool "Enable debug mode for OpenGL"
depends on LV_USE_OPENGLES
default n
endmenu endmenu
menu "Examples" menu "Examples"

View File

@@ -10,3 +10,4 @@ Drivers
libinput libinput
X11 X11
windows windows
opengles

View File

@@ -0,0 +1,65 @@
===============================
OpenGL ES Display/Inputs driver
===============================
Overview
--------
| The **OpenGL ES** display/input `driver <https://github.com/lvgl/lvgl/src/drivers/opengles>`__ offers support for simulating the LVGL display and keyboard/mouse inputs in an desktop window created via GLFW.
| It is an alternative to **Wayland**, **XCB**, **SDL** or **Qt**.
The main purpose for this driver is for testing/debugging the LVGL application in an **OpenGL** simulation window.
Prerequisites
-------------
The OpenGL driver uses GLEW GLFW to access the OpenGL window manager.
1. Install GLEW and GLFW: ``sudo apt-get install libglew-dev libglfw3-dev``
Configure OpenGL driver
-----------------------
1. Required linked libraries: -lGL -lGLEW -lglfw
2. Enable the OpenGL driver support in lv_conf.h, by cmake compiler define or by KConfig
.. code:: c
#define LV_USE_OPENGLES 1
Usage
-----
.. code:: c
#include "lvgl/lvgl.h"
#include "lvgl/examples/lv_examples.h"
#include "lvgl/demos/lv_demos.h"
int main()
{
lv_init();
lv_display_t * disp = lv_glfw_window_create(480, 272);
lv_indev_t * mouse = lv_glfw_mouse_create();
lv_indev_set_group(mouse, lv_group_get_default());
lv_indev_set_display(mouse, disp);
lv_display_set_default(disp);
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*/
lv_demo_widgets();
while (1)
{
uint32_t time_till_next = lv_timer_handler();
lv_delay_ms(time_till_next);
}
return 0;
}

View File

@@ -978,6 +978,12 @@
/* LVGL Windows backend */ /* LVGL Windows backend */
#define LV_USE_WINDOWS 0 #define LV_USE_WINDOWS 0
/* Use OpenGL to open window on PC and handle mouse and keyboard */
#define LV_USE_OPENGLES 0
#if LV_USE_OPENGLES
#define LV_USE_OPENGLES_DEBUG 1 /* Enable or disable debug for opengles */
#endif
/*================== /*==================
* EXAMPLES * EXAMPLES
*==================*/ *==================*/

View File

@@ -1,4 +1,4 @@
vcpkg install vcpkg-tool-ninja libpng freetype vcpkg install vcpkg-tool-ninja libpng freetype opengl glfw3 glew
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%
pip install pypng lz4 pip install pypng lz4
if %errorlevel% neq 0 exit /b %errorlevel% if %errorlevel% neq 0 exit /b %errorlevel%

View File

@@ -9,6 +9,7 @@ sudo dpkg --add-architecture i386
sudo apt update sudo apt update
sudo apt install gcc gcc-multilib g++-multilib ninja-build \ sudo apt install gcc gcc-multilib g++-multilib ninja-build \
libpng-dev libjpeg-turbo8-dev libfreetype6-dev \ libpng-dev libjpeg-turbo8-dev libfreetype6-dev \
libglew-dev libglfw3-dev \
libpng-dev:i386 libjpeg-dev:i386 libfreetype6-dev:i386 \ libpng-dev:i386 libjpeg-dev:i386 libfreetype6-dev:i386 \
ruby-full gcovr cmake python3 pngquant libinput-dev libxkbcommon-dev libdrm-dev pkg-config ruby-full gcovr cmake python3 pngquant libinput-dev libxkbcommon-dev libdrm-dev pkg-config
pip3 install pypng lz4 pip3 install pypng lz4

View File

@@ -0,0 +1,87 @@
/**
* @file lv_glfw_mouse.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_glfw_mouse.h"
#include "lv_glfw_mouse_private.h"
#if LV_USE_OPENGLES
#include <stdint.h>
#include <stdbool.h>
#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 */

View File

@@ -0,0 +1,43 @@
/**
* @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 */

View File

@@ -0,0 +1,89 @@
/**
* @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 <stdint.h>
#include <stdbool.h>
#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 */

View File

@@ -0,0 +1,52 @@
/**
* @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 */

View File

@@ -0,0 +1,379 @@
/**
* @file lv_glfw_window.c
*
*/
/*********************
* INCLUDES
*********************/
#include "lv_glfw_window.h"
#include "lv_glfw_mouse_private.h"
#if LV_USE_OPENGLES
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <memory.h>
#include "../../core/lv_refr.h"
#include "../../stdlib/lv_string.h"
#include "../../core/lv_global.h"
#include "../../display/lv_display_private.h"
#include "../../lv_init.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "lv_opengles_driver.h"
/*********************
* DEFINES
*********************/
/**********************
* 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 VARIABLES
**********************/
static lv_timer_t * update_handler_timer;
static lv_timer_t * event_handler_timer;
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
lv_display_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res)
{
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);
return NULL;
}
lv_display_add_event_cb(disp, release_disp_cb, LV_EVENT_DELETE, disp);
lv_display_set_driver_data(disp, dsc);
int ret = window_create(disp);
if(ret != 0) {
lv_display_send_event(disp, LV_EVENT_DELETE, NULL);
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);
lv_opengles_init(dsc->fb1, hor_res, ver_res);
return disp;
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_glfw_window_quit(lv_display_t * disp)
{
lv_glfw_window_t * dsc = lv_display_get_driver_data(disp);
if(inited) {
lv_timer_delete(update_handler_timer);
update_handler_timer = NULL;
glfwDestroyWindow(dsc->window);
glfwTerminate();
inited = false;
}
}
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)) {
window_update(disp);
}
/*IMPORTANT! It must be called to tell the system the flush is ready*/
lv_display_flush_ready(disp);
}
/**
* Handler for glfw events
*/
static void window_event_handler(lv_timer_t * t)
{
LV_UNUSED(t);
if(deiniting == false) {
return;
}
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;
}
glfwSetWindowShouldClose(dsc->window, GLFW_TRUE);
lv_display_send_event(disp, LV_EVENT_DELETE, NULL);
}
/**
* 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;
}
if(!glfwWindowShouldClose(dsc->window)) {
lv_opengles_update(dsc->fb1, disp->hor_res, disp->ver_res);
/* Swap front and back buffers */
glfwSwapBuffers(dsc->window);
glfwPollEvents();
}
}
static void glfw_error_cb(int error, const char * description)
{
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
}
static void window_close_callback(GLFWwindow * window)
{
LV_UNUSED(window);
deiniting = true;
}
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;
}
}
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);
}
}
}
static void mouse_move_cllback(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);
}
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_cllback);
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);
}
static uint32_t lv_glfw_tick_count_callback(void)
{
int milliseconds = (int)(glfwGetTime() * 1000);
return milliseconds;
}
#endif /*LV_USE_OPENGLES*/

View File

@@ -0,0 +1,46 @@
/**
* @file lv_glfw_window.h
*
*/
#ifndef LV_GLFW_WINDOW_H
#define LV_GLFW_WINDOW_H
#ifdef __cplusplus
extern "C" {
#endif
/*********************
* INCLUDES
*********************/
#include "../../display/lv_display.h"
#include "../../indev/lv_indev.h"
#if LV_USE_OPENGLES
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* GLOBAL PROTOTYPES
**********************/
lv_display_t * lv_glfw_window_create(int32_t hor_res, int32_t ver_res);
/**********************
* MACROS
**********************/
#endif /* LV_USE_OPENGLES */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_GLFW_WINDOW_H */

View File

@@ -0,0 +1,57 @@
/**
* @file lv_opengles_debug.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../display/lv_display.h"
#include "lv_opengles_debug.h"
#if LV_USE_OPENGLES
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
/***********************
* GLOBAL PROTOTYPES
***********************/
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void GLClearError()
{
while(glGetError() != GL_NO_ERROR);
}
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);
return false;
}
return true;
}
#endif /* LV_USE_OPENGLES */

View File

@@ -0,0 +1,38 @@
/**
* @file lv_opengles_debug.h
*
*/
#ifndef LV_OPENGLES_DEBUG_H
#define LV_OPENGLES_DEBUG_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#if LV_USE_OPENGLES
#include <GL/glew.h>
#include <GLFW/glfw3.h>
void GLClearError(void);
bool GLLogCall(const char * function, const char * file, int line);
#if LV_USE_OPENGLES_DEBUG
#define GL_CALL(x) GLClearError();\
x;\
GLLogCall(#x, __FILE__, __LINE__)
#else
#define GL_CALL(x) x
#endif
#endif /* LV_USE_OPENGLES */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_OPENGLES_DEBUG_H */

View File

@@ -0,0 +1,447 @@
/**
* @file lv_opengles_driver.c
*
*/
/*********************
* INCLUDES
*********************/
#include "../../display/lv_display.h"
#if LV_USE_OPENGLES
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "lv_opengles_debug.h"
#include "lv_opengles_driver.h"
/*********************
* DEFINES
*********************/
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
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);
static void lv_opengles_vertex_buffer_unbind(void);
static void lv_opengles_vertex_array_init(void);
static void lv_opengles_vertex_array_deinit(void);
static void lv_opengles_vertex_array_bind(void);
static void lv_opengles_vertex_array_unbind(void);
static void lv_opengles_vertex_array_add_buffer(void);
static void lv_opengles_index_buffer_init(const unsigned int * data, unsigned int count);
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);
static void lv_opengles_shader_deinit(void);
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_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
***********************/
/**********************
* STATIC VARIABLES
**********************/
static unsigned int vertex_buffer_id = 0;
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 * vertex_shader =
"#version 300 es\n"
"\n"
"in vec4 position;\n"
"in vec2 texCoord;\n"
"\n"
"out vec2 v_TexCoord;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = position;\n"
" v_TexCoord = texCoord;\n"
"};\n";
static const char * fragment_shader =
"#version 300 es\n"
"\n"
"precision mediump float;\n"
"\n"
"layout(location = 0) out vec4 color;\n"
"\n"
"in vec2 v_TexCoord;\n"
"\n"
"uniform vec4 u_Color;\n"
"uniform sampler2D u_Texture;\n"
"uniform int u_ColorDepth;\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"
" } else {\n"
" color = texColor;\n"
" }\n"
"};\n";
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_opengles_init(uint8_t * frame_buffer, int32_t hor, int32_t ver)
{
float positions[] = {
-1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f
};
unsigned int indices[] = {
0, 1, 2,
2, 3, 0
};
lv_opengles_vertex_buffer_init(positions, 4 * 4 * sizeof(float));
lv_opengles_vertex_array_init();
lv_opengles_vertex_array_add_buffer();
lv_opengles_index_buffer_init(indices, 6);
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 */
lv_opengles_vertex_array_unbind();
lv_opengles_vertex_buffer_unbind();
lv_opengles_index_buffer_unbind();
lv_opengles_shader_unbind();
}
void lv_opengles_deinit(void)
{
lv_opengles_texture_deinit();
lv_opengles_shader_deinit();
lv_opengles_index_buffer_deinit();
lv_opengles_vertex_buffer_deinit();
lv_opengles_vertex_array_deinit();
}
void lv_opengles_update(uint8_t * frame_buffer, int32_t hor, int32_t ver)
{
lv_opengles_render_clear();
lv_opengles_texture_update(frame_buffer, hor, ver);
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_render_draw();
}
/**********************
* STATIC FUNCTIONS
**********************/
static void lv_opengles_vertex_buffer_init(const void * data, unsigned int size)
{
GL_CALL(glGenBuffers(1, &vertex_buffer_id));
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id));
GL_CALL(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
}
static void lv_opengles_vertex_buffer_deinit()
{
GL_CALL(glDeleteBuffers(1, &vertex_buffer_id));
}
static void lv_opengles_vertex_buffer_bind()
{
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_id));
}
static void lv_opengles_vertex_buffer_unbind()
{
GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0));
}
static void lv_opengles_vertex_array_init()
{
GL_CALL(glGenVertexArrays(1, &vertex_array_id));
}
static void lv_opengles_vertex_array_deinit()
{
GL_CALL(glDeleteVertexArrays(1, &vertex_array_id));
}
static void lv_opengles_vertex_array_bind()
{
GL_CALL(glBindVertexArray(vertex_array_id));
}
static void lv_opengles_vertex_array_unbind()
{
GL_CALL(glBindVertexArray(0));
}
static void lv_opengles_vertex_array_add_buffer()
{
lv_opengles_vertex_buffer_bind();
intptr_t offset = 0;
for(unsigned int i = 0; i < 2; i++) {
lv_opengles_vertex_array_bind();
GL_CALL(glEnableVertexAttribArray(i));
GL_CALL(glVertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, 16, (const void *)offset));
offset += 2 * 4;
}
}
static void lv_opengles_index_buffer_init(const unsigned int * data, unsigned int count)
{
index_buffer_count = count;
GL_CALL(glGenBuffers(1, &index_buffer_id));
GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id));
GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(GLuint), data, GL_STATIC_DRAW));
}
static void lv_opengles_index_buffer_deinit()
{
GL_CALL(glDeleteBuffers(1, &index_buffer_id));
}
static unsigned int lv_opengles_index_buffer_get_count()
{
return index_buffer_count;
}
static void lv_opengles_index_buffer_bind()
{
GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_id));
}
static void lv_opengles_index_buffer_unbind()
{
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));
const char * src = source;
GL_CALL(glShaderSource(id, 1, &src, NULL));
GL_CALL(glCompileShader(id));
int result;
GL_CALL(glGetShaderiv(id, GL_COMPILE_STATUS, &result));
if(result == GL_FALSE) {
int length;
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);
GL_CALL(glDeleteShader(id));
return 0;
}
return id;
}
static unsigned int lv_opengles_shader_create(const char * vertexShader, const char * fragmentShader)
{
GL_CALL(unsigned int program = glCreateProgram());
unsigned int vs = lv_opengles_shader_compile(GL_VERTEX_SHADER, vertexShader);
unsigned int fs = lv_opengles_shader_compile(GL_FRAGMENT_SHADER, fragmentShader);
GL_CALL(glAttachShader(program, vs));
GL_CALL(glAttachShader(program, fs));
GL_CALL(glLinkProgram(program));
GL_CALL(glValidateProgram(program));
GL_CALL(glDeleteShader(vs));
GL_CALL(glDeleteShader(fs));
return program;
}
static void lv_opengles_shader_init()
{
shader_id = lv_opengles_shader_create(vertex_shader, fragment_shader);
}
static void lv_opengles_shader_deinit()
{
GL_CALL(glDeleteProgram(shader_id));
}
static void lv_opengles_shader_bind()
{
GL_CALL(glUseProgram(shader_id));
}
static void lv_opengles_shader_unbind()
{
GL_CALL(glUseProgram(0));
}
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) {
id = i;
}
}
if(id == -1) {
return -1;
}
if(shader_location[id] != 0) {
return shader_location[id];
}
GL_CALL(int location = glGetUniformLocation(shader_id, name));
if(location == -1)
LV_LOG_WARN("Warning: uniform '%s' doesn't exist!\n", name);
shader_location[id] = location;
return location;
}
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)
{
GL_CALL(glUniform4f(lv_opengles_shader_get_uniform_location(name), v0, v1, v2, v3));
}
static void lv_opengles_render_draw()
{
lv_opengles_shader_bind();
lv_opengles_vertex_array_bind();
lv_opengles_index_buffer_bind();
unsigned int count = lv_opengles_index_buffer_get_count();
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 */

View File

@@ -0,0 +1,27 @@
/**
* @file lv_opengles_driver.h
*
*/
#ifndef LV_OPENGLES_DRIVER_H
#define LV_OPENGLES_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif
#if LV_USE_OPENGLES
void lv_opengles_init(uint8_t * frame_buffer, int32_t hor, int32_t ver);
void lv_opengles_update(uint8_t * frame_buffer, int32_t hor, int32_t ver);
void lv_opengles_deinit(void);
#endif /* LV_USE_OPENGLES */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV_OPENGLES_DRIVER_H */

View File

@@ -37,6 +37,9 @@ extern "C" {
#include "windows/lv_windows_input.h" #include "windows/lv_windows_input.h"
#include "windows/lv_windows_display.h" #include "windows/lv_windows_display.h"
#include "glfw/lv_glfw_window.h"
#include "glfw/lv_glfw_mouse.h"
/********************* /*********************
* DEFINES * DEFINES
*********************/ *********************/

View File

@@ -3191,6 +3191,28 @@
#endif #endif
#endif #endif
/* Use OpenGL to open window on PC and handle mouse and keyboard */
#ifndef LV_USE_OPENGLES
#ifdef CONFIG_LV_USE_OPENGLES
#define LV_USE_OPENGLES CONFIG_LV_USE_OPENGLES
#else
#define LV_USE_OPENGLES 0
#endif
#endif
#if LV_USE_OPENGLES
#ifndef LV_USE_OPENGLES_DEBUG
#ifdef _LV_KCONFIG_PRESENT
#ifdef CONFIG_LV_USE_OPENGLES_DEBUG
#define LV_USE_OPENGLES_DEBUG CONFIG_LV_USE_OPENGLES_DEBUG
#else
#define LV_USE_OPENGLES_DEBUG 0
#endif
#else
#define LV_USE_OPENGLES_DEBUG 1 /* Enable or disable debug for opengles */
#endif
#endif
#endif
/*================== /*==================
* EXAMPLES * EXAMPLES
*==================*/ *==================*/

View File

@@ -336,6 +336,18 @@ if ($ENV{NON_AMD64_BUILD})
include_directories(${LIBINPUT_INCLUDE_DIRS}) include_directories(${LIBINPUT_INCLUDE_DIRS})
endif() endif()
# OpenGL ES is required for its driver
if (NOT $ENV{NON_AMD64_BUILD})
find_package(OpenGL REQUIRED)
find_package(GLEW REQUIRED)
find_package(glfw3 REQUIRED)
# Include directories
include_directories(${OPENGL_INCLUDE_DIR})
include_directories(${GLEW_INCLUDE_DIRS})
include_directories(${GLFW_INCLUDE_DIRS})
endif()
if (NOT LIBINPUT_FOUND) if (NOT LIBINPUT_FOUND)
message("libinput not found, defaulting to 0") message("libinput not found, defaulting to 0")
add_definitions(-DLV_USE_LIBINPUT=0) add_definitions(-DLV_USE_LIBINPUT=0)
@@ -423,6 +435,13 @@ foreach( test_case_fname ${TEST_CASE_FILES} )
m m
pthread pthread
${TEST_LIBS}) ${TEST_LIBS})
if (NOT $ENV{NON_AMD64_BUILD})
target_link_libraries(${test_name} PRIVATE
${OPENGL_LIBRARIES} ${GLEW_LIBRARIES} glfw)
endif()
target_include_directories(${test_name} PUBLIC ${TEST_INCLUDE_DIRS}) target_include_directories(${test_name} PUBLIC ${TEST_INCLUDE_DIRS})
target_compile_options(${test_name} PUBLIC ${LVGL_TESTFILE_COMPILE_OPTIONS}) target_compile_options(${test_name} PUBLIC ${LVGL_TESTFILE_COMPILE_OPTIONS})

View File

@@ -134,6 +134,10 @@
#define LV_LIBINPUT_XKB 1 #define LV_LIBINPUT_XKB 1
#endif #endif
#if !defined(NON_AMD64_BUILD) && !defined(_MSC_VER) && !defined(_WIN32)
#define LV_USE_OPENGLES 1
#endif
#define LV_USE_FREETYPE 1 #define LV_USE_FREETYPE 1
#define LV_FREETYPE_USE_LVGL_PORT 0 #define LV_FREETYPE_USE_LVGL_PORT 0
#define LV_FREETYPE_CACHE_FT_GLYPH_CNT 10 #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 10