mirror of
https://github.com/lvgl/lvgl.git
synced 2024-11-23 01:33:59 +08:00
feat(driver): import Wayland driver from v8 (#6549)
This commit is contained in:
parent
aebb4d3ad3
commit
e186b4c8b6
1
.gitignore
vendored
1
.gitignore
vendored
@ -24,6 +24,7 @@ test_screenshot_error.h
|
|||||||
build/
|
build/
|
||||||
tests/build_*/
|
tests/build_*/
|
||||||
tests/report/
|
tests/report/
|
||||||
|
tests/wayland_protocols/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
|
12
Kconfig
12
Kconfig
@ -1562,6 +1562,18 @@ menu "LVGL configuration"
|
|||||||
With 2 buffers in flush_cb only and address change is required.
|
With 2 buffers in flush_cb only and address change is required.
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
config LV_USE_WAYLAND
|
||||||
|
bool "Use the wayland client to open a window and handle inputs on Linux or BSD"
|
||||||
|
default n
|
||||||
|
config LV_WAYLAND_WINDOW_DECORATIONS
|
||||||
|
bool "Draw client side window decorations, only necessary on Mutter (GNOME)"
|
||||||
|
depends on LV_USE_WAYLAND
|
||||||
|
default n
|
||||||
|
config LV_WAYLAND_WL_SHELL
|
||||||
|
bool "Support the legacy wl_shell instead of the default XDG Shell protocol"
|
||||||
|
depends on LV_USE_WAYLAND
|
||||||
|
default n
|
||||||
|
|
||||||
config LV_USE_LINUX_FBDEV
|
config LV_USE_LINUX_FBDEV
|
||||||
bool "Use Linux framebuffer device"
|
bool "Use Linux framebuffer device"
|
||||||
default n
|
default n
|
||||||
|
@ -11,3 +11,4 @@ Drivers
|
|||||||
X11
|
X11
|
||||||
windows
|
windows
|
||||||
opengles
|
opengles
|
||||||
|
wayland
|
||||||
|
180
docs/integration/driver/wayland.rst
Normal file
180
docs/integration/driver/wayland.rst
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
=============================
|
||||||
|
Wayland Display/Inputs driver
|
||||||
|
=============================
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
| The **Wayland** `driver <https://github.com/lvgl/lvgl/tree/master/src/drivers/wayland>`__ offers support for simulating the LVGL display and keyboard/mouse inputs in a desktop window.
|
||||||
|
| It is an alternative to **X11** or **SDL2**
|
||||||
|
|
||||||
|
The main purpose for this driver is for testing/debugging the LVGL application, it can also be used to run applications in 'kiosk mode'
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
The wayland driver requires some dependencies.
|
||||||
|
|
||||||
|
On Ubuntu
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
sudo apt-get install libwayland-dev libxkbcommon-dev libwayland-bin wayland-protocols
|
||||||
|
|
||||||
|
On Fedora
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
sudo dnf install wayland-devel libxkbcommon-devel wayland-utils wayland-protocols-devel
|
||||||
|
|
||||||
|
|
||||||
|
Configuring the wayland driver
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
1. Enable the wayland driver in ``lv_conf.h``
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
#define LV_USE_WAYLAND 1
|
||||||
|
|
||||||
|
2. Optional configuration options:
|
||||||
|
|
||||||
|
- Enable window decorations, only required on GNOME because out of all the available wayland compositors
|
||||||
|
**only** Mutter/GNOME enforces the use of client side decorations
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
#define LV_WAYLAND_WINDOW_DECORATIONS 1
|
||||||
|
|
||||||
|
- Enable support for the deprecated 'wl_shell', Only useful when the BSP on the target has weston ``9.x``
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
#define LV_WAYLAND_WL_SHELL 1
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
An example simulator is available in this `repo <https://github.com/lvgl/lv_port_linux/>`__
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
#. In ``main.c`` ``#incude "lv_drivers/wayland/wayland.h"``
|
||||||
|
#. Enable the Wayland driver in ``lv_conf.h`` with ``LV_USE_WAYLAND 1``
|
||||||
|
|
||||||
|
#. ``LV_COLOR_DEPTH`` should be set either to ``32`` or ``16`` in ``lv_conf.h``
|
||||||
|
|
||||||
|
#. Add a display using ``lv_wayland_window_create()``,
|
||||||
|
possibly with a close callback to track the status of each display:
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
#define H_RES (800)
|
||||||
|
#define V_RES (480)
|
||||||
|
|
||||||
|
/* Create a display */
|
||||||
|
lv_disp_t * disp = lv_wayland_create_window(H_RES, V_RES, "Window Title", close_cb);
|
||||||
|
|
||||||
|
|
||||||
|
As part of the above call, the Wayland driver will register four input devices
|
||||||
|
for each display:
|
||||||
|
|
||||||
|
* a KEYPAD connected to Wayland keyboard events
|
||||||
|
* a POINTER connected to Wayland touch events
|
||||||
|
* a POINTER connected to Wayland pointer events
|
||||||
|
* an ENCODER connected to Wayland pointer axis events
|
||||||
|
|
||||||
|
Handles for input devices of each display can be obtained using
|
||||||
|
``lv_wayland_get_indev_keyboard()``, ``lv_wayland_get_indev_touchscreen()``,
|
||||||
|
``lv_wayland_get_indev_pointer()`` and ``lv_wayland_get_indev_pointeraxis()`` respectively.
|
||||||
|
|
||||||
|
Fullscreen mode
|
||||||
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
To programmatically fullscreen the window,
|
||||||
|
use the ``lv_wayland_window_set_fullscreen()`` function respectively with ``true``
|
||||||
|
or ``false`` for the ``fullscreen`` argument.
|
||||||
|
|
||||||
|
Maximized mode
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
To programmatically maximize the window,
|
||||||
|
use the ``lv_wayland_window_set_maximized()`` function respectively with ``true``
|
||||||
|
or ``false`` for the ``maximized`` argument.
|
||||||
|
|
||||||
|
|
||||||
|
Custom timer handler
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Always call ``lv_wayland_timer_handler()`` in your timer loop instead of the regular ``lv_timer_handler()``.
|
||||||
|
|
||||||
|
**Note:** ``lv_wayland_timer_handler()`` internally calls ``lv_timer_handler()``
|
||||||
|
|
||||||
|
This allows the wayland client to work on well on weston, resizing shared memory buffers during
|
||||||
|
a commit does not work well on weston.
|
||||||
|
|
||||||
|
Wrapping the call to ``lv_timer_hander()`` is a necessity to have more control over
|
||||||
|
when the LVGL flush callback is called.
|
||||||
|
|
||||||
|
The custom timer handler returns ``false`` if the frame from previous cycle is not rendered.
|
||||||
|
When this happens, it usually means that the application is minimized or hidden behind another window.
|
||||||
|
Causing the driver to wait until the arrival of any message on the wayland socket, the process is in interruptible sleep.
|
||||||
|
|
||||||
|
Building the wayland driver
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
An example simulator is available in this `repo <https://github.com/lvgl/lv_port_linux/>`__
|
||||||
|
|
||||||
|
If there is a need to use driver with another build system. The source and header files for the XDG shell
|
||||||
|
must be generated from the definitions for the XDG shell protocol.
|
||||||
|
|
||||||
|
In the example Cmake is used to perform the operation by invoking the ``wayland-scanner`` utility
|
||||||
|
|
||||||
|
To achieve this manually,
|
||||||
|
|
||||||
|
Make sure the dependencies listed at the start of the article are installed.
|
||||||
|
|
||||||
|
The wayland protocol is defined using XML files which are present in ``/usr/share/wayland-protocols``
|
||||||
|
|
||||||
|
To generate the required files run the following commands:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
wayland-scanner client-header </usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > wayland_xdg_shell.h
|
||||||
|
wayland-scanner private-code </usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > wayland_xdg_shell.c
|
||||||
|
|
||||||
|
The resulting files can then be integrated into the project, it's better to re-run ``wayland-scanner`` on
|
||||||
|
each build to ensure that the correct versions are generated, they must match the version of the ``wayland-client``
|
||||||
|
dynamically linked library installed on the system.
|
||||||
|
|
||||||
|
|
||||||
|
Current state and objectives
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
* Add direct rendering mode
|
||||||
|
* Refactor the shell integrations to avoid excessive conditional compilation
|
||||||
|
* Technically, the wayland driver allows to create multiple windows - but this feature is experimental.
|
||||||
|
* Eventually add enhanced support for XDG shell to allow the creation of desktop apps on Unix-like platforms,
|
||||||
|
similar to what the win32 driver does.
|
||||||
|
* Add a support for Mesa, currently wl_shm is used and it's not the most effective technique.
|
||||||
|
|
||||||
|
|
||||||
|
Bug reports
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The wayland driver is currently under construction, bug reports, contributions and feedback is always welcome.
|
||||||
|
|
||||||
|
It is however important to create detailed issues when a problem is encountered, logs and screenshots of the problem are of great help.
|
||||||
|
|
||||||
|
Please enable ``LV_USE_LOG`` and launch the simulator executable like so
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
WAYLAND_DEBUG=1 ./path/to/simulator_executable > /tmp/debug 2>&1
|
||||||
|
|
||||||
|
This will create a log file called ``debug`` in the ``/tmp`` directory, copy-paste the content of the file in the github issue.
|
||||||
|
The log file contains LVGL logs and the wayland messages.
|
||||||
|
|
||||||
|
Be sure to replicate the problem quickly otherwise the logs become too big
|
||||||
|
|
@ -955,6 +955,13 @@
|
|||||||
#define LV_X11_RENDER_MODE_FULL 0 /*Full render mode*/
|
#define LV_X11_RENDER_MODE_FULL 0 /*Full render mode*/
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*Use Wayland to open a window and handle input on Linux or BSD desktops */
|
||||||
|
#define LV_USE_WAYLAND 0
|
||||||
|
#if LV_USE_WAYLAND
|
||||||
|
#define LV_WAYLAND_WINDOW_DECORATIONS 0 /*Draw client side window decorations only necessary on Mutter/GNOME*/
|
||||||
|
#define LV_WAYLAND_WL_SHELL 0 /*Use the legacy wl_shell protocol instead of the default XDG shell*/
|
||||||
|
#endif
|
||||||
|
|
||||||
/*Driver for /dev/fb*/
|
/*Driver for /dev/fb*/
|
||||||
#define LV_USE_LINUX_FBDEV 0
|
#define LV_USE_LINUX_FBDEV 0
|
||||||
#if LV_USE_LINUX_FBDEV
|
#if LV_USE_LINUX_FBDEV
|
||||||
|
@ -11,5 +11,7 @@ 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 libsdl2-dev libsdl2-image-dev \
|
libglew-dev libglfw3-dev libsdl2-dev libsdl2-image-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 wayland-protocols libwayland-dev libwayland-bin \
|
||||||
|
libwayland-dev:i386 libxkbcommon-dev:i386
|
||||||
pip3 install pypng lz4
|
pip3 install pypng lz4
|
||||||
|
@ -43,6 +43,8 @@ extern "C" {
|
|||||||
|
|
||||||
#include "qnx/lv_qnx.h"
|
#include "qnx/lv_qnx.h"
|
||||||
|
|
||||||
|
#include "wayland/lv_wayland.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
*********************/
|
*********************/
|
||||||
|
2860
src/drivers/wayland/lv_wayland.c
Normal file
2860
src/drivers/wayland/lv_wayland.c
Normal file
File diff suppressed because it is too large
Load Diff
142
src/drivers/wayland/lv_wayland.h
Normal file
142
src/drivers/wayland/lv_wayland.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*******************************************************************
|
||||||
|
*
|
||||||
|
* @file lv_wayland.h - Public functions of the LVGL Wayland client
|
||||||
|
*
|
||||||
|
* Based on the original file from the repository.
|
||||||
|
*
|
||||||
|
* Porting to LVGL 9.1
|
||||||
|
* 2024 EDGEMTech Ltd.
|
||||||
|
*
|
||||||
|
* See LICENCE.txt for details
|
||||||
|
*
|
||||||
|
* Author(s): EDGEMTech Ltd, Erik Tagirov (erik.tagirov@edgemtech.ch)
|
||||||
|
*
|
||||||
|
******************************************************************/
|
||||||
|
#ifndef LV_WAYLAND_H
|
||||||
|
#define LV_WAYLAND_H
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#include "../../display/lv_display.h"
|
||||||
|
#include "../../indev/lv_indev.h"
|
||||||
|
|
||||||
|
#if LV_USE_WAYLAND
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef bool (*lv_wayland_display_close_f_t)(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the file descriptor of the wayland socket
|
||||||
|
*/
|
||||||
|
int lv_wayland_get_fd(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a window
|
||||||
|
* @param hor_res The width of the window in pixels
|
||||||
|
* @param ver_res The height of the window in pixels
|
||||||
|
* @param title The title of the window
|
||||||
|
* @param close_cb The callback that will be execute when the user closes the window
|
||||||
|
* @return The LVGL display associated to the window
|
||||||
|
*/
|
||||||
|
lv_display_t * lv_wayland_window_create(uint32_t hor_res, uint32_t ver_res, char * title,
|
||||||
|
lv_wayland_display_close_f_t close_cb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the window programmatically
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
*/
|
||||||
|
void lv_wayland_window_close(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the window is open
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @return true: The window is open
|
||||||
|
*/
|
||||||
|
bool lv_wayland_window_is_open(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the fullscreen state of the window
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @param fullscreen If true the window enters fullscreen
|
||||||
|
*/
|
||||||
|
void lv_wayland_window_set_fullscreen(lv_display_t * disp, bool fullscreen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the maximized state of the window
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @param fullscreen If true the window is maximized
|
||||||
|
*/
|
||||||
|
void lv_wayland_window_set_maximized(lv_display_t * disp, bool maximize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the input device of the mouse pointer
|
||||||
|
* @note It is used to create an input group on application start
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @return The input device
|
||||||
|
*/
|
||||||
|
lv_indev_t * lv_wayland_get_pointer(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the input device of the encoder
|
||||||
|
* @note It is used to create an input group on application start
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @return The input device
|
||||||
|
*/
|
||||||
|
lv_indev_t * lv_wayland_get_pointeraxis(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the input device of the keyboard
|
||||||
|
* @note It is used to create an input group on application start
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @return The input device
|
||||||
|
*/
|
||||||
|
lv_indev_t * lv_wayland_get_keyboard(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains the input device of the touch screen
|
||||||
|
* @note It is used to create an input group on application start
|
||||||
|
* @param disp Reference to the LVGL display associated to the window
|
||||||
|
* @return The input device
|
||||||
|
*/
|
||||||
|
lv_indev_t * lv_wayland_get_touchscreen(lv_display_t * disp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around lv_timer_handler
|
||||||
|
* @note Must be called in the application run loop instead of the
|
||||||
|
* regular lv_timer_handler provided by LVGL
|
||||||
|
* @return true: if the cycle was completed, false if the application
|
||||||
|
* went to sleep because the last frame wasn't completed
|
||||||
|
*/
|
||||||
|
bool lv_wayland_timer_handler(void);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#endif /* LV_USE_WAYLAND */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
#endif /* WAYLAND_H */
|
675
src/drivers/wayland/lv_wayland_smm.c
Normal file
675
src/drivers/wayland/lv_wayland_smm.c
Normal file
@ -0,0 +1,675 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_wayland_smm.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef int dummy_t; /* Make GCC on windows happy, avoid empty translation unit */
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
#include "lv_wayland_smm.h"
|
||||||
|
#include "../../display/lv_display.h"
|
||||||
|
|
||||||
|
#if LV_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>
|
||||||
|
|
||||||
|
#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))
|
||||||
|
|
||||||
|
#define WAYLAND_FD_NAME "/" SMM_FD_NAME "-XXXXX"
|
||||||
|
|
||||||
|
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[] = WAYLAND_FD_NAME;
|
||||||
|
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 /* LV_USE_WAYLAND */
|
||||||
|
#endif /* _WIN32 */
|
105
src/drivers/wayland/lv_wayland_smm.h
Normal file
105
src/drivers/wayland/lv_wayland_smm.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* @file lv_wayland_smm.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef LV_WAYLAND_SMM_H
|
||||||
|
#define LV_WAYLAND_SMM_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#include "../../display/lv_display.h"
|
||||||
|
#include LV_STDDEF_INCLUDE
|
||||||
|
#include LV_STDBOOL_INCLUDE
|
||||||
|
|
||||||
|
#if LV_USE_WAYLAND
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#define SMM_FD_NAME "lvgl-wayland"
|
||||||
|
#define SMM_POOL_TAGS (1)
|
||||||
|
#define SMM_BUFFER_TAGS (2)
|
||||||
|
#define SMM_GROUP_TAGS (1)
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef void smm_pool_t;
|
||||||
|
typedef void smm_buffer_t;
|
||||||
|
typedef void smm_group_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* LV_USE_WAYLAND */
|
||||||
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* LV_WAYLAND_SMM_H */
|
@ -3140,6 +3140,31 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*Use Wayland to open a window and handle input on Linux or BSD desktops */
|
||||||
|
#ifndef LV_USE_WAYLAND
|
||||||
|
#ifdef CONFIG_LV_USE_WAYLAND
|
||||||
|
#define LV_USE_WAYLAND CONFIG_LV_USE_WAYLAND
|
||||||
|
#else
|
||||||
|
#define LV_USE_WAYLAND 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#if LV_USE_WAYLAND
|
||||||
|
#ifndef LV_WAYLAND_WINDOW_DECORATIONS
|
||||||
|
#ifdef CONFIG_LV_WAYLAND_WINDOW_DECORATIONS
|
||||||
|
#define LV_WAYLAND_WINDOW_DECORATIONS CONFIG_LV_WAYLAND_WINDOW_DECORATIONS
|
||||||
|
#else
|
||||||
|
#define LV_WAYLAND_WINDOW_DECORATIONS 0 /*Draw client side window decorations only necessary on Mutter/GNOME*/
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef LV_WAYLAND_WL_SHELL
|
||||||
|
#ifdef CONFIG_LV_WAYLAND_WL_SHELL
|
||||||
|
#define LV_WAYLAND_WL_SHELL CONFIG_LV_WAYLAND_WL_SHELL
|
||||||
|
#else
|
||||||
|
#define LV_WAYLAND_WL_SHELL 0 /*Use the legacy wl_shell protocol instead of the default XDG shell*/
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/*Driver for /dev/fb*/
|
/*Driver for /dev/fb*/
|
||||||
#ifndef LV_USE_LINUX_FBDEV
|
#ifndef LV_USE_LINUX_FBDEV
|
||||||
#ifdef CONFIG_LV_USE_LINUX_FBDEV
|
#ifdef CONFIG_LV_USE_LINUX_FBDEV
|
||||||
|
@ -326,6 +326,19 @@ if(NOT WIN32)
|
|||||||
# libjpeg is required for the jpeg test case
|
# libjpeg is required for the jpeg test case
|
||||||
find_package(JPEG REQUIRED)
|
find_package(JPEG REQUIRED)
|
||||||
include_directories(${JPEG_INCLUDE_DIR})
|
include_directories(${JPEG_INCLUDE_DIR})
|
||||||
|
|
||||||
|
# Wayland
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(wayland-client REQUIRED wayland-client)
|
||||||
|
pkg_check_modules(wayland-cursor REQUIRED wayland-cursor)
|
||||||
|
pkg_check_modules(xkbcommon REQUIRED xkbcommon)
|
||||||
|
|
||||||
|
link_libraries(wayland-client wayland-cursor xkbcommon)
|
||||||
|
|
||||||
|
# Add auto generated source required for XDG shell
|
||||||
|
include_directories("${LVGL_TEST_DIR}/wayland_protocols")
|
||||||
|
target_sources(test_common PUBLIC "wayland_protocols/wayland_xdg_shell.c")
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# libpng is required for the png test case
|
# libpng is required for the png test case
|
||||||
|
@ -14,6 +14,9 @@ lvgl_test_dir = os.path.dirname(os.path.realpath(__file__))
|
|||||||
lvgl_script_path = os.path.join(lvgl_test_dir, "../scripts")
|
lvgl_script_path = os.path.join(lvgl_test_dir, "../scripts")
|
||||||
sys.path.append(lvgl_script_path)
|
sys.path.append(lvgl_script_path)
|
||||||
|
|
||||||
|
wayland_dir = os.path.join(lvgl_test_dir, "wayland_protocols")
|
||||||
|
wayland_protocols_dir = os.path.realpath("/usr/share/wayland-protocols")
|
||||||
|
|
||||||
from LVGLImage import LVGLImage, ColorFormat, CompressMethod
|
from LVGLImage import LVGLImage, ColorFormat, CompressMethod
|
||||||
|
|
||||||
# Key values must match variable names in CMakeLists.txt.
|
# Key values must match variable names in CMakeLists.txt.
|
||||||
@ -70,6 +73,37 @@ def get_build_dir(options_name):
|
|||||||
global lvgl_test_dir
|
global lvgl_test_dir
|
||||||
return os.path.join(lvgl_test_dir, get_base_build_dir(options_name))
|
return os.path.join(lvgl_test_dir, get_base_build_dir(options_name))
|
||||||
|
|
||||||
|
def gen_wayland_protocols(clean):
|
||||||
|
'''Generates the xdg shell interface from wayland protocol definitions'''
|
||||||
|
|
||||||
|
if clean:
|
||||||
|
delete_dir_ignore_missing(wayland_dir)
|
||||||
|
|
||||||
|
if not os.path.isdir(wayland_dir):
|
||||||
|
os.mkdir(wayland_dir)
|
||||||
|
subprocess.check_call(['wayland-scanner',
|
||||||
|
'client-header',
|
||||||
|
os.path.join(wayland_protocols_dir, "stable/xdg-shell/xdg-shell.xml"),
|
||||||
|
os.path.join(wayland_dir, "wayland_xdg_shell.h.original"),
|
||||||
|
])
|
||||||
|
|
||||||
|
subprocess.check_call(['wayland-scanner',
|
||||||
|
'private-code',
|
||||||
|
os.path.join(wayland_protocols_dir, "stable/xdg-shell/xdg-shell.xml"),
|
||||||
|
os.path.join(wayland_dir, "wayland_xdg_shell.c.original"),
|
||||||
|
])
|
||||||
|
|
||||||
|
# Insert guards
|
||||||
|
with open(os.path.join(wayland_dir, "wayland_xdg_shell.h"), "w") as outfile:
|
||||||
|
subprocess.check_call(['sed','-e', "1i #if LV_BUILD_TEST", '-e', '$a #endif',
|
||||||
|
os.path.join(wayland_dir, "wayland_xdg_shell.h.original")], stdout=outfile)
|
||||||
|
|
||||||
|
with open(os.path.join(wayland_dir, "wayland_xdg_shell.c"), "w") as outfile:
|
||||||
|
subprocess.check_call(['sed','-e', "1i #if LV_BUILD_TEST", '-e', '$a #endif',
|
||||||
|
os.path.join(wayland_dir, "wayland_xdg_shell.c.original")], stdout=outfile)
|
||||||
|
|
||||||
|
subprocess.check_call(['rm', os.path.join(wayland_dir, "wayland_xdg_shell.c.original")])
|
||||||
|
subprocess.check_call(['rm', os.path.join(wayland_dir, "wayland_xdg_shell.h.original")])
|
||||||
|
|
||||||
def build_tests(options_name, build_type, clean):
|
def build_tests(options_name, build_type, clean):
|
||||||
'''Build all tests for the specified options name.'''
|
'''Build all tests for the specified options name.'''
|
||||||
@ -89,6 +123,10 @@ def build_tests(options_name, build_type, clean):
|
|||||||
delete_dir_ignore_missing(build_dir)
|
delete_dir_ignore_missing(build_dir)
|
||||||
|
|
||||||
os.chdir(lvgl_test_dir)
|
os.chdir(lvgl_test_dir)
|
||||||
|
|
||||||
|
if platform.system() != 'Windows':
|
||||||
|
gen_wayland_protocols(clean)
|
||||||
|
|
||||||
created_build_dir = False
|
created_build_dir = False
|
||||||
if not os.path.isdir(build_dir):
|
if not os.path.isdir(build_dir):
|
||||||
os.mkdir(build_dir)
|
os.mkdir(build_dir)
|
||||||
|
@ -126,6 +126,11 @@
|
|||||||
#define LV_USE_LINUX_FBDEV 1
|
#define LV_USE_LINUX_FBDEV 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_USE_WAYLAND
|
||||||
|
#define LV_USE_WAYLAND 1
|
||||||
|
#define LV_WAYLAND_WINDOW_DECORATIONS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
#define LV_USE_ILI9341 1
|
#define LV_USE_ILI9341 1
|
||||||
#define LV_USE_ST7735 1
|
#define LV_USE_ST7735 1
|
||||||
#define LV_USE_ST7789 1
|
#define LV_USE_ST7789 1
|
||||||
|
Loading…
Reference in New Issue
Block a user