merge lvgl8.3.11 and lv_drivers updates into master
commit
8b837e783c
@ -1,2 +1,3 @@
|
|||||||
**/*.o
|
**/*.o
|
||||||
**/*.d
|
**/*.d
|
||||||
|
build/*
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
prefix="@CMAKE_INSTALL_PREFIX@"
|
||||||
|
includedir="${prefix}/@INC_INSTALL_DIR@"
|
||||||
|
libdir=${prefix}/lib
|
||||||
|
|
||||||
|
Name: lv-drivers
|
||||||
|
Description: Display controller and touchpad driver that can be directly used with LVGL
|
||||||
|
URL: https://lvgl.io/
|
||||||
|
Version: 9.0.0
|
||||||
|
Cflags: -I${includedir}
|
||||||
|
Libs: -L${libdir} -llv_drivers
|
||||||
|
Requires: lvgl
|
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @file sdl_common_internal.h
|
||||||
|
* Provides SDL related functions which are only used internal.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SDL_COMMON_INTERNAL_H
|
||||||
|
#define SDL_COMMON_INTERNAL_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "sdl_common.h"
|
||||||
|
|
||||||
|
#if USE_SDL || USE_SDL_GPU
|
||||||
|
|
||||||
|
#include SDL_INCLUDE_PATH
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
int quit_filter(void * userdata, SDL_Event * event);
|
||||||
|
|
||||||
|
void mouse_handler(SDL_Event * event);
|
||||||
|
void mousewheel_handler(SDL_Event * event);
|
||||||
|
uint32_t keycode_to_ctrl_key(SDL_Keycode sdl_key);
|
||||||
|
void keyboard_handler(SDL_Event * event);
|
||||||
|
|
||||||
|
#endif /* USE_SDL || USE_SDL_GPU */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SDL_COMMON_INTERNAL_H */
|
@ -0,0 +1,652 @@
|
|||||||
|
/**
|
||||||
|
* @file smm.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#if USE_WAYLAND
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include "smm.h"
|
||||||
|
|
||||||
|
#define MAX_NAME_ATTEMPTS (5)
|
||||||
|
#define PREFER_NUM_BUFFERS (3)
|
||||||
|
|
||||||
|
#define ROUND_UP(n, b) (((((n) ? (n) : 1) + (b) - 1) / (b)) * (b))
|
||||||
|
#define LLHEAD(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *first; \
|
||||||
|
struct type *last; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LLLINK(type) \
|
||||||
|
struct { \
|
||||||
|
struct type *next; \
|
||||||
|
struct type *prev; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LL_FIRST(head) ((head)->first)
|
||||||
|
#define LL_LAST(head) ((head)->last)
|
||||||
|
#define LL_IS_EMPTY(head) (LL_FIRST(head) == NULL)
|
||||||
|
#define LL_NEXT(src, member) ((src)->member.next)
|
||||||
|
#define LL_PREV(src, member) ((src)->member.prev)
|
||||||
|
|
||||||
|
#define LL_INIT(head) do { \
|
||||||
|
(head)->first = NULL; \
|
||||||
|
(head)->last = NULL; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LL_ENQUEUE(head, src, member) do { \
|
||||||
|
(src)->member.next = NULL; \
|
||||||
|
(src)->member.prev = (head)->last; \
|
||||||
|
if ((head)->last == NULL) { \
|
||||||
|
(head)->first = (src); \
|
||||||
|
} else { \
|
||||||
|
(head)->last->member.next = (src); \
|
||||||
|
} \
|
||||||
|
(head)->last = (src); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LL_DEQUEUE(entry, head, member) do { \
|
||||||
|
(entry) = LL_FIRST(head); \
|
||||||
|
LL_REMOVE(head, entry, member); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LL_INSERT_AFTER(head, dest, src, member) do { \
|
||||||
|
(src)->member.prev = (dest); \
|
||||||
|
(src)->member.next = (dest)->member.next; \
|
||||||
|
if ((dest)->member.next != NULL) { \
|
||||||
|
(dest)->member.next->member.prev = (src); \
|
||||||
|
} else { \
|
||||||
|
(head)->last = (src); \
|
||||||
|
} \
|
||||||
|
(dest)->member.next = (src); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LL_REMOVE(head, src, member) do { \
|
||||||
|
if ((src)->member.prev != NULL) { \
|
||||||
|
(src)->member.prev->member.next = (src)->member.next; \
|
||||||
|
} else { \
|
||||||
|
(head)->first = (src)->member.next; \
|
||||||
|
} \
|
||||||
|
if ((src)->member.next != NULL) { \
|
||||||
|
(src)->member.next->member.prev = (src)->member.prev; \
|
||||||
|
} else { \
|
||||||
|
(head)->last = (src)->member.prev; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LL_FOREACH(entry, head, member) \
|
||||||
|
for ((entry) = LL_FIRST(head); \
|
||||||
|
(entry) != NULL; \
|
||||||
|
(entry) = LL_NEXT(entry, member))
|
||||||
|
|
||||||
|
struct smm_pool {
|
||||||
|
struct smm_pool_properties props;
|
||||||
|
LLHEAD(smm_buffer) allocd;
|
||||||
|
void *map;
|
||||||
|
size_t map_size;
|
||||||
|
bool map_outdated;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smm_buffer {
|
||||||
|
struct smm_buffer_properties props;
|
||||||
|
bool group_resized;
|
||||||
|
LLLINK(smm_buffer) pool;
|
||||||
|
LLLINK(smm_buffer) use;
|
||||||
|
LLLINK(smm_buffer) age;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smm_group {
|
||||||
|
struct smm_group_properties props;
|
||||||
|
size_t size;
|
||||||
|
unsigned char num_buffers;
|
||||||
|
LLHEAD(smm_buffer) unused;
|
||||||
|
LLHEAD(smm_buffer) inuse;
|
||||||
|
LLHEAD(smm_buffer) history;
|
||||||
|
LLLINK(smm_group) link;
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t calc_buffer_size(struct smm_buffer *buf);
|
||||||
|
static void purge_history(struct smm_buffer *buf);
|
||||||
|
static struct smm_buffer *get_from_pool(struct smm_group *grp);
|
||||||
|
static void return_to_pool(struct smm_buffer *buf);
|
||||||
|
static struct smm_pool *alloc_pool(void);
|
||||||
|
static void free_pool(struct smm_pool *pool);
|
||||||
|
static struct smm_buffer *alloc_buffer(struct smm_buffer *last, size_t offset);
|
||||||
|
static void free_buffer(struct smm_buffer *buf);
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
unsigned long page_sz;
|
||||||
|
struct smm_events cbs;
|
||||||
|
struct smm_pool *active;
|
||||||
|
LLHEAD(smm_group) groups;
|
||||||
|
struct {
|
||||||
|
size_t active_used;
|
||||||
|
} statistics;
|
||||||
|
} smm_instance;
|
||||||
|
|
||||||
|
|
||||||
|
void smm_init(struct smm_events *evs)
|
||||||
|
{
|
||||||
|
memcpy(&smm_instance.cbs, evs, sizeof(struct smm_events));
|
||||||
|
srand((unsigned int)clock());
|
||||||
|
smm_instance.page_sz = (unsigned long)sysconf(_SC_PAGESIZE);
|
||||||
|
LL_INIT(&smm_instance.groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void smm_deinit(void)
|
||||||
|
{
|
||||||
|
struct smm_group *grp;
|
||||||
|
|
||||||
|
/* Destroy all buffer groups */
|
||||||
|
while (!LL_IS_EMPTY(&smm_instance.groups)) {
|
||||||
|
LL_DEQUEUE(grp, &smm_instance.groups, link);
|
||||||
|
smm_destroy(grp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void smm_setctx(void *ctx)
|
||||||
|
{
|
||||||
|
smm_instance.cbs.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
smm_group_t *smm_create(void)
|
||||||
|
{
|
||||||
|
struct smm_group *grp;
|
||||||
|
|
||||||
|
/* Allocate and intialize a new buffer group */
|
||||||
|
grp = malloc(sizeof(struct smm_group));
|
||||||
|
if (grp != NULL) {
|
||||||
|
grp->size = smm_instance.page_sz;
|
||||||
|
grp->num_buffers = 0;
|
||||||
|
LL_INIT(&grp->unused);
|
||||||
|
LL_INIT(&grp->inuse);
|
||||||
|
LL_INIT(&grp->history);
|
||||||
|
|
||||||
|
/* Add to instance groups queue */
|
||||||
|
LL_ENQUEUE(&smm_instance.groups, grp, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
return grp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void smm_resize(smm_group_t *grp, size_t sz)
|
||||||
|
{
|
||||||
|
struct smm_buffer *buf;
|
||||||
|
struct smm_group *rgrp = grp;
|
||||||
|
|
||||||
|
/* Round allocation size up to a sysconf(_SC_PAGE_SIZE) boundary */
|
||||||
|
rgrp->size = ROUND_UP(sz, smm_instance.page_sz);
|
||||||
|
|
||||||
|
/* Return all unused buffers to pool (to be re-allocated at the new size) */
|
||||||
|
while (!LL_IS_EMPTY(&rgrp->unused)) {
|
||||||
|
LL_DEQUEUE(buf, &rgrp->unused, use);
|
||||||
|
return_to_pool(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark all buffers in use to be freed to pool when possible */
|
||||||
|
LL_FOREACH(buf, &rgrp->inuse, use) {
|
||||||
|
buf->group_resized = true;
|
||||||
|
purge_history(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void smm_destroy(smm_group_t *grp)
|
||||||
|
{
|
||||||
|
struct smm_buffer *buf;
|
||||||
|
struct smm_group *dgrp = grp;
|
||||||
|
|
||||||
|
/* Return unused buffers */
|
||||||
|
while (!LL_IS_EMPTY(&dgrp->unused)) {
|
||||||
|
LL_DEQUEUE(buf, &dgrp->unused, use);
|
||||||
|
return_to_pool(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return buffers that are still in use (ideally this queue should be empty
|
||||||
|
* at this time)
|
||||||
|
*/
|
||||||
|
while (!LL_IS_EMPTY(&dgrp->inuse)) {
|
||||||
|
LL_DEQUEUE(buf, &dgrp->inuse, use);
|
||||||
|
return_to_pool(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove from instance groups queue */
|
||||||
|
LL_REMOVE(&smm_instance.groups, dgrp, link);
|
||||||
|
free(dgrp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
smm_buffer_t *smm_acquire(smm_group_t *grp)
|
||||||
|
{
|
||||||
|
struct smm_buffer *buf;
|
||||||
|
struct smm_group *agrp = grp;
|
||||||
|
|
||||||
|
if (LL_IS_EMPTY(&agrp->unused)) {
|
||||||
|
/* No unused buffer available, so get a new one from pool */
|
||||||
|
buf = get_from_pool(agrp);
|
||||||
|
} else {
|
||||||
|
/* Otherwise, reuse an unused buffer */
|
||||||
|
LL_DEQUEUE(buf, &agrp->unused, use);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf != NULL) {
|
||||||
|
/* Add buffer to in-use queue */
|
||||||
|
LL_ENQUEUE(&agrp->inuse, buf, use);
|
||||||
|
|
||||||
|
/* Emit 'init buffer' event */
|
||||||
|
if (smm_instance.cbs.init_buffer != NULL) {
|
||||||
|
if (smm_instance.cbs.init_buffer(smm_instance.cbs.ctx, &buf->props)) {
|
||||||
|
smm_release(buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf != NULL) {
|
||||||
|
/* Remove from history */
|
||||||
|
purge_history(buf);
|
||||||
|
|
||||||
|
/* Add to history a-new */
|
||||||
|
LL_ENQUEUE(&agrp->history, buf, age);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *smm_map(smm_buffer_t *buf)
|
||||||
|
{
|
||||||
|
struct smm_buffer *mbuf = buf;
|
||||||
|
struct smm_pool *pool = mbuf->props.pool;
|
||||||
|
void *map = pool->map;
|
||||||
|
|
||||||
|
if (pool->map_outdated) {
|
||||||
|
/* Update mapping to current pool size */
|
||||||
|
if (pool->map != NULL) {
|
||||||
|
munmap(pool->map, pool->map_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
map = mmap(NULL,
|
||||||
|
pool->props.size,
|
||||||
|
PROT_READ | PROT_WRITE,
|
||||||
|
MAP_SHARED,
|
||||||
|
pool->props.fd,
|
||||||
|
0);
|
||||||
|
|
||||||
|
if (map == MAP_FAILED) {
|
||||||
|
map = NULL;
|
||||||
|
pool->map = NULL;
|
||||||
|
} else {
|
||||||
|
pool->map = map;
|
||||||
|
pool->map_size = pool->props.size;
|
||||||
|
pool->map_outdated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate buffer mapping (from offset in pool) */
|
||||||
|
if (map != NULL) {
|
||||||
|
map = (((char *)map) + mbuf->props.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void smm_release(smm_buffer_t *buf)
|
||||||
|
{
|
||||||
|
struct smm_buffer *rbuf = buf;
|
||||||
|
struct smm_group *grp = rbuf->props.group;
|
||||||
|
|
||||||
|
/* Remove from in-use queue */
|
||||||
|
LL_REMOVE(&grp->inuse, rbuf, use);
|
||||||
|
|
||||||
|
if (rbuf->group_resized) {
|
||||||
|
/* Buffer group was resized while this buffer was in-use, thus it must be
|
||||||
|
* returned to it's pool
|
||||||
|
*/
|
||||||
|
rbuf->group_resized = false;
|
||||||
|
return_to_pool(rbuf);
|
||||||
|
} else {
|
||||||
|
/* Move to unused queue */
|
||||||
|
LL_ENQUEUE(&grp->unused, rbuf, use);
|
||||||
|
|
||||||
|
/* Try to limit total number of buffers to preferred number */
|
||||||
|
while ((grp->num_buffers > PREFER_NUM_BUFFERS) &&
|
||||||
|
(!LL_IS_EMPTY(&grp->unused))) {
|
||||||
|
LL_DEQUEUE(rbuf, &grp->unused, use);
|
||||||
|
return_to_pool(rbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
smm_buffer_t *smm_latest(smm_group_t *grp)
|
||||||
|
{
|
||||||
|
struct smm_group *lgrp = grp;
|
||||||
|
|
||||||
|
return LL_LAST(&lgrp->history);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
smm_buffer_t *smm_next(smm_buffer_t *buf)
|
||||||
|
{
|
||||||
|
struct smm_buffer *ibuf;
|
||||||
|
struct smm_buffer *nbuf = buf;
|
||||||
|
struct smm_group *grp = nbuf->props.group;
|
||||||
|
|
||||||
|
LL_FOREACH(ibuf, &grp->history, age) {
|
||||||
|
if (ibuf == nbuf) {
|
||||||
|
ibuf = LL_NEXT(ibuf, age);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ibuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void purge_history(struct smm_buffer *buf)
|
||||||
|
{
|
||||||
|
struct smm_buffer *ibuf;
|
||||||
|
struct smm_group *grp = buf->props.group;
|
||||||
|
|
||||||
|
/* Remove from history (and any older) */
|
||||||
|
LL_FOREACH(ibuf, &grp->history, age) {
|
||||||
|
if (ibuf == buf) {
|
||||||
|
do {
|
||||||
|
LL_DEQUEUE(ibuf, &grp->history, age);
|
||||||
|
} while (ibuf != buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t calc_buffer_size(struct smm_buffer *buf)
|
||||||
|
{
|
||||||
|
size_t buf_sz;
|
||||||
|
struct smm_pool *buf_pool = buf->props.pool;
|
||||||
|
|
||||||
|
if (buf == LL_LAST(&buf_pool->allocd)) {
|
||||||
|
buf_sz = (buf_pool->props.size - buf->props.offset);
|
||||||
|
} else {
|
||||||
|
buf_sz = (LL_NEXT(buf, pool)->props.offset - buf->props.offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf_sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct smm_buffer *get_from_pool(struct smm_group *grp)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
size_t buf_sz;
|
||||||
|
struct smm_buffer *buf;
|
||||||
|
struct smm_buffer *last = NULL;
|
||||||
|
|
||||||
|
/* TODO: Determine when to allocate a new active pool (i.e. memory shrink) */
|
||||||
|
|
||||||
|
if (smm_instance.active == NULL) {
|
||||||
|
/* Allocate a new active pool */
|
||||||
|
smm_instance.active = alloc_pool();
|
||||||
|
smm_instance.statistics.active_used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smm_instance.active == NULL) {
|
||||||
|
buf = NULL;
|
||||||
|
} else {
|
||||||
|
/* Search for a free buffer large enough for allocation */
|
||||||
|
LL_FOREACH(buf, &smm_instance.active->allocd, pool) {
|
||||||
|
last = buf;
|
||||||
|
if (buf->props.group == NULL) {
|
||||||
|
buf_sz = calc_buffer_size(buf);
|
||||||
|
if (buf_sz == grp->size) {
|
||||||
|
break;
|
||||||
|
} else if (buf_sz > grp->size) {
|
||||||
|
if ((buf != LL_LAST(&smm_instance.active->allocd)) &&
|
||||||
|
(LL_NEXT(buf, pool)->props.group == NULL)) {
|
||||||
|
/* Pull back next buffer to use unallocated size */
|
||||||
|
LL_NEXT(buf, pool)->props.offset -= (buf_sz - grp->size);
|
||||||
|
} else {
|
||||||
|
/* Allocate another buffer to hold unallocated size */
|
||||||
|
alloc_buffer(buf, buf->props.offset + grp->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf == NULL) {
|
||||||
|
/* No buffer found to meet allocation size, expand pool */
|
||||||
|
if ((last != NULL) &&
|
||||||
|
(last->props.group == NULL)) {
|
||||||
|
/* Use last free buffer */
|
||||||
|
buf_sz = (grp->size - buf_sz);
|
||||||
|
} else {
|
||||||
|
/* Allocate new buffer */
|
||||||
|
buf_sz = grp->size;
|
||||||
|
if (last == NULL) {
|
||||||
|
buf = alloc_buffer(NULL, 0);
|
||||||
|
} else {
|
||||||
|
buf = alloc_buffer(last, smm_instance.active->props.size);
|
||||||
|
}
|
||||||
|
last = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last != NULL) {
|
||||||
|
/* Expand pool backing memory */
|
||||||
|
ret = ftruncate(smm_instance.active->props.fd,
|
||||||
|
smm_instance.active->props.size + buf_sz);
|
||||||
|
if (ret) {
|
||||||
|
if (buf != NULL) {
|
||||||
|
free_buffer(buf);
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
smm_instance.active->props.size += buf_sz;
|
||||||
|
smm_instance.active->map_outdated = true;
|
||||||
|
buf = last;
|
||||||
|
|
||||||
|
if (!(smm_instance.active->props.size - buf_sz)) {
|
||||||
|
/* Emit 'new pool' event */
|
||||||
|
if ((smm_instance.cbs.new_pool != NULL) &&
|
||||||
|
(smm_instance.cbs.new_pool(smm_instance.cbs.ctx,
|
||||||
|
&smm_instance.active->props))) {
|
||||||
|
free_buffer(buf);
|
||||||
|
free_pool(smm_instance.active);
|
||||||
|
smm_instance.active = NULL;
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Emit 'expand pool' event */
|
||||||
|
if (smm_instance.cbs.expand_pool != NULL) {
|
||||||
|
smm_instance.cbs.expand_pool(smm_instance.cbs.ctx,
|
||||||
|
&smm_instance.active->props);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf != NULL) {
|
||||||
|
/* Set buffer group */
|
||||||
|
memcpy((void *)&buf->props.group, &grp, sizeof(struct smm_group *));
|
||||||
|
|
||||||
|
/* Emit 'new buffer' event */
|
||||||
|
if (smm_instance.cbs.new_buffer != NULL) {
|
||||||
|
if (smm_instance.cbs.new_buffer(smm_instance.cbs.ctx, &buf->props)) {
|
||||||
|
grp = NULL;
|
||||||
|
memcpy((void *)&buf->props.group, &grp, sizeof(struct smm_group *));
|
||||||
|
buf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buf != NULL) {
|
||||||
|
/* Update active pool usage statistic */
|
||||||
|
smm_instance.statistics.active_used += grp->size;
|
||||||
|
grp->num_buffers++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void return_to_pool(struct smm_buffer *buf)
|
||||||
|
{
|
||||||
|
struct smm_group *grp = buf->props.group;
|
||||||
|
struct smm_pool *pool = buf->props.pool;
|
||||||
|
|
||||||
|
/* Emit 'free buffer' event */
|
||||||
|
if (smm_instance.cbs.free_buffer != NULL) {
|
||||||
|
smm_instance.cbs.free_buffer(smm_instance.cbs.ctx, &buf->props);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer is no longer part of history */
|
||||||
|
purge_history(buf);
|
||||||
|
|
||||||
|
/* Buffer is no longer part of group */
|
||||||
|
grp->num_buffers--;
|
||||||
|
grp = NULL;
|
||||||
|
memcpy((void *)&buf->props.group, &grp, sizeof(struct smm_group *));
|
||||||
|
|
||||||
|
/* Update active pool usage statistic */
|
||||||
|
if (smm_instance.active == pool) {
|
||||||
|
smm_instance.statistics.active_used -= calc_buffer_size(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Coalesce with ungrouped buffers beside this one */
|
||||||
|
if ((buf != LL_LAST(&pool->allocd)) &&
|
||||||
|
(LL_NEXT(buf, pool)->props.group == NULL)) {
|
||||||
|
free_buffer(LL_NEXT(buf, pool));
|
||||||
|
}
|
||||||
|
if ((buf != LL_FIRST(&pool->allocd)) &&
|
||||||
|
(LL_PREV(buf, pool)->props.group == NULL)) {
|
||||||
|
buf = LL_PREV(buf, pool);
|
||||||
|
pool = buf->props.pool;
|
||||||
|
free_buffer(LL_NEXT(buf, pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free buffer (and pool), if only remaining buffer in pool */
|
||||||
|
if ((buf == LL_FIRST(&pool->allocd)) &&
|
||||||
|
(buf == LL_LAST(&pool->allocd))) {
|
||||||
|
free_buffer(buf);
|
||||||
|
|
||||||
|
/* Emit 'free pool' event */
|
||||||
|
if (smm_instance.cbs.free_pool != NULL) {
|
||||||
|
smm_instance.cbs.free_pool(smm_instance.cbs.ctx, &pool->props);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_pool(pool);
|
||||||
|
if (smm_instance.active == pool) {
|
||||||
|
smm_instance.active = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct smm_pool *alloc_pool(void)
|
||||||
|
{
|
||||||
|
struct smm_pool *pool;
|
||||||
|
char name[] = ("/" SMM_FD_NAME "-XXXXX");
|
||||||
|
unsigned char attempts = 0;
|
||||||
|
bool opened = false;
|
||||||
|
|
||||||
|
pool = malloc(sizeof(struct smm_pool));
|
||||||
|
if (pool != NULL) {
|
||||||
|
do {
|
||||||
|
/* A randomized pool name should help reduce collisions */
|
||||||
|
sprintf(name + sizeof(SMM_FD_NAME) + 1, "%05X", rand() & 0xFFFF);
|
||||||
|
pool->props.fd = shm_open(name,
|
||||||
|
O_RDWR | O_CREAT | O_EXCL,
|
||||||
|
S_IRUSR | S_IWUSR);
|
||||||
|
if (pool->props.fd >= 0) {
|
||||||
|
shm_unlink(name);
|
||||||
|
pool->props.size = 0;
|
||||||
|
pool->map = NULL;
|
||||||
|
pool->map_size = 0;
|
||||||
|
pool->map_outdated = false;
|
||||||
|
LL_INIT(&pool->allocd);
|
||||||
|
opened = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (errno != EEXIST) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
} while (attempts < MAX_NAME_ATTEMPTS);
|
||||||
|
|
||||||
|
if (!opened) {
|
||||||
|
free(pool);
|
||||||
|
pool = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void free_pool(struct smm_pool *pool)
|
||||||
|
{
|
||||||
|
if (pool->map != NULL) {
|
||||||
|
munmap(pool->map, pool->map_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(pool->props.fd);
|
||||||
|
free(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct smm_buffer *alloc_buffer(struct smm_buffer *last, size_t offset)
|
||||||
|
{
|
||||||
|
struct smm_buffer *buf;
|
||||||
|
struct smm_buffer_properties initial_props = {
|
||||||
|
{NULL},
|
||||||
|
NULL,
|
||||||
|
smm_instance.active,
|
||||||
|
offset
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Allocate and intialize a new buffer (including linking in to pool) */
|
||||||
|
buf = malloc(sizeof(struct smm_buffer));
|
||||||
|
if (buf != NULL) {
|
||||||
|
memcpy(&buf->props, &initial_props, sizeof(struct smm_buffer_properties));
|
||||||
|
buf->group_resized = false;
|
||||||
|
|
||||||
|
if (last == NULL) {
|
||||||
|
LL_ENQUEUE(&smm_instance.active->allocd, buf, pool);
|
||||||
|
} else {
|
||||||
|
LL_INSERT_AFTER(&smm_instance.active->allocd, last, buf, pool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void free_buffer(struct smm_buffer *buf)
|
||||||
|
{
|
||||||
|
struct smm_pool *buf_pool = buf->props.pool;
|
||||||
|
|
||||||
|
/* Remove from pool */
|
||||||
|
LL_REMOVE(&buf_pool->allocd, buf, pool);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* @file smm.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef SMM_H
|
||||||
|
#define SMM_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define SMM_FD_NAME "lvgl-wayland"
|
||||||
|
#define SMM_POOL_TAGS (1)
|
||||||
|
#define SMM_BUFFER_TAGS (2)
|
||||||
|
#define SMM_GROUP_TAGS (1)
|
||||||
|
|
||||||
|
#define SMM_POOL_PROPERTIES(p) ((const struct smm_pool_properties *)(p))
|
||||||
|
#define SMM_BUFFER_PROPERTIES(b) ((const struct smm_buffer_properties *)(b))
|
||||||
|
#define SMM_GROUP_PROPERTIES(g) ((const struct smm_group_properties *)(g))
|
||||||
|
#define SMM_TAG(o, n, v) \
|
||||||
|
do { \
|
||||||
|
void **smm_tag = (void **)((char *)o + (n * sizeof(void *))); \
|
||||||
|
*smm_tag = (v); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
typedef void smm_pool_t;
|
||||||
|
typedef void smm_buffer_t;
|
||||||
|
typedef void smm_group_t;
|
||||||
|
|
||||||
|
struct smm_events {
|
||||||
|
void *ctx;
|
||||||
|
bool (*new_pool)(void *ctx, smm_pool_t *pool);
|
||||||
|
void (*expand_pool)(void *ctx, smm_pool_t *pool);
|
||||||
|
void (*free_pool)(void *ctx, smm_pool_t *pool);
|
||||||
|
bool (*new_buffer)(void *ctx, smm_buffer_t *buf);
|
||||||
|
bool (*init_buffer)(void *ctx, smm_buffer_t *buf);
|
||||||
|
void (*free_buffer)(void *ctx, smm_buffer_t *buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smm_pool_properties {
|
||||||
|
void *tag[SMM_POOL_TAGS];
|
||||||
|
size_t size;
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smm_buffer_properties {
|
||||||
|
void *tag[SMM_BUFFER_TAGS];
|
||||||
|
smm_group_t *const group;
|
||||||
|
smm_pool_t *const pool;
|
||||||
|
size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct smm_group_properties {
|
||||||
|
void *tag[SMM_GROUP_TAGS];
|
||||||
|
};
|
||||||
|
|
||||||
|
void smm_init(struct smm_events *evs);
|
||||||
|
void smm_setctx(void *ctx);
|
||||||
|
void smm_deinit(void);
|
||||||
|
smm_group_t *smm_create(void);
|
||||||
|
void smm_resize(smm_group_t *grp, size_t sz);
|
||||||
|
void smm_destroy(smm_group_t *grp);
|
||||||
|
smm_buffer_t *smm_acquire(smm_group_t *grp);
|
||||||
|
void *smm_map(smm_buffer_t *buf);
|
||||||
|
void smm_release(smm_buffer_t *buf);
|
||||||
|
smm_buffer_t *smm_latest(smm_group_t *grp);
|
||||||
|
smm_buffer_t *smm_next(smm_buffer_t *buf);
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,281 @@
|
|||||||
|
/**
|
||||||
|
* @file x11.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "x11.h"
|
||||||
|
#if USE_X11
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
#ifndef KEYBOARD_BUFFER_SIZE
|
||||||
|
#define KEYBOARD_BUFFER_SIZE 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||||
|
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||||
|
|
||||||
|
#ifndef X11_OPTIMIZED_SCREEN_UPDATE
|
||||||
|
#define X11_OPTIMIZED_SCREEN_UPDATE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
static Display* display = NULL;
|
||||||
|
static Window window = (XID)-1;
|
||||||
|
static GC gc = NULL;
|
||||||
|
static XImage* ximage = NULL;
|
||||||
|
static lv_timer_t* timer = NULL;
|
||||||
|
|
||||||
|
static char kb_buffer[KEYBOARD_BUFFER_SIZE];
|
||||||
|
static lv_point_t mouse_pos = { 0, 0 };
|
||||||
|
static bool left_mouse_btn = false;
|
||||||
|
static bool right_mouse_btn = false;
|
||||||
|
static bool wheel_mouse_btn = false;
|
||||||
|
static int16_t wheel_cnt = 0;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
static int predicate(Display* disp, XEvent* evt, XPointer arg) { return 1; }
|
||||||
|
|
||||||
|
static void x11_event_handler(lv_timer_t * t)
|
||||||
|
{
|
||||||
|
XEvent myevent;
|
||||||
|
KeySym mykey;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
/* handle all outstanding X events */
|
||||||
|
while (XCheckIfEvent(display, &myevent, predicate, NULL)) {
|
||||||
|
switch(myevent.type)
|
||||||
|
{
|
||||||
|
case Expose:
|
||||||
|
if(myevent.xexpose.count==0)
|
||||||
|
{
|
||||||
|
XPutImage(display, window, gc, ximage, 0, 0, 0, 0, LV_HOR_RES, LV_VER_RES);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MotionNotify:
|
||||||
|
mouse_pos.x = myevent.xmotion.x;
|
||||||
|
mouse_pos.y = myevent.xmotion.y;
|
||||||
|
break;
|
||||||
|
case ButtonPress:
|
||||||
|
switch (myevent.xbutton.button)
|
||||||
|
{
|
||||||
|
case Button1:
|
||||||
|
left_mouse_btn = true;
|
||||||
|
break;
|
||||||
|
case Button2:
|
||||||
|
wheel_mouse_btn = true;
|
||||||
|
break;
|
||||||
|
case Button3:
|
||||||
|
right_mouse_btn = true;
|
||||||
|
break;
|
||||||
|
case Button4:
|
||||||
|
wheel_cnt--; // Scrolled up
|
||||||
|
break;
|
||||||
|
case Button5:
|
||||||
|
wheel_cnt++; // Scrolled down
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("unhandled button press : %d", myevent.xbutton.button);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ButtonRelease:
|
||||||
|
switch (myevent.xbutton.button)
|
||||||
|
{
|
||||||
|
case Button1:
|
||||||
|
left_mouse_btn = false;
|
||||||
|
break;
|
||||||
|
case Button2:
|
||||||
|
wheel_mouse_btn = false;
|
||||||
|
break;
|
||||||
|
case Button3:
|
||||||
|
right_mouse_btn = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case KeyPress:
|
||||||
|
n = XLookupString(&myevent.xkey, &kb_buffer[0], sizeof(kb_buffer), &mykey, NULL);
|
||||||
|
kb_buffer[n] = '\0';
|
||||||
|
break;
|
||||||
|
case KeyRelease:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("unhandled x11 event: %d", myevent.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_x11_hide_cursor()
|
||||||
|
{
|
||||||
|
XColor black = { .red = 0, .green = 0, .blue = 0 };
|
||||||
|
char empty_data[] = { 0 };
|
||||||
|
|
||||||
|
Pixmap empty_bitmap = XCreateBitmapFromData(display, window, empty_data, 1, 1);
|
||||||
|
Cursor inv_cursor = XCreatePixmapCursor(display, empty_bitmap, empty_bitmap, &black, &black, 0, 0);
|
||||||
|
XDefineCursor(display, window, inv_cursor);
|
||||||
|
XFreeCursor(display, inv_cursor);
|
||||||
|
XFreePixmap(display, empty_bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
void lv_x11_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p)
|
||||||
|
{
|
||||||
|
#if X11_OPTIMIZED_SCREEN_UPDATE
|
||||||
|
static const lv_area_t inv_area = { .x1 = 0xFFFF,
|
||||||
|
.x2 = 0,
|
||||||
|
.y1 = 0xFFFF,
|
||||||
|
.y2 = 0
|
||||||
|
};
|
||||||
|
static lv_area_t upd_area = inv_area;
|
||||||
|
|
||||||
|
/* build display update area until lv_disp_flush_is_last */
|
||||||
|
upd_area.x1 = MIN(upd_area.x1, area->x1);
|
||||||
|
upd_area.x2 = MAX(upd_area.x2, area->x2);
|
||||||
|
upd_area.y1 = MIN(upd_area.y1, area->y1);
|
||||||
|
upd_area.y2 = MAX(upd_area.y2, area->y2);
|
||||||
|
#endif // X11_OPTIMIZED_SCREEN_UPDATE
|
||||||
|
|
||||||
|
for (lv_coord_t y = area->y1; y <= area->y2; y++)
|
||||||
|
{
|
||||||
|
uint32_t dst_offs = area->x1 + y * LV_HOR_RES;
|
||||||
|
uint32_t* dst_data = &((uint32_t*)ximage->data)[dst_offs];
|
||||||
|
for (lv_coord_t x = area->x1; x <= area->x2; x++, color_p++, dst_data++)
|
||||||
|
{
|
||||||
|
*dst_data = lv_color_to32(*color_p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lv_disp_flush_is_last(disp_drv))
|
||||||
|
{
|
||||||
|
#if X11_OPTIMIZED_SCREEN_UPDATE
|
||||||
|
/* refresh collected display update area only */
|
||||||
|
lv_coord_t upd_w = upd_area.x2 - upd_area.x1 + 1;
|
||||||
|
lv_coord_t upd_h = upd_area.y2 - upd_area.y1 + 1;
|
||||||
|
XPutImage(display, window, gc, ximage, upd_area.x1, upd_area.y1, upd_area.x1, upd_area.y1, upd_w, upd_h);
|
||||||
|
/* invalidate collected area */
|
||||||
|
upd_area = inv_area;
|
||||||
|
#else
|
||||||
|
/* refresh full display */
|
||||||
|
XPutImage(display, window, gc, ximage, 0, 0, 0, 0, LV_HOR_RES, LV_VER_RES);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
lv_disp_flush_ready(disp_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_x11_get_pointer(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
(void) indev_drv; // Unused
|
||||||
|
|
||||||
|
data->point = mouse_pos;
|
||||||
|
data->state = left_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_x11_get_mousewheel(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
(void) indev_drv; // Unused
|
||||||
|
|
||||||
|
data->state = wheel_mouse_btn ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||||
|
data->enc_diff = wheel_cnt;
|
||||||
|
wheel_cnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_x11_get_keyboard(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||||
|
{
|
||||||
|
(void) indev_drv; // Unused
|
||||||
|
|
||||||
|
size_t len = strlen(kb_buffer);
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
data->state = LV_INDEV_STATE_PRESSED;
|
||||||
|
data->key = kb_buffer[0];
|
||||||
|
memmove(kb_buffer, kb_buffer + 1, len);
|
||||||
|
data->continue_reading = (len > 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_x11_init(char const* title, lv_coord_t width, lv_coord_t height)
|
||||||
|
{
|
||||||
|
/* setup display/screen */
|
||||||
|
display = XOpenDisplay(NULL);
|
||||||
|
int screen = DefaultScreen(display);
|
||||||
|
|
||||||
|
/* drawing contexts for an window */
|
||||||
|
unsigned long myforeground = BlackPixel(display, screen);
|
||||||
|
unsigned long mybackground = WhitePixel(display, screen);
|
||||||
|
|
||||||
|
/* create window */
|
||||||
|
window = XCreateSimpleWindow(display, DefaultRootWindow(display),
|
||||||
|
0, 0, width, height,
|
||||||
|
0, myforeground, mybackground);
|
||||||
|
|
||||||
|
/* window manager properties (yes, use of StdProp is obsolete) */
|
||||||
|
XSetStandardProperties(display, window, title, NULL, None, NULL, 0, NULL);
|
||||||
|
|
||||||
|
/* allow receiving mouse and keyboard events */
|
||||||
|
XSelectInput(display, window, PointerMotionMask|ButtonPressMask|ButtonReleaseMask|KeyPressMask|KeyReleaseMask|ExposureMask);
|
||||||
|
|
||||||
|
/* graphics context */
|
||||||
|
gc = XCreateGC(display, window, 0, 0);
|
||||||
|
|
||||||
|
lv_x11_hide_cursor();
|
||||||
|
|
||||||
|
/* create cache XImage */
|
||||||
|
Visual* visual = XDefaultVisual(display, screen);
|
||||||
|
int dplanes = DisplayPlanes(display, screen);
|
||||||
|
ximage = XCreateImage(display, visual, dplanes, ZPixmap, 0,
|
||||||
|
malloc(width * height * sizeof(uint32_t)), width, height, 32, 0);
|
||||||
|
|
||||||
|
timer = lv_timer_create(x11_event_handler, 10, NULL);
|
||||||
|
|
||||||
|
/* finally bring window on top of the other windows */
|
||||||
|
XMapRaised(display, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_x11_deinit(void)
|
||||||
|
{
|
||||||
|
lv_timer_del(timer);
|
||||||
|
|
||||||
|
free(ximage->data);
|
||||||
|
|
||||||
|
XDestroyImage(ximage);
|
||||||
|
ximage = NULL;
|
||||||
|
|
||||||
|
XFreeGC(display, gc);
|
||||||
|
gc = NULL;
|
||||||
|
XDestroyWindow(display, window);
|
||||||
|
window = (XID)-1;
|
||||||
|
|
||||||
|
XCloseDisplay(display);
|
||||||
|
display = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_X11
|
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* @file x11.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef X11_H
|
||||||
|
#define X11_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#ifndef LV_DRV_NO_CONF
|
||||||
|
#ifdef LV_CONF_INCLUDE_SIMPLE
|
||||||
|
#include "lv_drv_conf.h"
|
||||||
|
#else
|
||||||
|
#include "../../lv_drv_conf.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_X11
|
||||||
|
|
||||||
|
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#include "lvgl.h"
|
||||||
|
#else
|
||||||
|
#include "lvgl/lvgl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
void lv_x11_init(char const* title, lv_coord_t width, lv_coord_t height);
|
||||||
|
void lv_x11_deinit(void);
|
||||||
|
void lv_x11_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p);
|
||||||
|
void lv_x11_get_pointer(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||||
|
void lv_x11_get_mousewheel(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||||
|
void lv_x11_get_keyboard(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#endif /* USE_X11 */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* X11_H */
|
@ -1,23 +1,23 @@
|
|||||||
name: Push LVGL release to Espressif Component Service
|
name: Push LVGL release to Espressif Component Service
|
||||||
|
|
||||||
# If the commit is tagged, it will be uploaded. Other scenario silently fail.
|
# If the commit is tagged, it will be uploaded. Other scenario silently fail.
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
tags:
|
||||||
- master
|
- v*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upload_components:
|
upload_components:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: "recursive"
|
submodules: "recursive"
|
||||||
|
|
||||||
- name: Upload component to component registry
|
- name: Upload component to component registry
|
||||||
uses: espressif/github-actions/upload_components@master
|
uses: espressif/upload-components-ci-action@v1
|
||||||
with:
|
with:
|
||||||
name: "lvgl"
|
name: "lvgl"
|
||||||
version: "git"
|
version: ${{ github.ref_name }}
|
||||||
namespace: "lvgl"
|
namespace: "lvgl"
|
||||||
api_token: ${{ secrets.ESP_IDF_COMPONENT_API_TOKEN }}
|
api_token: ${{ secrets.ESP_IDF_COMPONENT_API_TOKEN }}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
# Tiny TTF font engine
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Use https://github.com/nothings/stb to render TrueType fonts in LVGL.
|
||||||
|
|
||||||
|
When enabled in `lv_conf.h` with `LV_USE_TINY_TTF`
|
||||||
|
`lv_tiny_ttf_create_data(data, data_size, font_size)` can be used to
|
||||||
|
create a TTF font instance at the specified font size. You can then
|
||||||
|
use that font anywhere `lv_font_t` is accepted.
|
||||||
|
|
||||||
|
By default, the TTF or OTF file must be embedded as an array, either in
|
||||||
|
a header, or loaded into RAM in order to function.
|
||||||
|
|
||||||
|
However, if `LV_TINY_TTF_FILE_SUPPORT` is enabled,
|
||||||
|
`lv_tiny_ttf_create_file(path, font_size)` will also be available,
|
||||||
|
allowing tiny_ttf to stream from a file. The file must remain open the
|
||||||
|
entire time the font is being used, and streaming on demand may be
|
||||||
|
considerably slower.
|
||||||
|
|
||||||
|
After a font is created, you can change the font size in pixels by using
|
||||||
|
`lv_tiny_ttf_set_size(font, font_size)`.
|
||||||
|
|
||||||
|
By default, a font will use up to 4KB of cache to speed up rendering
|
||||||
|
glyphs. This maximum can be changed by using
|
||||||
|
`lv_tiny_ttf_create_data_ex(data, data_size, font_size, cache_size)`
|
||||||
|
or `lv_tiny_ttf_create_file_ex(path, font_size, cache_size)` (when
|
||||||
|
available). The cache size is indicated in bytes.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```eval_rst
|
||||||
|
.. doxygenfile:: lv_tiny_ttf.h
|
||||||
|
:project: lvgl
|
||||||
|
```
|
@ -1,4 +1,32 @@
|
|||||||
# ARM-2D GPU
|
# Arm-2D GPU
|
||||||
|
|
||||||
TODO
|
Arm-2D is not a GPU but **an abstraction layer for 2D GPUs dedicated to Microcontrollers**. It supports all Cortex-M processors ranging from Cortex-M0 to the latest Cortex-M85.
|
||||||
|
|
||||||
|
Arm-2D is an open-source project on Github. For more, please refer to: https://github.com/ARM-software/Arm-2D.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## How to Use
|
||||||
|
|
||||||
|
In general, you can set the macro `LV_USE_GPU_ARM2D` to `1`in `lv_conf.h` to enable Arm-2D acceleration for LVGL.
|
||||||
|
|
||||||
|
If you are using **[CMSIS-Pack](https://github.com/lvgl/lvgl/tree/master/env_support/cmsis-pack)** to deploy the LVGL. You don't have to define the macro `LV_USE_GPU_ARM2D` manually, instead, please select the component `GPU Arm-2D` in the **RTE** dialog. This step will define the macro for us.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Design Considerations
|
||||||
|
|
||||||
|
As mentioned before, Arm-2D is an abstraction layer for 2D GPU; hence if there is no accelerator or dedicated instruction set (such as Helium or ACI) available for Arm-2D, it provides negligible performance boost for LVGL (sometimes worse) for regular Cortex-M processors.
|
||||||
|
|
||||||
|
**We highly recommend you enable Arm-2D acceleration for LVGL** when:
|
||||||
|
|
||||||
|
- The target processors are **Cortex-M55** and/or **Cortex-M85**
|
||||||
|
- The target processors support **[Helium](https://developer.arm.com/documentation/102102/0103/?lang=en)**.
|
||||||
|
- The device vendor provides an arm-2d compliant driver for their propriotory 2D accelerators and/or customized instruction set.
|
||||||
|
- The target device contains [DMA-350](https://community.arm.com/arm-community-blogs/b/internet-of-things-blog/posts/arm-corelink-dma-350-next-generation-direct-memory-access-for-endpoint-ai)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
- [A Cortex-M55 (supports Helium) based MDK Project, PC emulation is available.](https://github.com/lvgl/lv_port_an547_cm55_sim)
|
||||||
|
Binary file not shown.
@ -0,0 +1,4 @@
|
|||||||
|
This folder is for LVGL SquareLine Studio
|
||||||
|
|
||||||
|
SquareLine Studio can automatically put the generated C files into `ui` folder, so that rt-thread will automatically detect them; or, as a user, you can move the generated C files into `ui` folder manually.
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
from building import *
|
||||||
|
|
||||||
|
cwd = GetCurrentDir()
|
||||||
|
src = Glob('*.c')
|
||||||
|
inc = [cwd]
|
||||||
|
|
||||||
|
# check if .h or .hpp files exsit
|
||||||
|
def check_h_hpp_exsit(path):
|
||||||
|
file_dirs = os.listdir(path)
|
||||||
|
for file_dir in file_dirs:
|
||||||
|
if os.path.splitext(file_dir)[1] in ['.h', '.hpp']:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
sls_src_cwd = cwd
|
||||||
|
for root, dirs, files in os.walk(sls_src_cwd):
|
||||||
|
for dir in dirs:
|
||||||
|
current_path = os.path.join(root, dir)
|
||||||
|
src = src + Glob(os.path.join(current_path,'*.c')) # add all .c files
|
||||||
|
if check_h_hpp_exsit(current_path): # add .h and .hpp path
|
||||||
|
inc = inc + [current_path]
|
||||||
|
|
||||||
|
group = DefineGroup('LVGL-SquareLine', src, depend = ['PKG_USING_LVGL_SQUARELINE'], CPPPATH = inc)
|
||||||
|
|
||||||
|
Return('group')
|
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2022-05-13 Meco Man First version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __RTTHREAD__
|
||||||
|
|
||||||
|
void lv_user_gui_init(void)
|
||||||
|
{
|
||||||
|
extern void ui_init(void);
|
||||||
|
ui_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __RTTHREAD__ */
|
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2006-2021, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2022-11-20 Meco Man The first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __RTTHREAD__
|
||||||
|
|
||||||
|
#include "../../../../../lvgl.h" /* back to the root folder's lvgl.h */
|
||||||
|
|
||||||
|
#endif /* __RTTHREAD__ */
|
@ -1 +1 @@
|
|||||||
CSRCS += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/examples -name \*.c)
|
CSRCS += $(shell find -L $(LVGL_DIR)/$(LVGL_DIR_NAME)/examples -name "*.c")
|
||||||
|
@ -1,2 +1,5 @@
|
|||||||
description: LVGL - Light and Versatile Graphics Library
|
description: LVGL - Light and Versatile Graphics Library
|
||||||
url: https://github.com/lvgl/lvgl
|
url: https://lvgl.io/
|
||||||
|
repository: https://github.com/lvgl/lvgl.git
|
||||||
|
documentation: https://docs.lvgl.io/
|
||||||
|
issues: https://github.com/lvgl/lvgl/issues
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue