* [OSS-Tools] [PATCH platsch 1/3] convert to meson build @ 2024-06-12 4:06 LI Qingwu 2024-06-12 4:06 ` [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch LI Qingwu 2024-06-12 4:06 ` [OSS-Tools] [PATCH platsch 3/3] Add spinner executable for boot animation and text show LI Qingwu 0 siblings, 2 replies; 5+ messages in thread From: LI Qingwu @ 2024-06-12 4:06 UTC (permalink / raw) To: Qing-wu.Li, oss-tools Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn> --- Makefile.am | 29 ----------------------------- README.rst | 27 +++++++++++++++++++++++++++ cairo.c | 4 ++-- configure.ac | 32 -------------------------------- meson.build | 26 ++++++++++++++++++++++++++ meson_options.txt | 1 + 6 files changed, 56 insertions(+), 63 deletions(-) delete mode 100644 Makefile.am delete mode 100644 configure.ac create mode 100644 meson.build create mode 100644 meson_options.txt diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index d486994..0000000 --- a/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -EXTRA_DIST = README.rst LICENSE - -sbin_PROGRAMS = platsch - -platsch_SOURCES = platsch.c -platsch_CFLAGS = $(LIBDRM_CFLAGS) -platsch_LDADD = $(LIBDRM_LIBS) - -if ENABLE_CAIRO -platsch_SOURCES += cairo.c -platsch_CFLAGS += $(CAIRO_CFLAGS) -platsch_LDADD += $(CAIRO_LIBS) -endif - -CLEANFILES = \ - $(DIST_ARCHIVES) - -DISTCLEAN = \ - config.log \ - config.status \ - Makefile - -MAINTAINERCLEANFILES = \ - aclocal.m4 \ - configure \ - depcomp \ - install-sh \ - Makefile.in \ - missing diff --git a/README.rst b/README.rst index e318120..2af29e4 100644 --- a/README.rst +++ b/README.rst @@ -141,3 +141,30 @@ By adding a Signed-off-by line (e.g. using ``git commit -s``) saying:: (using your real name and e-mail address), you state that your contributions are in line with the DCO. + +Cross compiling instructions +---------------------------- + +To cross-compile the project, use the following commands: + +.. code-block:: shell + + meson build --cross-file=<path-to-meson-cross-file> + ninja -C build + +Build options +------------- + +The following build options are available: + +.. list-table:: + :header-rows: 1 + + * - Option name + - Values + - Default + - Notes + * - HAVE_CAIRO + - true, false + - true + - Enable Cairo support diff --git a/cairo.c b/cairo.c index d29b3a4..8ced3b2 100644 --- a/cairo.c +++ b/cairo.c @@ -79,7 +79,7 @@ static const char *image_format_to_string(cairo_format_t format) return "RGB128F"; #endif case CAIRO_FORMAT_INVALID: - defaul: + default: return "invalid"; } } @@ -90,7 +90,6 @@ static int png_import_backend_import_picture(cairo_t *cr, const char *filename) cairo_format_t image_fmt, surface_fmt; cairo_surface_t *image, *surface; cairo_status_t status; - cairo_format_t format; int ret = 0; image = cairo_image_surface_create_from_png(filename); @@ -253,6 +252,7 @@ static uint32_t convert_to_cairo_format(uint32_t format) case DRM_FORMAT_XRGB8888: return CAIRO_FORMAT_ARGB32; } + return CAIRO_FORMAT_INVALID; } static cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 11cdded..0000000 --- a/configure.ac +++ /dev/null @@ -1,32 +0,0 @@ -AC_PREREQ([2.69]) -AC_INIT([platsch], [2019.12.0], [oss-tools@pengutronix.de]) -AC_CONFIG_SRCDIR([platsch.c]) -AM_INIT_AUTOMAKE([foreign dist-xz]) - -AC_PROG_CC -AC_PROG_MAKE_SET - -PKG_CHECK_MODULES([LIBDRM], [libdrm >= 2.4.112]) - -AC_ARG_ENABLE([cairo], - [AS_HELP_STRING([--disable-cairo], - [Enable CAIRO support @<:@default=check@:>@])], - [], - [enable_cairo=check]) - -AS_IF([test "$enable_cairo" != "no"], - [PKG_CHECK_MODULES([CAIRO], - [cairo >= 1.0], - [enable_cairo=yes - AC_DEFINE(HAVE_CAIRO)], - [AS_IF([test "$enable_cairo" = "yes"], - [AC_MSG_ERROR([cairo required, but not found.])], - [enable_cairo=no]) - ]) - ]) - -AM_CONDITIONAL([ENABLE_CAIRO], [test "$enable_cairo" = "yes"]) - -AC_CONFIG_FILES([Makefile]) - -AC_OUTPUT diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..e7e8e80 --- /dev/null +++ b/meson.build @@ -0,0 +1,26 @@ +project('platsch', 'c') + +# Define dependencies conditionally based on the HAVE_CAIRO option +platsch_dep = [dependency('libdrm', required: true)] +sources = ['platsch.c'] +args = [] + +have_cairo = get_option('HAVE_CAIRO') + +if have_cairo + platsch_dep += dependency('cairo', required: true) + sources += 'cairo.c' + args += ['-DHAVE_CAIRO'] +endif + +# Define the headers +headers = ['platsch.h'] + +# Create the platsch executable +executable('platsch', + sources, + dependencies: platsch_dep, + c_args: args, + install: true, + include_directories: include_directories('.') +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..1adfef3 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('HAVE_CAIRO', type: 'boolean', value: true, description: 'Enable Cairo support') -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch 2024-06-12 4:06 [OSS-Tools] [PATCH platsch 1/3] convert to meson build LI Qingwu @ 2024-06-12 4:06 ` LI Qingwu 2024-06-12 4:06 ` [OSS-Tools] [PATCH platsch 3/3] Add spinner executable for boot animation and text show LI Qingwu 1 sibling, 0 replies; 5+ messages in thread From: LI Qingwu @ 2024-06-12 4:06 UTC (permalink / raw) To: Qing-wu.Li, oss-tools Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn> --- cairo.c | 4 +- libplatsch.c | 614 ++++++++++++++++++++++++++++++++++++++ platsch.h => libplatsch.h | 45 ++- meson.build | 21 +- platsch.c | 534 +-------------------------------- 5 files changed, 677 insertions(+), 541 deletions(-) create mode 100644 libplatsch.c rename platsch.h => libplatsch.h (51%) diff --git a/cairo.c b/cairo.c index 8ced3b2..d651b7a 100644 --- a/cairo.c +++ b/cairo.c @@ -25,7 +25,7 @@ #include <drm_fourcc.h> #include <cairo.h> -#include "platsch.h" +#include "libplatsch.h" static struct cairo_ctx { struct modeset_dev *dev; @@ -255,7 +255,7 @@ static uint32_t convert_to_cairo_format(uint32_t format) return CAIRO_FORMAT_INVALID; } -static cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) +cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) { cairo_surface_t *surface; cairo_status_t status; diff --git a/libplatsch.c b/libplatsch.c new file mode 100644 index 0000000..9fceda4 --- /dev/null +++ b/libplatsch.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2019 Pengutronix, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Some code parts base on example code written in 2012 by David Herrmann + * <dh.herrmann@googlemail.com> and dedicated to the Public Domain. It was found + * in 2019 on + * https://raw.githubusercontent.com/dvdhrm/docs/master/drm-howto/modeset.c + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <drm_fourcc.h> +#include "libplatsch.h" + + +static const struct platsch_format platsch_formats[] = { + { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ + { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, +}; + + +ssize_t readfull(int fd, void *buf, size_t count) +{ + ssize_t ret = 0, err; + + while (count > 0) { + err = read(fd, buf, count); + if (err < 0) + return err; + else if (err > 0) { + buf += err; + count -= err; + ret += err; + } else { + return ret; + } + } + + return ret; +} + +static int draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) +{ + int fd_src; + char filename[128]; + ssize_t size; + int ret; + + /* Try cairo draw first and fall back in case of failure. */ + ret = cairo_draw_buffer(dev, dir, base); + if (ret == 0) + return ret; + + /* + * make it easy and load a raw file in the right format instead of + * opening an (say) PNG and convert the image data to the right format. + */ + ret = snprintf(filename, sizeof(filename), + "%s/%s-%ux%u-%s.bin", + dir, base, dev->width, dev->height, dev->format->name); + if (ret >= sizeof(filename)) { + error("Failed to fit filename into buffer\n"); + return -EINVAL; + } + + fd_src = open(filename, O_RDONLY | O_CLOEXEC); + if (fd_src < 0) { + error("Failed to open %s: %m\n", filename); + return -ENOENT; + } + + size = readfull(fd_src, dev->map, dev->size); + if (size < dev->size) { + if (size < 0) + error("Failed to read from %s: %m\n", filename); + else + error("Could only read %zd/%u bytes from %s\n", + size, dev->size, filename); + return -EIO; + } + + ret = close(fd_src); + if (ret < 0) { + /* Nothing we can do about this, so just warn */ + error("Failed to close image file\n"); + return -EIO; + } + + return 0; +} + +static struct modeset_dev *modeset_list = NULL; + +static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, + struct modeset_dev *dev) +{ + drmModeEncoder *enc; + unsigned int i, j; + int32_t crtc_id; + struct modeset_dev *iter; + + /* first try the currently connected encoder+crtc */ + if (conn->encoder_id) { + debug("connector #%d uses encoder #%d\n", conn->connector_id, + conn->encoder_id); + enc = drmModeGetEncoder(fd, conn->encoder_id); + assert(enc); + assert(enc->encoder_id == conn->encoder_id); + } else { + debug("connector #%d has no active encoder\n", + conn->connector_id); + enc = NULL; + dev->setmode = 1; + } + + if (enc) { + if (enc->crtc_id) { + crtc_id = enc->crtc_id; + assert(crtc_id >= 0); + + for (iter = modeset_list; iter; iter = iter->next) { + if (iter->crtc_id == crtc_id) { + crtc_id = -1; + break; + } + } + + if (crtc_id > 0) { + debug("encoder #%d uses crtc #%d\n", + enc->encoder_id, enc->crtc_id); + drmModeFreeEncoder(enc); + dev->crtc_id = crtc_id; + return 0; + } else { + debug("encoder #%d used crtc #%d, but that's in use\n", + enc->encoder_id, iter->crtc_id); + } + } else { + debug("encoder #%d doesn't have an active crtc\n", + enc->encoder_id); + } + + drmModeFreeEncoder(enc); + } + + /* If the connector is not currently bound to an encoder or if the + * encoder+crtc is already used by another connector (actually unlikely + * but let's be safe), iterate all other available encoders to find a + * matching CRTC. */ + for (i = 0; i < conn->count_encoders; ++i) { + enc = drmModeGetEncoder(fd, conn->encoders[i]); + if (!enc) { + error("Cannot retrieve encoder %u: %m\n", + conn->encoders[i]); + continue; + } + assert(enc->encoder_id == conn->encoders[i]); + + /* iterate all global CRTCs */ + for (j = 0; j < res->count_crtcs; ++j) { + /* check whether this CRTC works with the encoder */ + if (!(enc->possible_crtcs & (1 << j))) + continue; + + /* check that no other device already uses this CRTC */ + crtc_id = res->crtcs[j]; + for (iter = modeset_list; iter; iter = iter->next) { + if (iter->crtc_id == crtc_id) { + crtc_id = -1; + break; + } + } + + /* we have found a CRTC, so save it and return */ + if (crtc_id >= 0) { + debug("encoder #%d will use crtc #%d\n", + enc->encoder_id, crtc_id); + drmModeFreeEncoder(enc); + dev->crtc_id = crtc_id; + return 0; + } + + } + drmModeFreeEncoder(enc); + } + + error("Cannot find suitable CRTC for connector #%u\n", + conn->connector_id); + return -ENOENT; +} + +static int modeset_create_fb(int fd, struct modeset_dev *dev) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + struct drm_mode_map_dumb mreq; + int ret; + + /* create dumb buffer */ + memset(&creq, 0, sizeof(creq)); + creq.width = dev->width; + creq.height = dev->height; + creq.bpp = dev->format->bpp; + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + error("Cannot create dumb buffer: %m\n"); + return -errno; + } + dev->stride = creq.pitch; + dev->size = creq.size; + dev->handle = creq.handle; + + /* create framebuffer object for the dumb-buffer */ + ret = drmModeAddFB2(fd, dev->width, dev->height, + dev->format->format, + (uint32_t[4]){ dev->handle, }, + (uint32_t[4]){ dev->stride, }, + (uint32_t[4]){ 0, }, + &dev->fb_id, 0); + if (ret) { + ret = -errno; + error("Cannot create framebuffer: %m\n"); + goto err_destroy; + } + + /* prepare buffer for memory mapping */ + memset(&mreq, 0, sizeof(mreq)); + mreq.handle = dev->handle; + ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + ret = -errno; + error("Cannot get mmap offset: %m\n"); + goto err_fb; + } + + /* perform actual memory mapping */ + dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, mreq.offset); + if (dev->map == MAP_FAILED) { + ret = -errno; + error("Cannot mmap dumb buffer: %m\n"); + goto err_fb; + } + + /* + * Clear the framebuffer. Normally it's overwritten later with some + * image data, but in case this fails, initialize to all-black. + */ + memset(dev->map, 0x0, dev->size); + + return 0; + +err_fb: + drmModeRmFB(fd, dev->fb_id); +err_destroy: + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = dev->handle; + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return ret; +} + +/* Returns lowercase connector type names with '_' for '-' */ +static char *get_normalized_conn_type_name(uint32_t connector_type) +{ + int i; + const char *connector_name; + char *normalized_name; + + connector_name = drmModeGetConnectorTypeName(connector_type); + if (!connector_name) + return NULL; + + normalized_name = strdup(connector_name); + + for (i = 0; normalized_name[i]; i++) { + normalized_name[i] = tolower(normalized_name[i]); + if (normalized_name[i] == '-') + normalized_name[i] = '_'; + } + + return normalized_name; +} + +static const struct platsch_format *platsch_format_find(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) + if (!strcmp(platsch_formats[i].name, name)) + return &platsch_formats[i]; + + return NULL; +} + +static int set_env_connector_mode(drmModeConnector *conn, + struct modeset_dev *dev) +{ + int ret, i = 0; + u_int32_t width = 0, height = 0; + const char *mode; + char *connector_type_name, mode_env_name[32], fmt_specifier[32] = ""; + const struct platsch_format *format = NULL; + + connector_type_name = get_normalized_conn_type_name(conn->connector_type); + if (!connector_type_name) { + error("could not look up name for connector type %u\n", + conn->connector_type); + goto fallback; + } + + ret = snprintf(mode_env_name, sizeof(mode_env_name), "platsch_%s%u_mode", + connector_type_name, conn->connector_type_id); + free(connector_type_name); + if (ret >= sizeof(mode_env_name)) { + error("failed to fit platsch env mode variable name into buffer\n"); + return -EFAULT; + } + + /* check for connector mode configuration in environment */ + debug("looking up %s env variable\n", mode_env_name); + mode = getenv(mode_env_name); + if (!mode) + goto fallback; + + /* format suffix is optional */ + ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); + if (ret < 2) { + error("error while scanning %s for mode\n", mode_env_name); + return -EFAULT; + } + + /* use first mode matching given resolution */ + for (i = 0; i < conn->count_modes; i++) { + drmModeModeInfo mode = conn->modes[i]; + if (mode.hdisplay == width && mode.vdisplay == height) { + memcpy(&dev->mode, &mode, sizeof(dev->mode)); + dev->width = width; + dev->height = height; + break; + } + } + + if (i == conn->count_modes) { + error("no mode available matching %ux%u\n", width, height); + return -ENOENT; + } + + format = platsch_format_find(fmt_specifier); + if (!format) { + if (strlen(fmt_specifier)) + error("unknown format specifier %s\n", fmt_specifier); + goto fallback_format; + } + + dev->format = format; + + return 0; + +fallback: + memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); + dev->width = conn->modes[0].hdisplay; + dev->height = conn->modes[0].vdisplay; + debug("using default mode for connector #%u\n", conn->connector_id); + +fallback_format: + dev->format = &platsch_formats[0]; + debug("using default format %s for connector #%u\n", dev->format->name, + conn->connector_id); + + return 0; +} + +static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, + struct modeset_dev *dev) +{ + int ret; + + /* check if a monitor is connected */ + if (conn->connection != DRM_MODE_CONNECTED) { + error("Ignoring unused connector #%u\n", conn->connector_id); + return -ENOENT; + } + + /* check if there is at least one valid mode */ + if (conn->count_modes == 0) { + error("no valid mode for connector #%u\n", conn->connector_id); + return -EFAULT; + } + + /* configure mode information in our device structure */ + ret = set_env_connector_mode(conn, dev); + if (ret) { + error("no valid mode for connector #%u\n", conn->connector_id); + return ret; + } + debug("mode for connector #%u is %ux%u@%s\n", + conn->connector_id, dev->width, dev->height, dev->format->name); + + /* find a crtc for this connector */ + ret = drmprepare_crtc(fd, res, conn, dev); + if (ret) { + error("no valid crtc for connector #%u\n", conn->connector_id); + return ret; + } + + /* create a framebuffer for this CRTC */ + ret = modeset_create_fb(fd, dev); + if (ret) { + error("cannot create framebuffer for connector #%u\n", + conn->connector_id); + return ret; + } + + return 0; +} + +static int drmprepare(int fd) +{ + drmModeRes *res; + drmModeConnector *conn; + unsigned int i; + struct modeset_dev *dev; + int ret; + + /* retrieve resources */ + res = drmModeGetResources(fd); + if (!res) { + error("cannot retrieve DRM resources: %m\n"); + return -errno; + } + + debug("Found %d connectors\n", res->count_connectors); + + /* iterate all connectors */ + for (i = 0; i < res->count_connectors; ++i) { + /* get information for each connector */ + conn = drmModeGetConnector(fd, res->connectors[i]); + if (!conn) { + error("Cannot retrieve DRM connector #%u: %m\n", + res->connectors[i]); + continue; + } + assert(conn->connector_id == res->connectors[i]); + + debug("Connector #%u has type %s\n", conn->connector_id, + drmModeGetConnectorTypeName(conn->connector_type)); + + /* create a device structure */ + dev = malloc(sizeof(*dev)); + if (!dev) { + error("Cannot allocate memory for connector #%u: %m\n", + res->connectors[i]); + continue; + } + memset(dev, 0, sizeof(*dev)); + dev->conn_id = conn->connector_id; + + ret = drmprepare_connector(fd, res, conn, dev); + if (ret) { + if (ret != -ENOENT) { + error("Cannot setup device for connector #%u: %m\n", + res->connectors[i]); + } + free(dev); + drmModeFreeConnector(conn); + continue; + } + + /* free connector data and link device into global list */ + drmModeFreeConnector(conn); + dev->next = modeset_list; + modeset_list = dev; + } + + /* free resources again */ + drmModeFreeResources(res); + return 0; +} + + +static int drmfd; + +struct modeset_dev *init(void) +{ + static char drmdev[128]; + int ret = 0, i; + + for (i = 0; i < 64; i++) { + struct drm_mode_card_res res = {0}; + + /* + * XXX: Maybe use drmOpen instead? + * (Where should name/busid come from?) + * XXX: Loop through drm devices to find one with connectors. + */ + ret = snprintf(drmdev, sizeof(drmdev), DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret >= sizeof(drmdev)) { + error("Huh, device name overflowed buffer\n"); + goto execinit; + } + + drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); + if (drmfd < 0) { + error("Failed to open drm device: %m\n"); + goto execinit; + } + + ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); + if (ret < 0) { + close(drmfd); + continue; + } else { + /* Device found */ + break; + } + } + + if (i == 64) { + error("No suitable DRM device found\n"); + goto execinit; + } + + ret = drmprepare(drmfd); + if (ret) { + error("Failed to prepare DRM device\n"); + goto execinit; + } + + return modeset_list; + +execinit: + return NULL; +} + + +int update_display(struct modeset_dev *dev) { + int ret = 0; + + if (dev->setmode) { + ret = drmModeSetCrtc(drmfd, dev->crtc_id, dev->fb_id, 0, 0, &dev->conn_id, 1, &dev->mode); + if (ret) { + error("Cannot set CRTC for connector #%u: %m\n", dev->conn_id); + } + dev->setmode = 0; + } else { + ret = drmModePageFlip(drmfd, dev->crtc_id, dev->fb_id, 0, NULL); + if (ret) { + error("Page flip failed on connector #%u: %m\n", dev->conn_id); + } + } + return ret; +} + +int draw(struct modeset_dev *dev, const char *dir, const char *base) +{ + int ret = 0; + ret = draw_buffer(dev, dir, base); + if (ret) { + error("Failed to draw buffer\n"); + return ret; + } + return update_display(dev); +} + +int finish(void) { + int ret = drmDropMaster(drmfd); + if (ret) { + error("Failed to drop master on DRM device\n"); + } + + return ret; +} + +void deinit(void) { + struct modeset_dev *iter; + + for (iter = modeset_list; iter; iter = iter->next) { + if (iter->map) { + munmap(iter->map, iter->size); + } + if (iter->fb_id) { + drmModeRmFB(drmfd, iter->fb_id); + } + free(iter); + } +} diff --git a/platsch.h b/libplatsch.h similarity index 51% rename from platsch.h rename to libplatsch.h index dee22eb..87f6ef5 100644 --- a/platsch.h +++ b/libplatsch.h @@ -1,8 +1,27 @@ -#ifndef __PLATSCH_H__ -#define __PLATSCH_H__ +#ifndef __LIBPLATSCH_H__ +#define __LIBPLATSCH_H__ -#include <stdint.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> + +#include <xf86drm.h> #include <xf86drmMode.h> +#include <drm_fourcc.h> +#include "libplatsch.h" + #define debug(fmt, ...) printf("%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) #define error(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) @@ -17,7 +36,6 @@ struct platsch_format { struct modeset_dev { struct modeset_dev *next; - uint32_t width; uint32_t height; uint32_t stride; @@ -25,7 +43,6 @@ struct modeset_dev { const struct platsch_format *format; uint32_t handle; void *map; - bool setmode; drmModeModeInfo mode; uint32_t fb_id; @@ -34,14 +51,24 @@ struct modeset_dev { }; ssize_t readfull(int fd, void *buf, size_t count); +struct modeset_dev * init(void); -#ifdef HAVE_CAIRO -int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base); -#else +int draw(struct modeset_dev *dev, const char *dir, const char *base); +int finish(void); +int update_display(struct modeset_dev *dev); + +#ifndef HAVE_CAIRO static inline int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) { + printf("cairo_draw_buffer do nothing %s %s\n", dir, base); return -ENOTSUP; } +#else + +#include <cairo.h> +int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base); +cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base); + #endif /* HAVE_CAIRO */ -#endif /* __PLATSCH_H__ */ +#endif /* __LIBPLATSCH_H__ */ diff --git a/meson.build b/meson.build index e7e8e80..8c15fb9 100644 --- a/meson.build +++ b/meson.build @@ -1,26 +1,35 @@ project('platsch', 'c') +have_cairo = get_option('HAVE_CAIRO') + # Define dependencies conditionally based on the HAVE_CAIRO option platsch_dep = [dependency('libdrm', required: true)] -sources = ['platsch.c'] +sources = ['libplatsch.c'] args = [] -have_cairo = get_option('HAVE_CAIRO') - if have_cairo platsch_dep += dependency('cairo', required: true) sources += 'cairo.c' args += ['-DHAVE_CAIRO'] endif +# Create a static library from libplatsch.c and optionally cairo.c +libplatsch = static_library('libplatsch', + sources, + dependencies: platsch_dep, + c_args: args, + install: true +) + # Define the headers -headers = ['platsch.h'] +headers = ['libplatsch.h', 'platsch.h'] -# Create the platsch executable +# Create the platsch executable, linking it statically with libplatsch executable('platsch', - sources, + 'platsch.c', dependencies: platsch_dep, c_args: args, + link_with: libplatsch, install: true, include_directories: include_directories('.') ) diff --git a/platsch.c b/platsch.c index a7e82a6..92123d8 100644 --- a/platsch.c +++ b/platsch.c @@ -35,16 +35,7 @@ #include <sys/mman.h> #include <fcntl.h> -#include <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include "platsch.h" - -static const struct platsch_format platsch_formats[] = { - { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ - { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, -}; +#include "libplatsch.h" void redirect_stdfd(void) { @@ -64,461 +55,6 @@ void redirect_stdfd(void) close(devnull); } -ssize_t readfull(int fd, void *buf, size_t count) -{ - ssize_t ret = 0, err; - - while (count > 0) { - err = read(fd, buf, count); - if (err < 0) - return err; - else if (err > 0) { - buf += err; - count -= err; - ret += err; - } else { - return ret; - } - } - - return ret; -} - -void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) -{ - int fd_src; - char filename[128]; - ssize_t size; - int ret; - - /* Try cairo draw first and fall back in case of failure. */ - ret = cairo_draw_buffer(dev, dir, base); - if (ret == 0) - return; - - /* - * make it easy and load a raw file in the right format instead of - * opening an (say) PNG and convert the image data to the right format. - */ - ret = snprintf(filename, sizeof(filename), - "%s/%s-%ux%u-%s.bin", - dir, base, dev->width, dev->height, dev->format->name); - if (ret >= sizeof(filename)) { - error("Failed to fit filename into buffer\n"); - return; - } - - fd_src = open(filename, O_RDONLY | O_CLOEXEC); - if (fd_src < 0) { - error("Failed to open %s: %m\n", filename); - return; - } - - size = readfull(fd_src, dev->map, dev->size); - if (size < dev->size) { - if (size < 0) - error("Failed to read from %s: %m\n", filename); - else - error("Could only read %zd/%u bytes from %s\n", - size, dev->size, filename); - } - - ret = close(fd_src); - if (ret < 0) { - /* Nothing we can do about this, so just warn */ - error("Failed to close image file\n"); - } - - return; -} - -static struct modeset_dev *modeset_list = NULL; - -static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) -{ - drmModeEncoder *enc; - unsigned int i, j; - int32_t crtc_id; - struct modeset_dev *iter; - - /* first try the currently connected encoder+crtc */ - if (conn->encoder_id) { - debug("connector #%d uses encoder #%d\n", conn->connector_id, - conn->encoder_id); - enc = drmModeGetEncoder(fd, conn->encoder_id); - assert(enc); - assert(enc->encoder_id == conn->encoder_id); - } else { - debug("connector #%d has no active encoder\n", - conn->connector_id); - enc = NULL; - dev->setmode = 1; - } - - if (enc) { - if (enc->crtc_id) { - crtc_id = enc->crtc_id; - assert(crtc_id >= 0); - - for (iter = modeset_list; iter; iter = iter->next) { - if (iter->crtc_id == crtc_id) { - crtc_id = -1; - break; - } - } - - if (crtc_id > 0) { - debug("encoder #%d uses crtc #%d\n", - enc->encoder_id, enc->crtc_id); - drmModeFreeEncoder(enc); - dev->crtc_id = crtc_id; - return 0; - } else { - debug("encoder #%d used crtc #%d, but that's in use\n", - enc->encoder_id, iter->crtc_id); - } - } else { - debug("encoder #%d doesn't have an active crtc\n", - enc->encoder_id); - } - - drmModeFreeEncoder(enc); - } - - /* If the connector is not currently bound to an encoder or if the - * encoder+crtc is already used by another connector (actually unlikely - * but let's be safe), iterate all other available encoders to find a - * matching CRTC. */ - for (i = 0; i < conn->count_encoders; ++i) { - enc = drmModeGetEncoder(fd, conn->encoders[i]); - if (!enc) { - error("Cannot retrieve encoder %u: %m\n", - conn->encoders[i]); - continue; - } - assert(enc->encoder_id == conn->encoders[i]); - - /* iterate all global CRTCs */ - for (j = 0; j < res->count_crtcs; ++j) { - /* check whether this CRTC works with the encoder */ - if (!(enc->possible_crtcs & (1 << j))) - continue; - - /* check that no other device already uses this CRTC */ - crtc_id = res->crtcs[j]; - for (iter = modeset_list; iter; iter = iter->next) { - if (iter->crtc_id == crtc_id) { - crtc_id = -1; - break; - } - } - - /* we have found a CRTC, so save it and return */ - if (crtc_id >= 0) { - debug("encoder #%d will use crtc #%d\n", - enc->encoder_id, crtc_id); - drmModeFreeEncoder(enc); - dev->crtc_id = crtc_id; - return 0; - } - - } - drmModeFreeEncoder(enc); - } - - error("Cannot find suitable CRTC for connector #%u\n", - conn->connector_id); - return -ENOENT; -} - -static int modeset_create_fb(int fd, struct modeset_dev *dev) -{ - struct drm_mode_create_dumb creq; - struct drm_mode_destroy_dumb dreq; - struct drm_mode_map_dumb mreq; - int ret; - - /* create dumb buffer */ - memset(&creq, 0, sizeof(creq)); - creq.width = dev->width; - creq.height = dev->height; - creq.bpp = dev->format->bpp; - ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); - if (ret < 0) { - error("Cannot create dumb buffer: %m\n"); - return -errno; - } - dev->stride = creq.pitch; - dev->size = creq.size; - dev->handle = creq.handle; - - /* create framebuffer object for the dumb-buffer */ - ret = drmModeAddFB2(fd, dev->width, dev->height, - dev->format->format, - (uint32_t[4]){ dev->handle, }, - (uint32_t[4]){ dev->stride, }, - (uint32_t[4]){ 0, }, - &dev->fb_id, 0); - if (ret) { - ret = -errno; - error("Cannot create framebuffer: %m\n"); - goto err_destroy; - } - - /* prepare buffer for memory mapping */ - memset(&mreq, 0, sizeof(mreq)); - mreq.handle = dev->handle; - ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); - if (ret) { - ret = -errno; - error("Cannot get mmap offset: %m\n"); - goto err_fb; - } - - /* perform actual memory mapping */ - dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, - fd, mreq.offset); - if (dev->map == MAP_FAILED) { - ret = -errno; - error("Cannot mmap dumb buffer: %m\n"); - goto err_fb; - } - - /* - * Clear the framebuffer. Normally it's overwritten later with some - * image data, but in case this fails, initialize to all-black. - */ - memset(dev->map, 0x0, dev->size); - - return 0; - -err_fb: - drmModeRmFB(fd, dev->fb_id); -err_destroy: - memset(&dreq, 0, sizeof(dreq)); - dreq.handle = dev->handle; - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); - return ret; -} - -/* Returns lowercase connector type names with '_' for '-' */ -static char *get_normalized_conn_type_name(uint32_t connector_type) -{ - int i; - const char *connector_name; - char *normalized_name; - - connector_name = drmModeGetConnectorTypeName(connector_type); - if (!connector_name) - return NULL; - - normalized_name = strdup(connector_name); - - for (i = 0; normalized_name[i]; i++) { - normalized_name[i] = tolower(normalized_name[i]); - if (normalized_name[i] == '-') - normalized_name[i] = '_'; - } - - return normalized_name; -} - -static const struct platsch_format *platsch_format_find(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) - if (!strcmp(platsch_formats[i].name, name)) - return &platsch_formats[i]; - - return NULL; -} - -static int set_env_connector_mode(drmModeConnector *conn, - struct modeset_dev *dev) -{ - int ret, i = 0; - u_int32_t width = 0, height = 0; - const char *mode; - char *connector_type_name, mode_env_name[32], fmt_specifier[32] = ""; - const struct platsch_format *format = NULL; - - connector_type_name = get_normalized_conn_type_name(conn->connector_type); - if (!connector_type_name) { - error("could not look up name for connector type %u\n", - conn->connector_type); - goto fallback; - } - - ret = snprintf(mode_env_name, sizeof(mode_env_name), "platsch_%s%u_mode", - connector_type_name, conn->connector_type_id); - free(connector_type_name); - if (ret >= sizeof(mode_env_name)) { - error("failed to fit platsch env mode variable name into buffer\n"); - return -EFAULT; - } - - /* check for connector mode configuration in environment */ - debug("looking up %s env variable\n", mode_env_name); - mode = getenv(mode_env_name); - if (!mode) - goto fallback; - - /* format suffix is optional */ - ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); - if (ret < 2) { - error("error while scanning %s for mode\n", mode_env_name); - return -EFAULT; - } - - /* use first mode matching given resolution */ - for (i = 0; i < conn->count_modes; i++) { - drmModeModeInfo mode = conn->modes[i]; - if (mode.hdisplay == width && mode.vdisplay == height) { - memcpy(&dev->mode, &mode, sizeof(dev->mode)); - dev->width = width; - dev->height = height; - break; - } - } - - if (i == conn->count_modes) { - error("no mode available matching %ux%u\n", width, height); - return -ENOENT; - } - - format = platsch_format_find(fmt_specifier); - if (!format) { - if (strlen(fmt_specifier)) - error("unknown format specifier %s\n", fmt_specifier); - goto fallback_format; - } - - dev->format = format; - - return 0; - -fallback: - memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); - dev->width = conn->modes[0].hdisplay; - dev->height = conn->modes[0].vdisplay; - debug("using default mode for connector #%u\n", conn->connector_id); - -fallback_format: - dev->format = &platsch_formats[0]; - debug("using default format %s for connector #%u\n", dev->format->name, - conn->connector_id); - - return 0; -} - -static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) -{ - int ret; - - /* check if a monitor is connected */ - if (conn->connection != DRM_MODE_CONNECTED) { - error("Ignoring unused connector #%u\n", conn->connector_id); - return -ENOENT; - } - - /* check if there is at least one valid mode */ - if (conn->count_modes == 0) { - error("no valid mode for connector #%u\n", conn->connector_id); - return -EFAULT; - } - - /* configure mode information in our device structure */ - ret = set_env_connector_mode(conn, dev); - if (ret) { - error("no valid mode for connector #%u\n", conn->connector_id); - return ret; - } - debug("mode for connector #%u is %ux%u@%s\n", - conn->connector_id, dev->width, dev->height, dev->format->name); - - /* find a crtc for this connector */ - ret = drmprepare_crtc(fd, res, conn, dev); - if (ret) { - error("no valid crtc for connector #%u\n", conn->connector_id); - return ret; - } - - /* create a framebuffer for this CRTC */ - ret = modeset_create_fb(fd, dev); - if (ret) { - error("cannot create framebuffer for connector #%u\n", - conn->connector_id); - return ret; - } - - return 0; -} - -static int drmprepare(int fd) -{ - drmModeRes *res; - drmModeConnector *conn; - unsigned int i; - struct modeset_dev *dev; - int ret; - - /* retrieve resources */ - res = drmModeGetResources(fd); - if (!res) { - error("cannot retrieve DRM resources: %m\n"); - return -errno; - } - - debug("Found %d connectors\n", res->count_connectors); - - /* iterate all connectors */ - for (i = 0; i < res->count_connectors; ++i) { - /* get information for each connector */ - conn = drmModeGetConnector(fd, res->connectors[i]); - if (!conn) { - error("Cannot retrieve DRM connector #%u: %m\n", - res->connectors[i]); - continue; - } - assert(conn->connector_id == res->connectors[i]); - - debug("Connector #%u has type %s\n", conn->connector_id, - drmModeGetConnectorTypeName(conn->connector_type)); - - /* create a device structure */ - dev = malloc(sizeof(*dev)); - if (!dev) { - error("Cannot allocate memory for connector #%u: %m\n", - res->connectors[i]); - continue; - } - memset(dev, 0, sizeof(*dev)); - dev->conn_id = conn->connector_id; - - ret = drmprepare_connector(fd, res, conn, dev); - if (ret) { - if (ret != -ENOENT) { - error("Cannot setup device for connector #%u: %m\n", - res->connectors[i]); - } - free(dev); - drmModeFreeConnector(conn); - continue; - } - - /* free connector data and link device into global list */ - drmModeFreeConnector(conn); - dev->next = modeset_list; - modeset_list = dev; - } - - /* free resources again */ - drmModeFreeResources(res); - return 0; -} static struct option longopts[] = { @@ -539,14 +75,14 @@ static void usage(const char *prog) int main(int argc, char *argv[]) { char **initsargv; - int drmfd; - char drmdev[128]; + //int drmfd; + //char drmdev[128]; struct modeset_dev *iter; bool pid1 = getpid() == 1; const char *dir = "/usr/share/platsch"; const char *base = "splash"; const char *env; - int ret = 0, c, i; + int ret = 0, c; env = getenv("platsch_directory"); if (env) @@ -582,67 +118,17 @@ int main(int argc, char *argv[]) } } - for (i = 0; i < 64; i++) { - struct drm_mode_card_res res = {0}; - - /* - * XXX: Maybe use drmOpen instead? - * (Where should name/busid come from?) - * XXX: Loop through drm devices to find one with connectors. - */ - ret = snprintf(drmdev, sizeof(drmdev), DRM_DEV_NAME, DRM_DIR_NAME, i); - if (ret >= sizeof(drmdev)) { - error("Huh, device name overflowed buffer\n"); - goto execinit; - } - - drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); - if (drmfd < 0) { - error("Failed to open drm device: %m\n"); - goto execinit; - } - - ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); - if (ret < 0) { - close(drmfd); - continue; - } else { - /* Device found */ - break; - } + struct modeset_dev *modeset_list = init(); + if (!modeset_list) { + error("Failed to initialize modeset\n"); + return EXIT_FAILURE; } - - ret = drmprepare(drmfd); - assert(!ret); - for (iter = modeset_list; iter; iter = iter->next) { - - /* draw first then set the mode */ - draw_buffer(iter, dir, base); - - if (iter->setmode) { - debug("set crtc\n"); - - ret = drmModeSetCrtc(drmfd, iter->crtc_id, iter->fb_id, - 0, 0, &iter->conn_id, 1, &iter->mode); - if (ret) - error("Cannot set CRTC for connector #%u: %m\n", - iter->conn_id); - } else { - debug("page flip\n"); - ret = drmModePageFlip(drmfd, iter->crtc_id, iter->fb_id, - 0, NULL); - if (ret) - error("Page flip failed on connector #%u: %m\n", - iter->conn_id); - } + draw(iter,dir,base); } - ret = drmDropMaster(drmfd); - if (ret) - error("Failed to drop master on drm device\n"); + finish(); -execinit: if (pid1) { ret = fork(); if (ret < 0) { -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [OSS-Tools] [PATCH platsch 3/3] Add spinner executable for boot animation and text show 2024-06-12 4:06 [OSS-Tools] [PATCH platsch 1/3] convert to meson build LI Qingwu 2024-06-12 4:06 ` [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch LI Qingwu @ 2024-06-12 4:06 ` LI Qingwu 1 sibling, 0 replies; 5+ messages in thread From: LI Qingwu @ 2024-06-12 4:06 UTC (permalink / raw) To: Qing-wu.Li, oss-tools This commit introduces a new executable, spinner, which supports two types of animations for boot sequences: 1 rotates square PNG images per frame 2 shows a sequence of square images from a strip of PNG images. it supports read configuration from a conf file. the default path of conf is /usr/share/platsch/spinner.conf the dir of the conf can be set via env platsch_directory here is an example of conf: backdrop="/usr/share/platsch/splash.png" symbol="/usr/share/platsch/Spinner.png" fps=20 frames=0 text="Now loading..." text_x=350 text_y=400 text_font="Sans" textsize=30 Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn> --- README.rst | 76 ++++++++++++- meson.build | 30 +++++- meson_options.txt | 1 + spinner.c | 266 ++++++++++++++++++++++++++++++++++++++++++++++ spinner.conf | 13 +++ spinner_conf.c | 66 ++++++++++++ spinner_conf.h | 39 +++++++ 7 files changed, 487 insertions(+), 4 deletions(-) create mode 100644 spinner.c create mode 100644 spinner.conf create mode 100644 spinner_conf.c create mode 100644 spinner_conf.h diff --git a/README.rst b/README.rst index 2af29e4..ee0da5f 100644 --- a/README.rst +++ b/README.rst @@ -45,7 +45,7 @@ RGB565 This generates a 1920x1080 splash image in ``RGB565`` format from a png file:: #!/bin/bash - magick \ + convert \ /path/to/source.png \ -resize 1920x1080\! \ -flip \ @@ -62,7 +62,7 @@ This generates a 1920x1080 splash image in ``XRGB8888`` format from a png file:: #!/bin/bash - magick \ + convert \ /path/to/source.png \ -resize 1920x1080\! \ -flip \ @@ -149,9 +149,42 @@ To cross-compile the project, use the following commands: .. code-block:: shell - meson build --cross-file=<path-to-meson-cross-file> + meson setup --cross-file=<path-to-meson-cross-file> ninja -C build +Here are sample cross commands: + +.. code-block:: shell + + meson setup ./build -DHAVE_CAIRO=true --cross-file ./meson.cross + ninja -C build + +Here is a sample cross file: + +.. code-block:: ini + + [binaries] + c = ${CC} + cpp = ${CXX} + cython = 'cython3' + ar = '${AR}' + nm = '${NM}' + strip = '${STRIP}' + readelf = '${READELF}' + objcopy = '${OBJCOPY}' + pkgconfig = '${pkgconfig}' + + [properties] + needs_exe_wrapper = true + + + [target_machine] + system = 'linux' + cpu_family = 'aarch64' + cpu = 'aarch64' + endian = 'little' + + Build options ------------- @@ -168,3 +201,40 @@ The following build options are available: - true, false - true - Enable Cairo support + * - SPINNER + - true, false + - false + - Enable spinner + +Spinner - Splash Screen with Animation +====================================== + +The `spinner` executable is designed to provide boot animations. It supports two types of animations: + +1. **Square PNG Rotation Animation**: Rotates a square PNG image. +2. **Sequence Move Rectangle Animation**: Displays a sequence of square images from a strip of PNG images. + +spinner Configuration +--------------------- + +The configuration for the `spinner` executable is read from a configuration file, +with a default path of `/usr/share/platsch/spinner.conf`. +The directory of the configuration file can be set via the `platsch_directory` environment variable. + +Example Configuration File +-------------------------- + +Here is an example of a configuration file (`spinner.conf`): + +.. code-block:: ini + + backdrop="/usr/share/platsch/splash.png" + symbol="/usr/share/platsch/Spinner.png" + fps=20 + # frames=0 means infinite + frames=0 + text="text to display" + text_x=350 + text_y=400 + text_font="Sans" + text_size=30 diff --git a/meson.build b/meson.build index 8c15fb9..f12b2da 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,13 @@ project('platsch', 'c') -have_cairo = get_option('HAVE_CAIRO') + +# If SPINNER is true, force HAVE_CAIRO to be true +if get_option('SPINNER') + message('SPINNER is enabled, forcing HAVE_CAIRO to be true') + have_cairo = true +else + have_cairo = get_option('HAVE_CAIRO') +endif # Define dependencies conditionally based on the HAVE_CAIRO option platsch_dep = [dependency('libdrm', required: true)] @@ -33,3 +40,24 @@ executable('platsch', install: true, include_directories: include_directories('.') ) + +# Create the spinner executable if SPINNER true +if get_option('SPINNER') + spinner_dep = [ + dependency('cairo', required: true), + dependency('libdrm', required: true) + ] + + spinner_src = [ + 'spinner.c', + 'spinner_conf.c' + ] + executable('spinner', + spinner_src, + dependencies: spinner_dep, + link_with: libplatsch, + c_args: args, + install: true, + include_directories: include_directories('.') + ) +endif \ No newline at end of file diff --git a/meson_options.txt b/meson_options.txt index 1adfef3..78c4fba 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('HAVE_CAIRO', type: 'boolean', value: true, description: 'Enable Cairo support') +option('SPINNER', type: 'boolean', value: false, description: 'Enable spinner') diff --git a/spinner.c b/spinner.c new file mode 100644 index 0000000..9a68be7 --- /dev/null +++ b/spinner.c @@ -0,0 +1,266 @@ +#include "libplatsch.h" +#include "spinner_conf.h" +#include <cairo.h> +#include <math.h> +#include <sys/time.h> + +typedef struct spinner { + cairo_format_t fmt; + cairo_surface_t *background_surface; + cairo_surface_t *icon_surface; + cairo_surface_t *image_surface; + cairo_surface_t *drawing_surface; + cairo_t *cr_background; + cairo_t *cr_drawing; + cairo_t *device_cr; + int background_height; + int background_width; + int display_height; + int display_width; + int icon_height; + int icon_width; + struct modeset_dev *dev; + struct spinner *next; +} spinner_t; + +void on_draw_Sequence_animation(cairo_t *cr, spinner_t *data) +{ + static int current_frame; + int num_frames = data->icon_width / data->icon_height; + int frame_width = data->icon_height; + + cairo_set_source_surface(cr, data->background_surface, 0, 0); + cairo_paint(cr); + + cairo_save(cr); + + cairo_translate(cr, data->display_width / 2, data->display_height / 2); + + cairo_set_source_surface(cr, data->icon_surface, + -frame_width / 2 - current_frame * frame_width, + -frame_width / 2); + + cairo_rectangle(cr, -frame_width / 2, -frame_width / 2, + frame_width, frame_width); + cairo_clip(cr); + cairo_paint(cr); + + cairo_restore(cr); + + current_frame = (current_frame + 1) % num_frames; +} + +void on_draw_rotation_animation(cairo_t *cr, spinner_t *data) +{ + static float angle = 0.0; + + cairo_set_source_surface(cr, data->background_surface, 0, 0); + cairo_paint(cr); + cairo_save(cr); + cairo_translate(cr, data->background_width / 2, data->background_height / 2); + cairo_rotate(cr, angle); + cairo_translate(cr, -data->icon_width / 2, -data->icon_height / 2); + cairo_set_source_surface(cr, data->icon_surface, 0, 0); + cairo_paint(cr); + cairo_restore(cr); + angle += 0.1; + if (angle > 2 * M_PI) + angle = 0.0; +} + +int main(int argc, char *argv[]) +{ + bool pid1 = getpid() == 1; + char filename[128]; + Config config = DEFAULT_CONFIG; + const char *base = "splash"; + const char *dir = "/usr/share/platsch"; + const char *env; + int frames; + int ret; + long elapsed_time; + + spinner_t *spinner_list = NULL, *spinner_node = NULL, *spinner_iter = NULL; + struct modeset_dev *iter; + struct timeval start, end; + + env = getenv("platsch_directory"); + if (env) + dir = env; + + env = getenv("platsch_basename"); + if (env) + base = env; + + ret = snprintf(filename, sizeof(filename), "%s/spinner.conf", dir); + if (ret >= sizeof(filename)) { + error("Failed to fit filename\n"); + return EXIT_FAILURE; + } + + parseConfig(filename, &config); + + struct modeset_dev *modeset_list = init(); + + if (!modeset_list) { + fprintf(stderr, "Failed to initialize modeset\n"); + return EXIT_FAILURE; + } + + for (iter = modeset_list; iter; iter = iter->next) { + spinner_node = (spinner_t *)malloc(sizeof(spinner_t)); + if (!spinner_node) { + fprintf(stderr, "Failed to allocate memory for spinner_node\n"); + return EXIT_FAILURE; + } + memset(spinner_node, 0, sizeof(*spinner_node)); + printf("spinner_node=%p\n", spinner_node); + + spinner_node->device_cr = cairo_init(iter, dir, base); + if (!spinner_node->device_cr) + return EXIT_FAILURE; + + cairo_surface_t *surface = cairo_get_target(spinner_node->device_cr); + + if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, "Failed to get cairo surface\n"); + return EXIT_FAILURE; + } + spinner_node->display_width = cairo_image_surface_get_width(surface); + spinner_node->display_height = cairo_image_surface_get_height(surface); + spinner_node->fmt = cairo_image_surface_get_format(surface); + + spinner_node->background_surface = cairo_image_surface_create( + spinner_node->fmt, + spinner_node->display_width, + spinner_node->display_height); + if (cairo_surface_status(spinner_node->background_surface) + != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, "Failed to load splash.png\n"); + return EXIT_FAILURE; + } + + spinner_node->image_surface = cairo_image_surface_create_from_png(config.backdrop); + if (cairo_surface_status(spinner_node->image_surface) != CAIRO_STATUS_SUCCESS) { + error("Failed to create cairo surface from %s\n", config.backdrop); + return EXIT_FAILURE; + } + + int image_width = cairo_image_surface_get_width(spinner_node->image_surface); + int image_height = cairo_image_surface_get_height(spinner_node->image_surface); + double scale_x = (double)spinner_node->display_width / image_width; + double scale_y = (double)spinner_node->display_height / image_height; + + spinner_node->cr_background = cairo_create(spinner_node->background_surface); + cairo_scale(spinner_node->cr_background, scale_x, scale_y); + cairo_set_source_surface(spinner_node->cr_background, + spinner_node->image_surface, 0, 0); + + cairo_paint(spinner_node->cr_background); + + cairo_select_font_face(spinner_node->cr_background, config.text_font, + CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + cairo_set_font_size(spinner_node->cr_background, (double)config.text_size); + cairo_set_source_rgb(spinner_node->cr_background, 0, 0, 0); + cairo_move_to(spinner_node->cr_background, config.text_x, config.text_y); + cairo_show_text(spinner_node->cr_background, config.text); + + spinner_node->background_width = cairo_image_surface_get_width( + spinner_node->background_surface); + spinner_node->background_height = cairo_image_surface_get_height( + spinner_node->background_surface); + printf("spinner_node->background_width=%d, spinner_node->background_height=%d\n", + spinner_node->background_width, spinner_node->background_height); + + spinner_node->icon_surface = cairo_image_surface_create_from_png(config.symbol); + if (cairo_surface_status(spinner_node->icon_surface) != CAIRO_STATUS_SUCCESS) { + fprintf(stderr, "Failed to load %s\n", config.symbol); + return EXIT_FAILURE; + } + spinner_node->icon_width = cairo_image_surface_get_width( + spinner_node->icon_surface); + spinner_node->icon_height = cairo_image_surface_get_height( + spinner_node->icon_surface); + printf("spinner_node->icon_width=%d, spinner_node->icon_height=%d\n", + spinner_node->icon_width, spinner_node->icon_height); + + spinner_node->drawing_surface = cairo_image_surface_create( + spinner_node->fmt, + spinner_node->display_width, + spinner_node->display_height); + if (cairo_surface_status(spinner_node->drawing_surface) != CAIRO_STATUS_SUCCESS) { + error("Failed to create drawing surface\n"); + return EXIT_FAILURE; + } + spinner_node->cr_drawing = cairo_create(spinner_node->drawing_surface); + + cairo_set_source_surface( + spinner_node->device_cr, + spinner_node->drawing_surface, 0, 0); + update_display(iter); + + spinner_node->next = spinner_list; + spinner_list = spinner_node; + } + + if (pid1) { + char **initsargv; + + ret = fork(); + printf("fork ret=%d\n", ret); + if (ret < 0) + error("failed to fork for init: %m\n"); + else if (ret == 0) + goto drawing; + + initsargv = calloc(sizeof(argv[0]), argc + 1); + + if (!initsargv) { + error("failed to allocate argv for init\n"); + return EXIT_FAILURE; + } + memcpy(initsargv, argv, argc * sizeof(argv[0])); + initsargv[0] = "/sbin/init"; + initsargv[argc] = NULL; + + execv("/sbin/init", initsargv); + + error("failed to exec init: %m\n"); + + return EXIT_FAILURE; + } + +drawing: + printf("drawing\n"); + frames = config.frames; + if (config.frames == 0) + frames = 1; + + while (frames) { + gettimeofday(&start, NULL); + for (spinner_iter = spinner_list; spinner_iter; spinner_iter = spinner_iter->next) { + if (spinner_node->icon_width / spinner_node->icon_height > 2) + on_draw_Sequence_animation(spinner_iter->cr_drawing, spinner_iter); + else + on_draw_rotation_animation(spinner_iter->cr_drawing, spinner_iter); + + cairo_set_source_surface( + spinner_iter->device_cr, + spinner_iter->drawing_surface, 0, 0); + cairo_paint(spinner_iter->device_cr); + } + gettimeofday(&end, NULL); + elapsed_time = (end.tv_sec - start.tv_sec) * 1000000 + + (end.tv_usec - start.tv_usec); + + long sleep_time = (1000000 / config.fps) - elapsed_time; + + if (sleep_time > 0) + usleep(sleep_time); + + if (config.frames > 0) + frames--; + } + + return 0; +} diff --git a/spinner.conf b/spinner.conf new file mode 100644 index 0000000..c95a850 --- /dev/null +++ b/spinner.conf @@ -0,0 +1,13 @@ + +backdrop="/mnt/data/platsch/splash.png" +#symbol="/mnt/data/platsch/Spider.png" +symbol="/mnt/data/platsch/Spinner.png" + +fps=1 +#frames=0 for infinite +frames=0 +text="hello" +text_x=350 +text_y=400 +text_font="Sans" +text_size=30 diff --git a/spinner_conf.c b/spinner_conf.c new file mode 100644 index 0000000..c50a08c --- /dev/null +++ b/spinner_conf.c @@ -0,0 +1,66 @@ +#include "spinner_conf.h" +#include <errno.h> +#include <string.h> + +int parseConfig(const char *filename, Config *config) +{ + FILE *file; + char line[MAX_LINE_LENGTH*2]; + char key[MAX_LINE_LENGTH]; + char value[MAX_LINE_LENGTH+1]; + char *value_start; + char *value_end; + + file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Unable to open file: %s\n", filename); + return -EFAULT; + } + + while (fgets(line, sizeof(line), file)) { + if (strlen(line) > MAX_LINE_LENGTH) { + fprintf(stderr, "conf string too long\n"); + continue; + } + if (line[0] != '#' && sscanf(line, "%[^=]=%[^\n]", key, value) == 2) { + value_start = strchr(line, '=') + 1; + value_end = line + strlen(line) - 1; + + while (isspace(*value_start)) value_start++; + while (isspace(*value_end) || *value_end == '"') value_end--; + + if (*value_start == '"') { + value_start++; + if (*value_end == '"') value_end--; + } + + strncpy(value, value_start, value_end - value_start + 1); + value[value_end - value_start + 1] = '\0'; + value[sizeof(value) - 1] = '\0'; + + if (strcmp(key, "backdrop") == 0) { + strncpy(config->backdrop, value, MAX_LINE_LENGTH); + config->backdrop[sizeof(config->backdrop) - 1] = '\0'; + } else if (strcmp(key, "symbol") == 0) { + strncpy(config->symbol, value, MAX_LINE_LENGTH); + config->symbol[sizeof(config->symbol) - 1] = '\0'; + } else if (strcmp(key, "fps") == 0) { + config->fps = atoi(value); + } else if (strcmp(key, "frames") == 0) { + config->frames = atoi(value); + } else if (strcmp(key, "text_x") == 0) { + config->text_x = atoi(value); + } else if (strcmp(key, "text_y") == 0) { + config->text_y = atoi(value); + } else if (strcmp(key, "text_font") == 0) { + strncpy(config->text_font, value, MAX_LINE_LENGTH); + config->text_font[sizeof(config->text_font) - 1] = '\0'; + } else if (strcmp(key, "text_size") == 0) { + config->text_size = atoi(value); + } + } + } + + fclose(file); + return 0; +} diff --git a/spinner_conf.h b/spinner_conf.h new file mode 100644 index 0000000..e797f3e --- /dev/null +++ b/spinner_conf.h @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#ifndef __SPINNER_CONF_H__ +#define __SPINNER_CONF_H__ + + +#define MAX_LINE_LENGTH 128 + +typedef struct { + char backdrop[MAX_LINE_LENGTH]; + char symbol[MAX_LINE_LENGTH]; + char type[MAX_LINE_LENGTH]; + int fps; + int frames; + int text_x; + int text_y; + char text_font[MAX_LINE_LENGTH]; + int text_size; + char text[MAX_LINE_LENGTH]; +} Config; + +int parseConfig(const char *filename, Config *config); + +#define DEFAULT_CONFIG { \ + .backdrop = "/usr/share/platsch/splash.png", \ + .symbol = "/usr/share/platsch/spinner.png", \ + .type = "Rotation", \ + .fps = 20, \ + .frames = 0, \ + .text_x = 100, \ + .text_y = 100, \ + .text_font = "Sans", \ + .text_size = 30, \ + .text = "Now loading..." \ +} +#endif -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [OSS-Tools] [PATCH platsch 1/3] convert to meson build @ 2024-06-12 7:53 LI Qingwu 2024-06-12 7:53 ` [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch LI Qingwu 0 siblings, 1 reply; 5+ messages in thread From: LI Qingwu @ 2024-06-12 7:53 UTC (permalink / raw) To: Qing-wu.Li, oss-tools, m.felsch Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn> --- Makefile.am | 29 ----------------------------- README.rst | 27 +++++++++++++++++++++++++++ cairo.c | 4 ++-- configure.ac | 32 -------------------------------- meson.build | 26 ++++++++++++++++++++++++++ meson_options.txt | 1 + 6 files changed, 56 insertions(+), 63 deletions(-) delete mode 100644 Makefile.am delete mode 100644 configure.ac create mode 100644 meson.build create mode 100644 meson_options.txt diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index d486994..0000000 --- a/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -EXTRA_DIST = README.rst LICENSE - -sbin_PROGRAMS = platsch - -platsch_SOURCES = platsch.c -platsch_CFLAGS = $(LIBDRM_CFLAGS) -platsch_LDADD = $(LIBDRM_LIBS) - -if ENABLE_CAIRO -platsch_SOURCES += cairo.c -platsch_CFLAGS += $(CAIRO_CFLAGS) -platsch_LDADD += $(CAIRO_LIBS) -endif - -CLEANFILES = \ - $(DIST_ARCHIVES) - -DISTCLEAN = \ - config.log \ - config.status \ - Makefile - -MAINTAINERCLEANFILES = \ - aclocal.m4 \ - configure \ - depcomp \ - install-sh \ - Makefile.in \ - missing diff --git a/README.rst b/README.rst index e318120..2af29e4 100644 --- a/README.rst +++ b/README.rst @@ -141,3 +141,30 @@ By adding a Signed-off-by line (e.g. using ``git commit -s``) saying:: (using your real name and e-mail address), you state that your contributions are in line with the DCO. + +Cross compiling instructions +---------------------------- + +To cross-compile the project, use the following commands: + +.. code-block:: shell + + meson build --cross-file=<path-to-meson-cross-file> + ninja -C build + +Build options +------------- + +The following build options are available: + +.. list-table:: + :header-rows: 1 + + * - Option name + - Values + - Default + - Notes + * - HAVE_CAIRO + - true, false + - true + - Enable Cairo support diff --git a/cairo.c b/cairo.c index d29b3a4..8ced3b2 100644 --- a/cairo.c +++ b/cairo.c @@ -79,7 +79,7 @@ static const char *image_format_to_string(cairo_format_t format) return "RGB128F"; #endif case CAIRO_FORMAT_INVALID: - defaul: + default: return "invalid"; } } @@ -90,7 +90,6 @@ static int png_import_backend_import_picture(cairo_t *cr, const char *filename) cairo_format_t image_fmt, surface_fmt; cairo_surface_t *image, *surface; cairo_status_t status; - cairo_format_t format; int ret = 0; image = cairo_image_surface_create_from_png(filename); @@ -253,6 +252,7 @@ static uint32_t convert_to_cairo_format(uint32_t format) case DRM_FORMAT_XRGB8888: return CAIRO_FORMAT_ARGB32; } + return CAIRO_FORMAT_INVALID; } static cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 11cdded..0000000 --- a/configure.ac +++ /dev/null @@ -1,32 +0,0 @@ -AC_PREREQ([2.69]) -AC_INIT([platsch], [2019.12.0], [oss-tools@pengutronix.de]) -AC_CONFIG_SRCDIR([platsch.c]) -AM_INIT_AUTOMAKE([foreign dist-xz]) - -AC_PROG_CC -AC_PROG_MAKE_SET - -PKG_CHECK_MODULES([LIBDRM], [libdrm >= 2.4.112]) - -AC_ARG_ENABLE([cairo], - [AS_HELP_STRING([--disable-cairo], - [Enable CAIRO support @<:@default=check@:>@])], - [], - [enable_cairo=check]) - -AS_IF([test "$enable_cairo" != "no"], - [PKG_CHECK_MODULES([CAIRO], - [cairo >= 1.0], - [enable_cairo=yes - AC_DEFINE(HAVE_CAIRO)], - [AS_IF([test "$enable_cairo" = "yes"], - [AC_MSG_ERROR([cairo required, but not found.])], - [enable_cairo=no]) - ]) - ]) - -AM_CONDITIONAL([ENABLE_CAIRO], [test "$enable_cairo" = "yes"]) - -AC_CONFIG_FILES([Makefile]) - -AC_OUTPUT diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..e7e8e80 --- /dev/null +++ b/meson.build @@ -0,0 +1,26 @@ +project('platsch', 'c') + +# Define dependencies conditionally based on the HAVE_CAIRO option +platsch_dep = [dependency('libdrm', required: true)] +sources = ['platsch.c'] +args = [] + +have_cairo = get_option('HAVE_CAIRO') + +if have_cairo + platsch_dep += dependency('cairo', required: true) + sources += 'cairo.c' + args += ['-DHAVE_CAIRO'] +endif + +# Define the headers +headers = ['platsch.h'] + +# Create the platsch executable +executable('platsch', + sources, + dependencies: platsch_dep, + c_args: args, + install: true, + include_directories: include_directories('.') +) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..1adfef3 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1 @@ +option('HAVE_CAIRO', type: 'boolean', value: true, description: 'Enable Cairo support') -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch 2024-06-12 7:53 [OSS-Tools] [PATCH platsch 1/3] convert to meson build LI Qingwu @ 2024-06-12 7:53 ` LI Qingwu 2024-06-12 8:09 ` Marco Felsch 0 siblings, 1 reply; 5+ messages in thread From: LI Qingwu @ 2024-06-12 7:53 UTC (permalink / raw) To: Qing-wu.Li, oss-tools, m.felsch Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn> --- cairo.c | 4 +- libplatsch.c | 614 ++++++++++++++++++++++++++++++++++++++ platsch.h => libplatsch.h | 45 ++- meson.build | 21 +- platsch.c | 534 +-------------------------------- 5 files changed, 677 insertions(+), 541 deletions(-) create mode 100644 libplatsch.c rename platsch.h => libplatsch.h (51%) diff --git a/cairo.c b/cairo.c index 8ced3b2..d651b7a 100644 --- a/cairo.c +++ b/cairo.c @@ -25,7 +25,7 @@ #include <drm_fourcc.h> #include <cairo.h> -#include "platsch.h" +#include "libplatsch.h" static struct cairo_ctx { struct modeset_dev *dev; @@ -255,7 +255,7 @@ static uint32_t convert_to_cairo_format(uint32_t format) return CAIRO_FORMAT_INVALID; } -static cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) +cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) { cairo_surface_t *surface; cairo_status_t status; diff --git a/libplatsch.c b/libplatsch.c new file mode 100644 index 0000000..9fceda4 --- /dev/null +++ b/libplatsch.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) 2019 Pengutronix, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> + * + * Permission to use, copy, modify, and/or distribute this software + * for any purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Some code parts base on example code written in 2012 by David Herrmann + * <dh.herrmann@googlemail.com> and dedicated to the Public Domain. It was found + * in 2019 on + * https://raw.githubusercontent.com/dvdhrm/docs/master/drm-howto/modeset.c + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> +#include <drm_fourcc.h> +#include "libplatsch.h" + + +static const struct platsch_format platsch_formats[] = { + { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ + { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, +}; + + +ssize_t readfull(int fd, void *buf, size_t count) +{ + ssize_t ret = 0, err; + + while (count > 0) { + err = read(fd, buf, count); + if (err < 0) + return err; + else if (err > 0) { + buf += err; + count -= err; + ret += err; + } else { + return ret; + } + } + + return ret; +} + +static int draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) +{ + int fd_src; + char filename[128]; + ssize_t size; + int ret; + + /* Try cairo draw first and fall back in case of failure. */ + ret = cairo_draw_buffer(dev, dir, base); + if (ret == 0) + return ret; + + /* + * make it easy and load a raw file in the right format instead of + * opening an (say) PNG and convert the image data to the right format. + */ + ret = snprintf(filename, sizeof(filename), + "%s/%s-%ux%u-%s.bin", + dir, base, dev->width, dev->height, dev->format->name); + if (ret >= sizeof(filename)) { + error("Failed to fit filename into buffer\n"); + return -EINVAL; + } + + fd_src = open(filename, O_RDONLY | O_CLOEXEC); + if (fd_src < 0) { + error("Failed to open %s: %m\n", filename); + return -ENOENT; + } + + size = readfull(fd_src, dev->map, dev->size); + if (size < dev->size) { + if (size < 0) + error("Failed to read from %s: %m\n", filename); + else + error("Could only read %zd/%u bytes from %s\n", + size, dev->size, filename); + return -EIO; + } + + ret = close(fd_src); + if (ret < 0) { + /* Nothing we can do about this, so just warn */ + error("Failed to close image file\n"); + return -EIO; + } + + return 0; +} + +static struct modeset_dev *modeset_list = NULL; + +static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, + struct modeset_dev *dev) +{ + drmModeEncoder *enc; + unsigned int i, j; + int32_t crtc_id; + struct modeset_dev *iter; + + /* first try the currently connected encoder+crtc */ + if (conn->encoder_id) { + debug("connector #%d uses encoder #%d\n", conn->connector_id, + conn->encoder_id); + enc = drmModeGetEncoder(fd, conn->encoder_id); + assert(enc); + assert(enc->encoder_id == conn->encoder_id); + } else { + debug("connector #%d has no active encoder\n", + conn->connector_id); + enc = NULL; + dev->setmode = 1; + } + + if (enc) { + if (enc->crtc_id) { + crtc_id = enc->crtc_id; + assert(crtc_id >= 0); + + for (iter = modeset_list; iter; iter = iter->next) { + if (iter->crtc_id == crtc_id) { + crtc_id = -1; + break; + } + } + + if (crtc_id > 0) { + debug("encoder #%d uses crtc #%d\n", + enc->encoder_id, enc->crtc_id); + drmModeFreeEncoder(enc); + dev->crtc_id = crtc_id; + return 0; + } else { + debug("encoder #%d used crtc #%d, but that's in use\n", + enc->encoder_id, iter->crtc_id); + } + } else { + debug("encoder #%d doesn't have an active crtc\n", + enc->encoder_id); + } + + drmModeFreeEncoder(enc); + } + + /* If the connector is not currently bound to an encoder or if the + * encoder+crtc is already used by another connector (actually unlikely + * but let's be safe), iterate all other available encoders to find a + * matching CRTC. */ + for (i = 0; i < conn->count_encoders; ++i) { + enc = drmModeGetEncoder(fd, conn->encoders[i]); + if (!enc) { + error("Cannot retrieve encoder %u: %m\n", + conn->encoders[i]); + continue; + } + assert(enc->encoder_id == conn->encoders[i]); + + /* iterate all global CRTCs */ + for (j = 0; j < res->count_crtcs; ++j) { + /* check whether this CRTC works with the encoder */ + if (!(enc->possible_crtcs & (1 << j))) + continue; + + /* check that no other device already uses this CRTC */ + crtc_id = res->crtcs[j]; + for (iter = modeset_list; iter; iter = iter->next) { + if (iter->crtc_id == crtc_id) { + crtc_id = -1; + break; + } + } + + /* we have found a CRTC, so save it and return */ + if (crtc_id >= 0) { + debug("encoder #%d will use crtc #%d\n", + enc->encoder_id, crtc_id); + drmModeFreeEncoder(enc); + dev->crtc_id = crtc_id; + return 0; + } + + } + drmModeFreeEncoder(enc); + } + + error("Cannot find suitable CRTC for connector #%u\n", + conn->connector_id); + return -ENOENT; +} + +static int modeset_create_fb(int fd, struct modeset_dev *dev) +{ + struct drm_mode_create_dumb creq; + struct drm_mode_destroy_dumb dreq; + struct drm_mode_map_dumb mreq; + int ret; + + /* create dumb buffer */ + memset(&creq, 0, sizeof(creq)); + creq.width = dev->width; + creq.height = dev->height; + creq.bpp = dev->format->bpp; + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); + if (ret < 0) { + error("Cannot create dumb buffer: %m\n"); + return -errno; + } + dev->stride = creq.pitch; + dev->size = creq.size; + dev->handle = creq.handle; + + /* create framebuffer object for the dumb-buffer */ + ret = drmModeAddFB2(fd, dev->width, dev->height, + dev->format->format, + (uint32_t[4]){ dev->handle, }, + (uint32_t[4]){ dev->stride, }, + (uint32_t[4]){ 0, }, + &dev->fb_id, 0); + if (ret) { + ret = -errno; + error("Cannot create framebuffer: %m\n"); + goto err_destroy; + } + + /* prepare buffer for memory mapping */ + memset(&mreq, 0, sizeof(mreq)); + mreq.handle = dev->handle; + ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); + if (ret) { + ret = -errno; + error("Cannot get mmap offset: %m\n"); + goto err_fb; + } + + /* perform actual memory mapping */ + dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, mreq.offset); + if (dev->map == MAP_FAILED) { + ret = -errno; + error("Cannot mmap dumb buffer: %m\n"); + goto err_fb; + } + + /* + * Clear the framebuffer. Normally it's overwritten later with some + * image data, but in case this fails, initialize to all-black. + */ + memset(dev->map, 0x0, dev->size); + + return 0; + +err_fb: + drmModeRmFB(fd, dev->fb_id); +err_destroy: + memset(&dreq, 0, sizeof(dreq)); + dreq.handle = dev->handle; + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); + return ret; +} + +/* Returns lowercase connector type names with '_' for '-' */ +static char *get_normalized_conn_type_name(uint32_t connector_type) +{ + int i; + const char *connector_name; + char *normalized_name; + + connector_name = drmModeGetConnectorTypeName(connector_type); + if (!connector_name) + return NULL; + + normalized_name = strdup(connector_name); + + for (i = 0; normalized_name[i]; i++) { + normalized_name[i] = tolower(normalized_name[i]); + if (normalized_name[i] == '-') + normalized_name[i] = '_'; + } + + return normalized_name; +} + +static const struct platsch_format *platsch_format_find(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) + if (!strcmp(platsch_formats[i].name, name)) + return &platsch_formats[i]; + + return NULL; +} + +static int set_env_connector_mode(drmModeConnector *conn, + struct modeset_dev *dev) +{ + int ret, i = 0; + u_int32_t width = 0, height = 0; + const char *mode; + char *connector_type_name, mode_env_name[32], fmt_specifier[32] = ""; + const struct platsch_format *format = NULL; + + connector_type_name = get_normalized_conn_type_name(conn->connector_type); + if (!connector_type_name) { + error("could not look up name for connector type %u\n", + conn->connector_type); + goto fallback; + } + + ret = snprintf(mode_env_name, sizeof(mode_env_name), "platsch_%s%u_mode", + connector_type_name, conn->connector_type_id); + free(connector_type_name); + if (ret >= sizeof(mode_env_name)) { + error("failed to fit platsch env mode variable name into buffer\n"); + return -EFAULT; + } + + /* check for connector mode configuration in environment */ + debug("looking up %s env variable\n", mode_env_name); + mode = getenv(mode_env_name); + if (!mode) + goto fallback; + + /* format suffix is optional */ + ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); + if (ret < 2) { + error("error while scanning %s for mode\n", mode_env_name); + return -EFAULT; + } + + /* use first mode matching given resolution */ + for (i = 0; i < conn->count_modes; i++) { + drmModeModeInfo mode = conn->modes[i]; + if (mode.hdisplay == width && mode.vdisplay == height) { + memcpy(&dev->mode, &mode, sizeof(dev->mode)); + dev->width = width; + dev->height = height; + break; + } + } + + if (i == conn->count_modes) { + error("no mode available matching %ux%u\n", width, height); + return -ENOENT; + } + + format = platsch_format_find(fmt_specifier); + if (!format) { + if (strlen(fmt_specifier)) + error("unknown format specifier %s\n", fmt_specifier); + goto fallback_format; + } + + dev->format = format; + + return 0; + +fallback: + memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); + dev->width = conn->modes[0].hdisplay; + dev->height = conn->modes[0].vdisplay; + debug("using default mode for connector #%u\n", conn->connector_id); + +fallback_format: + dev->format = &platsch_formats[0]; + debug("using default format %s for connector #%u\n", dev->format->name, + conn->connector_id); + + return 0; +} + +static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, + struct modeset_dev *dev) +{ + int ret; + + /* check if a monitor is connected */ + if (conn->connection != DRM_MODE_CONNECTED) { + error("Ignoring unused connector #%u\n", conn->connector_id); + return -ENOENT; + } + + /* check if there is at least one valid mode */ + if (conn->count_modes == 0) { + error("no valid mode for connector #%u\n", conn->connector_id); + return -EFAULT; + } + + /* configure mode information in our device structure */ + ret = set_env_connector_mode(conn, dev); + if (ret) { + error("no valid mode for connector #%u\n", conn->connector_id); + return ret; + } + debug("mode for connector #%u is %ux%u@%s\n", + conn->connector_id, dev->width, dev->height, dev->format->name); + + /* find a crtc for this connector */ + ret = drmprepare_crtc(fd, res, conn, dev); + if (ret) { + error("no valid crtc for connector #%u\n", conn->connector_id); + return ret; + } + + /* create a framebuffer for this CRTC */ + ret = modeset_create_fb(fd, dev); + if (ret) { + error("cannot create framebuffer for connector #%u\n", + conn->connector_id); + return ret; + } + + return 0; +} + +static int drmprepare(int fd) +{ + drmModeRes *res; + drmModeConnector *conn; + unsigned int i; + struct modeset_dev *dev; + int ret; + + /* retrieve resources */ + res = drmModeGetResources(fd); + if (!res) { + error("cannot retrieve DRM resources: %m\n"); + return -errno; + } + + debug("Found %d connectors\n", res->count_connectors); + + /* iterate all connectors */ + for (i = 0; i < res->count_connectors; ++i) { + /* get information for each connector */ + conn = drmModeGetConnector(fd, res->connectors[i]); + if (!conn) { + error("Cannot retrieve DRM connector #%u: %m\n", + res->connectors[i]); + continue; + } + assert(conn->connector_id == res->connectors[i]); + + debug("Connector #%u has type %s\n", conn->connector_id, + drmModeGetConnectorTypeName(conn->connector_type)); + + /* create a device structure */ + dev = malloc(sizeof(*dev)); + if (!dev) { + error("Cannot allocate memory for connector #%u: %m\n", + res->connectors[i]); + continue; + } + memset(dev, 0, sizeof(*dev)); + dev->conn_id = conn->connector_id; + + ret = drmprepare_connector(fd, res, conn, dev); + if (ret) { + if (ret != -ENOENT) { + error("Cannot setup device for connector #%u: %m\n", + res->connectors[i]); + } + free(dev); + drmModeFreeConnector(conn); + continue; + } + + /* free connector data and link device into global list */ + drmModeFreeConnector(conn); + dev->next = modeset_list; + modeset_list = dev; + } + + /* free resources again */ + drmModeFreeResources(res); + return 0; +} + + +static int drmfd; + +struct modeset_dev *init(void) +{ + static char drmdev[128]; + int ret = 0, i; + + for (i = 0; i < 64; i++) { + struct drm_mode_card_res res = {0}; + + /* + * XXX: Maybe use drmOpen instead? + * (Where should name/busid come from?) + * XXX: Loop through drm devices to find one with connectors. + */ + ret = snprintf(drmdev, sizeof(drmdev), DRM_DEV_NAME, DRM_DIR_NAME, i); + if (ret >= sizeof(drmdev)) { + error("Huh, device name overflowed buffer\n"); + goto execinit; + } + + drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); + if (drmfd < 0) { + error("Failed to open drm device: %m\n"); + goto execinit; + } + + ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); + if (ret < 0) { + close(drmfd); + continue; + } else { + /* Device found */ + break; + } + } + + if (i == 64) { + error("No suitable DRM device found\n"); + goto execinit; + } + + ret = drmprepare(drmfd); + if (ret) { + error("Failed to prepare DRM device\n"); + goto execinit; + } + + return modeset_list; + +execinit: + return NULL; +} + + +int update_display(struct modeset_dev *dev) { + int ret = 0; + + if (dev->setmode) { + ret = drmModeSetCrtc(drmfd, dev->crtc_id, dev->fb_id, 0, 0, &dev->conn_id, 1, &dev->mode); + if (ret) { + error("Cannot set CRTC for connector #%u: %m\n", dev->conn_id); + } + dev->setmode = 0; + } else { + ret = drmModePageFlip(drmfd, dev->crtc_id, dev->fb_id, 0, NULL); + if (ret) { + error("Page flip failed on connector #%u: %m\n", dev->conn_id); + } + } + return ret; +} + +int draw(struct modeset_dev *dev, const char *dir, const char *base) +{ + int ret = 0; + ret = draw_buffer(dev, dir, base); + if (ret) { + error("Failed to draw buffer\n"); + return ret; + } + return update_display(dev); +} + +int finish(void) { + int ret = drmDropMaster(drmfd); + if (ret) { + error("Failed to drop master on DRM device\n"); + } + + return ret; +} + +void deinit(void) { + struct modeset_dev *iter; + + for (iter = modeset_list; iter; iter = iter->next) { + if (iter->map) { + munmap(iter->map, iter->size); + } + if (iter->fb_id) { + drmModeRmFB(drmfd, iter->fb_id); + } + free(iter); + } +} diff --git a/platsch.h b/libplatsch.h similarity index 51% rename from platsch.h rename to libplatsch.h index dee22eb..87f6ef5 100644 --- a/platsch.h +++ b/libplatsch.h @@ -1,8 +1,27 @@ -#ifndef __PLATSCH_H__ -#define __PLATSCH_H__ +#ifndef __LIBPLATSCH_H__ +#define __LIBPLATSCH_H__ -#include <stdint.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <getopt.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> + +#include <xf86drm.h> #include <xf86drmMode.h> +#include <drm_fourcc.h> +#include "libplatsch.h" + #define debug(fmt, ...) printf("%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) #define error(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) @@ -17,7 +36,6 @@ struct platsch_format { struct modeset_dev { struct modeset_dev *next; - uint32_t width; uint32_t height; uint32_t stride; @@ -25,7 +43,6 @@ struct modeset_dev { const struct platsch_format *format; uint32_t handle; void *map; - bool setmode; drmModeModeInfo mode; uint32_t fb_id; @@ -34,14 +51,24 @@ struct modeset_dev { }; ssize_t readfull(int fd, void *buf, size_t count); +struct modeset_dev * init(void); -#ifdef HAVE_CAIRO -int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base); -#else +int draw(struct modeset_dev *dev, const char *dir, const char *base); +int finish(void); +int update_display(struct modeset_dev *dev); + +#ifndef HAVE_CAIRO static inline int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) { + printf("cairo_draw_buffer do nothing %s %s\n", dir, base); return -ENOTSUP; } +#else + +#include <cairo.h> +int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base); +cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base); + #endif /* HAVE_CAIRO */ -#endif /* __PLATSCH_H__ */ +#endif /* __LIBPLATSCH_H__ */ diff --git a/meson.build b/meson.build index e7e8e80..8c15fb9 100644 --- a/meson.build +++ b/meson.build @@ -1,26 +1,35 @@ project('platsch', 'c') +have_cairo = get_option('HAVE_CAIRO') + # Define dependencies conditionally based on the HAVE_CAIRO option platsch_dep = [dependency('libdrm', required: true)] -sources = ['platsch.c'] +sources = ['libplatsch.c'] args = [] -have_cairo = get_option('HAVE_CAIRO') - if have_cairo platsch_dep += dependency('cairo', required: true) sources += 'cairo.c' args += ['-DHAVE_CAIRO'] endif +# Create a static library from libplatsch.c and optionally cairo.c +libplatsch = static_library('libplatsch', + sources, + dependencies: platsch_dep, + c_args: args, + install: true +) + # Define the headers -headers = ['platsch.h'] +headers = ['libplatsch.h', 'platsch.h'] -# Create the platsch executable +# Create the platsch executable, linking it statically with libplatsch executable('platsch', - sources, + 'platsch.c', dependencies: platsch_dep, c_args: args, + link_with: libplatsch, install: true, include_directories: include_directories('.') ) diff --git a/platsch.c b/platsch.c index a7e82a6..92123d8 100644 --- a/platsch.c +++ b/platsch.c @@ -35,16 +35,7 @@ #include <sys/mman.h> #include <fcntl.h> -#include <xf86drm.h> -#include <xf86drmMode.h> -#include <drm_fourcc.h> - -#include "platsch.h" - -static const struct platsch_format platsch_formats[] = { - { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ - { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, -}; +#include "libplatsch.h" void redirect_stdfd(void) { @@ -64,461 +55,6 @@ void redirect_stdfd(void) close(devnull); } -ssize_t readfull(int fd, void *buf, size_t count) -{ - ssize_t ret = 0, err; - - while (count > 0) { - err = read(fd, buf, count); - if (err < 0) - return err; - else if (err > 0) { - buf += err; - count -= err; - ret += err; - } else { - return ret; - } - } - - return ret; -} - -void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) -{ - int fd_src; - char filename[128]; - ssize_t size; - int ret; - - /* Try cairo draw first and fall back in case of failure. */ - ret = cairo_draw_buffer(dev, dir, base); - if (ret == 0) - return; - - /* - * make it easy and load a raw file in the right format instead of - * opening an (say) PNG and convert the image data to the right format. - */ - ret = snprintf(filename, sizeof(filename), - "%s/%s-%ux%u-%s.bin", - dir, base, dev->width, dev->height, dev->format->name); - if (ret >= sizeof(filename)) { - error("Failed to fit filename into buffer\n"); - return; - } - - fd_src = open(filename, O_RDONLY | O_CLOEXEC); - if (fd_src < 0) { - error("Failed to open %s: %m\n", filename); - return; - } - - size = readfull(fd_src, dev->map, dev->size); - if (size < dev->size) { - if (size < 0) - error("Failed to read from %s: %m\n", filename); - else - error("Could only read %zd/%u bytes from %s\n", - size, dev->size, filename); - } - - ret = close(fd_src); - if (ret < 0) { - /* Nothing we can do about this, so just warn */ - error("Failed to close image file\n"); - } - - return; -} - -static struct modeset_dev *modeset_list = NULL; - -static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) -{ - drmModeEncoder *enc; - unsigned int i, j; - int32_t crtc_id; - struct modeset_dev *iter; - - /* first try the currently connected encoder+crtc */ - if (conn->encoder_id) { - debug("connector #%d uses encoder #%d\n", conn->connector_id, - conn->encoder_id); - enc = drmModeGetEncoder(fd, conn->encoder_id); - assert(enc); - assert(enc->encoder_id == conn->encoder_id); - } else { - debug("connector #%d has no active encoder\n", - conn->connector_id); - enc = NULL; - dev->setmode = 1; - } - - if (enc) { - if (enc->crtc_id) { - crtc_id = enc->crtc_id; - assert(crtc_id >= 0); - - for (iter = modeset_list; iter; iter = iter->next) { - if (iter->crtc_id == crtc_id) { - crtc_id = -1; - break; - } - } - - if (crtc_id > 0) { - debug("encoder #%d uses crtc #%d\n", - enc->encoder_id, enc->crtc_id); - drmModeFreeEncoder(enc); - dev->crtc_id = crtc_id; - return 0; - } else { - debug("encoder #%d used crtc #%d, but that's in use\n", - enc->encoder_id, iter->crtc_id); - } - } else { - debug("encoder #%d doesn't have an active crtc\n", - enc->encoder_id); - } - - drmModeFreeEncoder(enc); - } - - /* If the connector is not currently bound to an encoder or if the - * encoder+crtc is already used by another connector (actually unlikely - * but let's be safe), iterate all other available encoders to find a - * matching CRTC. */ - for (i = 0; i < conn->count_encoders; ++i) { - enc = drmModeGetEncoder(fd, conn->encoders[i]); - if (!enc) { - error("Cannot retrieve encoder %u: %m\n", - conn->encoders[i]); - continue; - } - assert(enc->encoder_id == conn->encoders[i]); - - /* iterate all global CRTCs */ - for (j = 0; j < res->count_crtcs; ++j) { - /* check whether this CRTC works with the encoder */ - if (!(enc->possible_crtcs & (1 << j))) - continue; - - /* check that no other device already uses this CRTC */ - crtc_id = res->crtcs[j]; - for (iter = modeset_list; iter; iter = iter->next) { - if (iter->crtc_id == crtc_id) { - crtc_id = -1; - break; - } - } - - /* we have found a CRTC, so save it and return */ - if (crtc_id >= 0) { - debug("encoder #%d will use crtc #%d\n", - enc->encoder_id, crtc_id); - drmModeFreeEncoder(enc); - dev->crtc_id = crtc_id; - return 0; - } - - } - drmModeFreeEncoder(enc); - } - - error("Cannot find suitable CRTC for connector #%u\n", - conn->connector_id); - return -ENOENT; -} - -static int modeset_create_fb(int fd, struct modeset_dev *dev) -{ - struct drm_mode_create_dumb creq; - struct drm_mode_destroy_dumb dreq; - struct drm_mode_map_dumb mreq; - int ret; - - /* create dumb buffer */ - memset(&creq, 0, sizeof(creq)); - creq.width = dev->width; - creq.height = dev->height; - creq.bpp = dev->format->bpp; - ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); - if (ret < 0) { - error("Cannot create dumb buffer: %m\n"); - return -errno; - } - dev->stride = creq.pitch; - dev->size = creq.size; - dev->handle = creq.handle; - - /* create framebuffer object for the dumb-buffer */ - ret = drmModeAddFB2(fd, dev->width, dev->height, - dev->format->format, - (uint32_t[4]){ dev->handle, }, - (uint32_t[4]){ dev->stride, }, - (uint32_t[4]){ 0, }, - &dev->fb_id, 0); - if (ret) { - ret = -errno; - error("Cannot create framebuffer: %m\n"); - goto err_destroy; - } - - /* prepare buffer for memory mapping */ - memset(&mreq, 0, sizeof(mreq)); - mreq.handle = dev->handle; - ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); - if (ret) { - ret = -errno; - error("Cannot get mmap offset: %m\n"); - goto err_fb; - } - - /* perform actual memory mapping */ - dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, - fd, mreq.offset); - if (dev->map == MAP_FAILED) { - ret = -errno; - error("Cannot mmap dumb buffer: %m\n"); - goto err_fb; - } - - /* - * Clear the framebuffer. Normally it's overwritten later with some - * image data, but in case this fails, initialize to all-black. - */ - memset(dev->map, 0x0, dev->size); - - return 0; - -err_fb: - drmModeRmFB(fd, dev->fb_id); -err_destroy: - memset(&dreq, 0, sizeof(dreq)); - dreq.handle = dev->handle; - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); - return ret; -} - -/* Returns lowercase connector type names with '_' for '-' */ -static char *get_normalized_conn_type_name(uint32_t connector_type) -{ - int i; - const char *connector_name; - char *normalized_name; - - connector_name = drmModeGetConnectorTypeName(connector_type); - if (!connector_name) - return NULL; - - normalized_name = strdup(connector_name); - - for (i = 0; normalized_name[i]; i++) { - normalized_name[i] = tolower(normalized_name[i]); - if (normalized_name[i] == '-') - normalized_name[i] = '_'; - } - - return normalized_name; -} - -static const struct platsch_format *platsch_format_find(const char *name) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) - if (!strcmp(platsch_formats[i].name, name)) - return &platsch_formats[i]; - - return NULL; -} - -static int set_env_connector_mode(drmModeConnector *conn, - struct modeset_dev *dev) -{ - int ret, i = 0; - u_int32_t width = 0, height = 0; - const char *mode; - char *connector_type_name, mode_env_name[32], fmt_specifier[32] = ""; - const struct platsch_format *format = NULL; - - connector_type_name = get_normalized_conn_type_name(conn->connector_type); - if (!connector_type_name) { - error("could not look up name for connector type %u\n", - conn->connector_type); - goto fallback; - } - - ret = snprintf(mode_env_name, sizeof(mode_env_name), "platsch_%s%u_mode", - connector_type_name, conn->connector_type_id); - free(connector_type_name); - if (ret >= sizeof(mode_env_name)) { - error("failed to fit platsch env mode variable name into buffer\n"); - return -EFAULT; - } - - /* check for connector mode configuration in environment */ - debug("looking up %s env variable\n", mode_env_name); - mode = getenv(mode_env_name); - if (!mode) - goto fallback; - - /* format suffix is optional */ - ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); - if (ret < 2) { - error("error while scanning %s for mode\n", mode_env_name); - return -EFAULT; - } - - /* use first mode matching given resolution */ - for (i = 0; i < conn->count_modes; i++) { - drmModeModeInfo mode = conn->modes[i]; - if (mode.hdisplay == width && mode.vdisplay == height) { - memcpy(&dev->mode, &mode, sizeof(dev->mode)); - dev->width = width; - dev->height = height; - break; - } - } - - if (i == conn->count_modes) { - error("no mode available matching %ux%u\n", width, height); - return -ENOENT; - } - - format = platsch_format_find(fmt_specifier); - if (!format) { - if (strlen(fmt_specifier)) - error("unknown format specifier %s\n", fmt_specifier); - goto fallback_format; - } - - dev->format = format; - - return 0; - -fallback: - memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); - dev->width = conn->modes[0].hdisplay; - dev->height = conn->modes[0].vdisplay; - debug("using default mode for connector #%u\n", conn->connector_id); - -fallback_format: - dev->format = &platsch_formats[0]; - debug("using default format %s for connector #%u\n", dev->format->name, - conn->connector_id); - - return 0; -} - -static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, - struct modeset_dev *dev) -{ - int ret; - - /* check if a monitor is connected */ - if (conn->connection != DRM_MODE_CONNECTED) { - error("Ignoring unused connector #%u\n", conn->connector_id); - return -ENOENT; - } - - /* check if there is at least one valid mode */ - if (conn->count_modes == 0) { - error("no valid mode for connector #%u\n", conn->connector_id); - return -EFAULT; - } - - /* configure mode information in our device structure */ - ret = set_env_connector_mode(conn, dev); - if (ret) { - error("no valid mode for connector #%u\n", conn->connector_id); - return ret; - } - debug("mode for connector #%u is %ux%u@%s\n", - conn->connector_id, dev->width, dev->height, dev->format->name); - - /* find a crtc for this connector */ - ret = drmprepare_crtc(fd, res, conn, dev); - if (ret) { - error("no valid crtc for connector #%u\n", conn->connector_id); - return ret; - } - - /* create a framebuffer for this CRTC */ - ret = modeset_create_fb(fd, dev); - if (ret) { - error("cannot create framebuffer for connector #%u\n", - conn->connector_id); - return ret; - } - - return 0; -} - -static int drmprepare(int fd) -{ - drmModeRes *res; - drmModeConnector *conn; - unsigned int i; - struct modeset_dev *dev; - int ret; - - /* retrieve resources */ - res = drmModeGetResources(fd); - if (!res) { - error("cannot retrieve DRM resources: %m\n"); - return -errno; - } - - debug("Found %d connectors\n", res->count_connectors); - - /* iterate all connectors */ - for (i = 0; i < res->count_connectors; ++i) { - /* get information for each connector */ - conn = drmModeGetConnector(fd, res->connectors[i]); - if (!conn) { - error("Cannot retrieve DRM connector #%u: %m\n", - res->connectors[i]); - continue; - } - assert(conn->connector_id == res->connectors[i]); - - debug("Connector #%u has type %s\n", conn->connector_id, - drmModeGetConnectorTypeName(conn->connector_type)); - - /* create a device structure */ - dev = malloc(sizeof(*dev)); - if (!dev) { - error("Cannot allocate memory for connector #%u: %m\n", - res->connectors[i]); - continue; - } - memset(dev, 0, sizeof(*dev)); - dev->conn_id = conn->connector_id; - - ret = drmprepare_connector(fd, res, conn, dev); - if (ret) { - if (ret != -ENOENT) { - error("Cannot setup device for connector #%u: %m\n", - res->connectors[i]); - } - free(dev); - drmModeFreeConnector(conn); - continue; - } - - /* free connector data and link device into global list */ - drmModeFreeConnector(conn); - dev->next = modeset_list; - modeset_list = dev; - } - - /* free resources again */ - drmModeFreeResources(res); - return 0; -} static struct option longopts[] = { @@ -539,14 +75,14 @@ static void usage(const char *prog) int main(int argc, char *argv[]) { char **initsargv; - int drmfd; - char drmdev[128]; + //int drmfd; + //char drmdev[128]; struct modeset_dev *iter; bool pid1 = getpid() == 1; const char *dir = "/usr/share/platsch"; const char *base = "splash"; const char *env; - int ret = 0, c, i; + int ret = 0, c; env = getenv("platsch_directory"); if (env) @@ -582,67 +118,17 @@ int main(int argc, char *argv[]) } } - for (i = 0; i < 64; i++) { - struct drm_mode_card_res res = {0}; - - /* - * XXX: Maybe use drmOpen instead? - * (Where should name/busid come from?) - * XXX: Loop through drm devices to find one with connectors. - */ - ret = snprintf(drmdev, sizeof(drmdev), DRM_DEV_NAME, DRM_DIR_NAME, i); - if (ret >= sizeof(drmdev)) { - error("Huh, device name overflowed buffer\n"); - goto execinit; - } - - drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); - if (drmfd < 0) { - error("Failed to open drm device: %m\n"); - goto execinit; - } - - ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); - if (ret < 0) { - close(drmfd); - continue; - } else { - /* Device found */ - break; - } + struct modeset_dev *modeset_list = init(); + if (!modeset_list) { + error("Failed to initialize modeset\n"); + return EXIT_FAILURE; } - - ret = drmprepare(drmfd); - assert(!ret); - for (iter = modeset_list; iter; iter = iter->next) { - - /* draw first then set the mode */ - draw_buffer(iter, dir, base); - - if (iter->setmode) { - debug("set crtc\n"); - - ret = drmModeSetCrtc(drmfd, iter->crtc_id, iter->fb_id, - 0, 0, &iter->conn_id, 1, &iter->mode); - if (ret) - error("Cannot set CRTC for connector #%u: %m\n", - iter->conn_id); - } else { - debug("page flip\n"); - ret = drmModePageFlip(drmfd, iter->crtc_id, iter->fb_id, - 0, NULL); - if (ret) - error("Page flip failed on connector #%u: %m\n", - iter->conn_id); - } + draw(iter,dir,base); } - ret = drmDropMaster(drmfd); - if (ret) - error("Failed to drop master on drm device\n"); + finish(); -execinit: if (pid1) { ret = fork(); if (ret < 0) { -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch 2024-06-12 7:53 ` [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch LI Qingwu @ 2024-06-12 8:09 ` Marco Felsch 0 siblings, 0 replies; 5+ messages in thread From: Marco Felsch @ 2024-06-12 8:09 UTC (permalink / raw) To: LI Qingwu; +Cc: oss-tools On 24-06-12, LI Qingwu wrote: > Signed-off-by: LI Qingwu <Qing-wu.Li@leica-geosystems.com.cn> > --- > cairo.c | 4 +- Please make sure that your patches are based on the upstream status. Regards, Marco > libplatsch.c | 614 ++++++++++++++++++++++++++++++++++++++ > platsch.h => libplatsch.h | 45 ++- > meson.build | 21 +- > platsch.c | 534 +-------------------------------- > 5 files changed, 677 insertions(+), 541 deletions(-) > create mode 100644 libplatsch.c > rename platsch.h => libplatsch.h (51%) > > diff --git a/cairo.c b/cairo.c > index 8ced3b2..d651b7a 100644 > --- a/cairo.c > +++ b/cairo.c > @@ -25,7 +25,7 @@ > #include <drm_fourcc.h> > #include <cairo.h> > > -#include "platsch.h" > +#include "libplatsch.h" > > static struct cairo_ctx { > struct modeset_dev *dev; > @@ -255,7 +255,7 @@ static uint32_t convert_to_cairo_format(uint32_t format) > return CAIRO_FORMAT_INVALID; > } > > -static cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) > +cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base) > { > cairo_surface_t *surface; > cairo_status_t status; > diff --git a/libplatsch.c b/libplatsch.c > new file mode 100644 > index 0000000..9fceda4 > --- /dev/null > +++ b/libplatsch.c > @@ -0,0 +1,614 @@ > +/* > + * Copyright (C) 2019 Pengutronix, Uwe Kleine-König <u.kleine-koenig@pengutronix.de> > + * > + * Permission to use, copy, modify, and/or distribute this software > + * for any purpose with or without fee is hereby granted. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL > + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED > + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE > + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS > + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, > + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN > + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + * > + * Some code parts base on example code written in 2012 by David Herrmann > + * <dh.herrmann@googlemail.com> and dedicated to the Public Domain. It was found > + * in 2019 on > + * https://raw.githubusercontent.com/dvdhrm/docs/master/drm-howto/modeset.c > + */ > + > +#include <assert.h> > +#include <ctype.h> > +#include <errno.h> > +#include <getopt.h> > +#include <libgen.h> > +#include <stdarg.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <fcntl.h> > + > +#include <xf86drm.h> > +#include <xf86drmMode.h> > +#include <drm_fourcc.h> > +#include "libplatsch.h" > + > + > +static const struct platsch_format platsch_formats[] = { > + { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ > + { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, > +}; > + > + > +ssize_t readfull(int fd, void *buf, size_t count) > +{ > + ssize_t ret = 0, err; > + > + while (count > 0) { > + err = read(fd, buf, count); > + if (err < 0) > + return err; > + else if (err > 0) { > + buf += err; > + count -= err; > + ret += err; > + } else { > + return ret; > + } > + } > + > + return ret; > +} > + > +static int draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) > +{ > + int fd_src; > + char filename[128]; > + ssize_t size; > + int ret; > + > + /* Try cairo draw first and fall back in case of failure. */ > + ret = cairo_draw_buffer(dev, dir, base); > + if (ret == 0) > + return ret; > + > + /* > + * make it easy and load a raw file in the right format instead of > + * opening an (say) PNG and convert the image data to the right format. > + */ > + ret = snprintf(filename, sizeof(filename), > + "%s/%s-%ux%u-%s.bin", > + dir, base, dev->width, dev->height, dev->format->name); > + if (ret >= sizeof(filename)) { > + error("Failed to fit filename into buffer\n"); > + return -EINVAL; > + } > + > + fd_src = open(filename, O_RDONLY | O_CLOEXEC); > + if (fd_src < 0) { > + error("Failed to open %s: %m\n", filename); > + return -ENOENT; > + } > + > + size = readfull(fd_src, dev->map, dev->size); > + if (size < dev->size) { > + if (size < 0) > + error("Failed to read from %s: %m\n", filename); > + else > + error("Could only read %zd/%u bytes from %s\n", > + size, dev->size, filename); > + return -EIO; > + } > + > + ret = close(fd_src); > + if (ret < 0) { > + /* Nothing we can do about this, so just warn */ > + error("Failed to close image file\n"); > + return -EIO; > + } > + > + return 0; > +} > + > +static struct modeset_dev *modeset_list = NULL; > + > +static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, > + struct modeset_dev *dev) > +{ > + drmModeEncoder *enc; > + unsigned int i, j; > + int32_t crtc_id; > + struct modeset_dev *iter; > + > + /* first try the currently connected encoder+crtc */ > + if (conn->encoder_id) { > + debug("connector #%d uses encoder #%d\n", conn->connector_id, > + conn->encoder_id); > + enc = drmModeGetEncoder(fd, conn->encoder_id); > + assert(enc); > + assert(enc->encoder_id == conn->encoder_id); > + } else { > + debug("connector #%d has no active encoder\n", > + conn->connector_id); > + enc = NULL; > + dev->setmode = 1; > + } > + > + if (enc) { > + if (enc->crtc_id) { > + crtc_id = enc->crtc_id; > + assert(crtc_id >= 0); > + > + for (iter = modeset_list; iter; iter = iter->next) { > + if (iter->crtc_id == crtc_id) { > + crtc_id = -1; > + break; > + } > + } > + > + if (crtc_id > 0) { > + debug("encoder #%d uses crtc #%d\n", > + enc->encoder_id, enc->crtc_id); > + drmModeFreeEncoder(enc); > + dev->crtc_id = crtc_id; > + return 0; > + } else { > + debug("encoder #%d used crtc #%d, but that's in use\n", > + enc->encoder_id, iter->crtc_id); > + } > + } else { > + debug("encoder #%d doesn't have an active crtc\n", > + enc->encoder_id); > + } > + > + drmModeFreeEncoder(enc); > + } > + > + /* If the connector is not currently bound to an encoder or if the > + * encoder+crtc is already used by another connector (actually unlikely > + * but let's be safe), iterate all other available encoders to find a > + * matching CRTC. */ > + for (i = 0; i < conn->count_encoders; ++i) { > + enc = drmModeGetEncoder(fd, conn->encoders[i]); > + if (!enc) { > + error("Cannot retrieve encoder %u: %m\n", > + conn->encoders[i]); > + continue; > + } > + assert(enc->encoder_id == conn->encoders[i]); > + > + /* iterate all global CRTCs */ > + for (j = 0; j < res->count_crtcs; ++j) { > + /* check whether this CRTC works with the encoder */ > + if (!(enc->possible_crtcs & (1 << j))) > + continue; > + > + /* check that no other device already uses this CRTC */ > + crtc_id = res->crtcs[j]; > + for (iter = modeset_list; iter; iter = iter->next) { > + if (iter->crtc_id == crtc_id) { > + crtc_id = -1; > + break; > + } > + } > + > + /* we have found a CRTC, so save it and return */ > + if (crtc_id >= 0) { > + debug("encoder #%d will use crtc #%d\n", > + enc->encoder_id, crtc_id); > + drmModeFreeEncoder(enc); > + dev->crtc_id = crtc_id; > + return 0; > + } > + > + } > + drmModeFreeEncoder(enc); > + } > + > + error("Cannot find suitable CRTC for connector #%u\n", > + conn->connector_id); > + return -ENOENT; > +} > + > +static int modeset_create_fb(int fd, struct modeset_dev *dev) > +{ > + struct drm_mode_create_dumb creq; > + struct drm_mode_destroy_dumb dreq; > + struct drm_mode_map_dumb mreq; > + int ret; > + > + /* create dumb buffer */ > + memset(&creq, 0, sizeof(creq)); > + creq.width = dev->width; > + creq.height = dev->height; > + creq.bpp = dev->format->bpp; > + ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); > + if (ret < 0) { > + error("Cannot create dumb buffer: %m\n"); > + return -errno; > + } > + dev->stride = creq.pitch; > + dev->size = creq.size; > + dev->handle = creq.handle; > + > + /* create framebuffer object for the dumb-buffer */ > + ret = drmModeAddFB2(fd, dev->width, dev->height, > + dev->format->format, > + (uint32_t[4]){ dev->handle, }, > + (uint32_t[4]){ dev->stride, }, > + (uint32_t[4]){ 0, }, > + &dev->fb_id, 0); > + if (ret) { > + ret = -errno; > + error("Cannot create framebuffer: %m\n"); > + goto err_destroy; > + } > + > + /* prepare buffer for memory mapping */ > + memset(&mreq, 0, sizeof(mreq)); > + mreq.handle = dev->handle; > + ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); > + if (ret) { > + ret = -errno; > + error("Cannot get mmap offset: %m\n"); > + goto err_fb; > + } > + > + /* perform actual memory mapping */ > + dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, > + fd, mreq.offset); > + if (dev->map == MAP_FAILED) { > + ret = -errno; > + error("Cannot mmap dumb buffer: %m\n"); > + goto err_fb; > + } > + > + /* > + * Clear the framebuffer. Normally it's overwritten later with some > + * image data, but in case this fails, initialize to all-black. > + */ > + memset(dev->map, 0x0, dev->size); > + > + return 0; > + > +err_fb: > + drmModeRmFB(fd, dev->fb_id); > +err_destroy: > + memset(&dreq, 0, sizeof(dreq)); > + dreq.handle = dev->handle; > + drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); > + return ret; > +} > + > +/* Returns lowercase connector type names with '_' for '-' */ > +static char *get_normalized_conn_type_name(uint32_t connector_type) > +{ > + int i; > + const char *connector_name; > + char *normalized_name; > + > + connector_name = drmModeGetConnectorTypeName(connector_type); > + if (!connector_name) > + return NULL; > + > + normalized_name = strdup(connector_name); > + > + for (i = 0; normalized_name[i]; i++) { > + normalized_name[i] = tolower(normalized_name[i]); > + if (normalized_name[i] == '-') > + normalized_name[i] = '_'; > + } > + > + return normalized_name; > +} > + > +static const struct platsch_format *platsch_format_find(const char *name) > +{ > + int i; > + > + for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) > + if (!strcmp(platsch_formats[i].name, name)) > + return &platsch_formats[i]; > + > + return NULL; > +} > + > +static int set_env_connector_mode(drmModeConnector *conn, > + struct modeset_dev *dev) > +{ > + int ret, i = 0; > + u_int32_t width = 0, height = 0; > + const char *mode; > + char *connector_type_name, mode_env_name[32], fmt_specifier[32] = ""; > + const struct platsch_format *format = NULL; > + > + connector_type_name = get_normalized_conn_type_name(conn->connector_type); > + if (!connector_type_name) { > + error("could not look up name for connector type %u\n", > + conn->connector_type); > + goto fallback; > + } > + > + ret = snprintf(mode_env_name, sizeof(mode_env_name), "platsch_%s%u_mode", > + connector_type_name, conn->connector_type_id); > + free(connector_type_name); > + if (ret >= sizeof(mode_env_name)) { > + error("failed to fit platsch env mode variable name into buffer\n"); > + return -EFAULT; > + } > + > + /* check for connector mode configuration in environment */ > + debug("looking up %s env variable\n", mode_env_name); > + mode = getenv(mode_env_name); > + if (!mode) > + goto fallback; > + > + /* format suffix is optional */ > + ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); > + if (ret < 2) { > + error("error while scanning %s for mode\n", mode_env_name); > + return -EFAULT; > + } > + > + /* use first mode matching given resolution */ > + for (i = 0; i < conn->count_modes; i++) { > + drmModeModeInfo mode = conn->modes[i]; > + if (mode.hdisplay == width && mode.vdisplay == height) { > + memcpy(&dev->mode, &mode, sizeof(dev->mode)); > + dev->width = width; > + dev->height = height; > + break; > + } > + } > + > + if (i == conn->count_modes) { > + error("no mode available matching %ux%u\n", width, height); > + return -ENOENT; > + } > + > + format = platsch_format_find(fmt_specifier); > + if (!format) { > + if (strlen(fmt_specifier)) > + error("unknown format specifier %s\n", fmt_specifier); > + goto fallback_format; > + } > + > + dev->format = format; > + > + return 0; > + > +fallback: > + memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); > + dev->width = conn->modes[0].hdisplay; > + dev->height = conn->modes[0].vdisplay; > + debug("using default mode for connector #%u\n", conn->connector_id); > + > +fallback_format: > + dev->format = &platsch_formats[0]; > + debug("using default format %s for connector #%u\n", dev->format->name, > + conn->connector_id); > + > + return 0; > +} > + > +static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, > + struct modeset_dev *dev) > +{ > + int ret; > + > + /* check if a monitor is connected */ > + if (conn->connection != DRM_MODE_CONNECTED) { > + error("Ignoring unused connector #%u\n", conn->connector_id); > + return -ENOENT; > + } > + > + /* check if there is at least one valid mode */ > + if (conn->count_modes == 0) { > + error("no valid mode for connector #%u\n", conn->connector_id); > + return -EFAULT; > + } > + > + /* configure mode information in our device structure */ > + ret = set_env_connector_mode(conn, dev); > + if (ret) { > + error("no valid mode for connector #%u\n", conn->connector_id); > + return ret; > + } > + debug("mode for connector #%u is %ux%u@%s\n", > + conn->connector_id, dev->width, dev->height, dev->format->name); > + > + /* find a crtc for this connector */ > + ret = drmprepare_crtc(fd, res, conn, dev); > + if (ret) { > + error("no valid crtc for connector #%u\n", conn->connector_id); > + return ret; > + } > + > + /* create a framebuffer for this CRTC */ > + ret = modeset_create_fb(fd, dev); > + if (ret) { > + error("cannot create framebuffer for connector #%u\n", > + conn->connector_id); > + return ret; > + } > + > + return 0; > +} > + > +static int drmprepare(int fd) > +{ > + drmModeRes *res; > + drmModeConnector *conn; > + unsigned int i; > + struct modeset_dev *dev; > + int ret; > + > + /* retrieve resources */ > + res = drmModeGetResources(fd); > + if (!res) { > + error("cannot retrieve DRM resources: %m\n"); > + return -errno; > + } > + > + debug("Found %d connectors\n", res->count_connectors); > + > + /* iterate all connectors */ > + for (i = 0; i < res->count_connectors; ++i) { > + /* get information for each connector */ > + conn = drmModeGetConnector(fd, res->connectors[i]); > + if (!conn) { > + error("Cannot retrieve DRM connector #%u: %m\n", > + res->connectors[i]); > + continue; > + } > + assert(conn->connector_id == res->connectors[i]); > + > + debug("Connector #%u has type %s\n", conn->connector_id, > + drmModeGetConnectorTypeName(conn->connector_type)); > + > + /* create a device structure */ > + dev = malloc(sizeof(*dev)); > + if (!dev) { > + error("Cannot allocate memory for connector #%u: %m\n", > + res->connectors[i]); > + continue; > + } > + memset(dev, 0, sizeof(*dev)); > + dev->conn_id = conn->connector_id; > + > + ret = drmprepare_connector(fd, res, conn, dev); > + if (ret) { > + if (ret != -ENOENT) { > + error("Cannot setup device for connector #%u: %m\n", > + res->connectors[i]); > + } > + free(dev); > + drmModeFreeConnector(conn); > + continue; > + } > + > + /* free connector data and link device into global list */ > + drmModeFreeConnector(conn); > + dev->next = modeset_list; > + modeset_list = dev; > + } > + > + /* free resources again */ > + drmModeFreeResources(res); > + return 0; > +} > + > + > +static int drmfd; > + > +struct modeset_dev *init(void) > +{ > + static char drmdev[128]; > + int ret = 0, i; > + > + for (i = 0; i < 64; i++) { > + struct drm_mode_card_res res = {0}; > + > + /* > + * XXX: Maybe use drmOpen instead? > + * (Where should name/busid come from?) > + * XXX: Loop through drm devices to find one with connectors. > + */ > + ret = snprintf(drmdev, sizeof(drmdev), DRM_DEV_NAME, DRM_DIR_NAME, i); > + if (ret >= sizeof(drmdev)) { > + error("Huh, device name overflowed buffer\n"); > + goto execinit; > + } > + > + drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); > + if (drmfd < 0) { > + error("Failed to open drm device: %m\n"); > + goto execinit; > + } > + > + ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); > + if (ret < 0) { > + close(drmfd); > + continue; > + } else { > + /* Device found */ > + break; > + } > + } > + > + if (i == 64) { > + error("No suitable DRM device found\n"); > + goto execinit; > + } > + > + ret = drmprepare(drmfd); > + if (ret) { > + error("Failed to prepare DRM device\n"); > + goto execinit; > + } > + > + return modeset_list; > + > +execinit: > + return NULL; > +} > + > + > +int update_display(struct modeset_dev *dev) { > + int ret = 0; > + > + if (dev->setmode) { > + ret = drmModeSetCrtc(drmfd, dev->crtc_id, dev->fb_id, 0, 0, &dev->conn_id, 1, &dev->mode); > + if (ret) { > + error("Cannot set CRTC for connector #%u: %m\n", dev->conn_id); > + } > + dev->setmode = 0; > + } else { > + ret = drmModePageFlip(drmfd, dev->crtc_id, dev->fb_id, 0, NULL); > + if (ret) { > + error("Page flip failed on connector #%u: %m\n", dev->conn_id); > + } > + } > + return ret; > +} > + > +int draw(struct modeset_dev *dev, const char *dir, const char *base) > +{ > + int ret = 0; > + ret = draw_buffer(dev, dir, base); > + if (ret) { > + error("Failed to draw buffer\n"); > + return ret; > + } > + return update_display(dev); > +} > + > +int finish(void) { > + int ret = drmDropMaster(drmfd); > + if (ret) { > + error("Failed to drop master on DRM device\n"); > + } > + > + return ret; > +} > + > +void deinit(void) { > + struct modeset_dev *iter; > + > + for (iter = modeset_list; iter; iter = iter->next) { > + if (iter->map) { > + munmap(iter->map, iter->size); > + } > + if (iter->fb_id) { > + drmModeRmFB(drmfd, iter->fb_id); > + } > + free(iter); > + } > +} > diff --git a/platsch.h b/libplatsch.h > similarity index 51% > rename from platsch.h > rename to libplatsch.h > index dee22eb..87f6ef5 100644 > --- a/platsch.h > +++ b/libplatsch.h > @@ -1,8 +1,27 @@ > -#ifndef __PLATSCH_H__ > -#define __PLATSCH_H__ > +#ifndef __LIBPLATSCH_H__ > +#define __LIBPLATSCH_H__ > > -#include <stdint.h> > +#include <assert.h> > +#include <ctype.h> > +#include <errno.h> > +#include <getopt.h> > +#include <libgen.h> > +#include <stdarg.h> > +#include <stdbool.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <sys/mman.h> > +#include <fcntl.h> > + > +#include <xf86drm.h> > #include <xf86drmMode.h> > +#include <drm_fourcc.h> > +#include "libplatsch.h" > + > > #define debug(fmt, ...) printf("%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__) > #define error(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) > @@ -17,7 +36,6 @@ struct platsch_format { > > struct modeset_dev { > struct modeset_dev *next; > - > uint32_t width; > uint32_t height; > uint32_t stride; > @@ -25,7 +43,6 @@ struct modeset_dev { > const struct platsch_format *format; > uint32_t handle; > void *map; > - > bool setmode; > drmModeModeInfo mode; > uint32_t fb_id; > @@ -34,14 +51,24 @@ struct modeset_dev { > }; > > ssize_t readfull(int fd, void *buf, size_t count); > +struct modeset_dev * init(void); > > -#ifdef HAVE_CAIRO > -int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base); > -#else > +int draw(struct modeset_dev *dev, const char *dir, const char *base); > +int finish(void); > +int update_display(struct modeset_dev *dev); > + > +#ifndef HAVE_CAIRO > static inline int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) > { > + printf("cairo_draw_buffer do nothing %s %s\n", dir, base); > return -ENOTSUP; > } > +#else > + > +#include <cairo.h> > +int cairo_draw_buffer(struct modeset_dev *dev, const char *dir, const char *base); > +cairo_t *cairo_init(struct modeset_dev *dev, const char *dir, const char *base); > + > #endif /* HAVE_CAIRO */ > > -#endif /* __PLATSCH_H__ */ > +#endif /* __LIBPLATSCH_H__ */ > diff --git a/meson.build b/meson.build > index e7e8e80..8c15fb9 100644 > --- a/meson.build > +++ b/meson.build > @@ -1,26 +1,35 @@ > project('platsch', 'c') > > +have_cairo = get_option('HAVE_CAIRO') > + > # Define dependencies conditionally based on the HAVE_CAIRO option > platsch_dep = [dependency('libdrm', required: true)] > -sources = ['platsch.c'] > +sources = ['libplatsch.c'] > args = [] > > -have_cairo = get_option('HAVE_CAIRO') > - > if have_cairo > platsch_dep += dependency('cairo', required: true) > sources += 'cairo.c' > args += ['-DHAVE_CAIRO'] > endif > > +# Create a static library from libplatsch.c and optionally cairo.c > +libplatsch = static_library('libplatsch', > + sources, > + dependencies: platsch_dep, > + c_args: args, > + install: true > +) > + > # Define the headers > -headers = ['platsch.h'] > +headers = ['libplatsch.h', 'platsch.h'] > > -# Create the platsch executable > +# Create the platsch executable, linking it statically with libplatsch > executable('platsch', > - sources, > + 'platsch.c', > dependencies: platsch_dep, > c_args: args, > + link_with: libplatsch, > install: true, > include_directories: include_directories('.') > ) > diff --git a/platsch.c b/platsch.c > index a7e82a6..92123d8 100644 > --- a/platsch.c > +++ b/platsch.c > @@ -35,16 +35,7 @@ > #include <sys/mman.h> > #include <fcntl.h> > > -#include <xf86drm.h> > -#include <xf86drmMode.h> > -#include <drm_fourcc.h> > - > -#include "platsch.h" > - > -static const struct platsch_format platsch_formats[] = { > - { DRM_FORMAT_RGB565, 16, "RGB565" }, /* default */ > - { DRM_FORMAT_XRGB8888, 32, "XRGB8888" }, > -}; > +#include "libplatsch.h" > > void redirect_stdfd(void) > { > @@ -64,461 +55,6 @@ void redirect_stdfd(void) > close(devnull); > } > > -ssize_t readfull(int fd, void *buf, size_t count) > -{ > - ssize_t ret = 0, err; > - > - while (count > 0) { > - err = read(fd, buf, count); > - if (err < 0) > - return err; > - else if (err > 0) { > - buf += err; > - count -= err; > - ret += err; > - } else { > - return ret; > - } > - } > - > - return ret; > -} > - > -void draw_buffer(struct modeset_dev *dev, const char *dir, const char *base) > -{ > - int fd_src; > - char filename[128]; > - ssize_t size; > - int ret; > - > - /* Try cairo draw first and fall back in case of failure. */ > - ret = cairo_draw_buffer(dev, dir, base); > - if (ret == 0) > - return; > - > - /* > - * make it easy and load a raw file in the right format instead of > - * opening an (say) PNG and convert the image data to the right format. > - */ > - ret = snprintf(filename, sizeof(filename), > - "%s/%s-%ux%u-%s.bin", > - dir, base, dev->width, dev->height, dev->format->name); > - if (ret >= sizeof(filename)) { > - error("Failed to fit filename into buffer\n"); > - return; > - } > - > - fd_src = open(filename, O_RDONLY | O_CLOEXEC); > - if (fd_src < 0) { > - error("Failed to open %s: %m\n", filename); > - return; > - } > - > - size = readfull(fd_src, dev->map, dev->size); > - if (size < dev->size) { > - if (size < 0) > - error("Failed to read from %s: %m\n", filename); > - else > - error("Could only read %zd/%u bytes from %s\n", > - size, dev->size, filename); > - } > - > - ret = close(fd_src); > - if (ret < 0) { > - /* Nothing we can do about this, so just warn */ > - error("Failed to close image file\n"); > - } > - > - return; > -} > - > -static struct modeset_dev *modeset_list = NULL; > - > -static int drmprepare_crtc(int fd, drmModeRes *res, drmModeConnector *conn, > - struct modeset_dev *dev) > -{ > - drmModeEncoder *enc; > - unsigned int i, j; > - int32_t crtc_id; > - struct modeset_dev *iter; > - > - /* first try the currently connected encoder+crtc */ > - if (conn->encoder_id) { > - debug("connector #%d uses encoder #%d\n", conn->connector_id, > - conn->encoder_id); > - enc = drmModeGetEncoder(fd, conn->encoder_id); > - assert(enc); > - assert(enc->encoder_id == conn->encoder_id); > - } else { > - debug("connector #%d has no active encoder\n", > - conn->connector_id); > - enc = NULL; > - dev->setmode = 1; > - } > - > - if (enc) { > - if (enc->crtc_id) { > - crtc_id = enc->crtc_id; > - assert(crtc_id >= 0); > - > - for (iter = modeset_list; iter; iter = iter->next) { > - if (iter->crtc_id == crtc_id) { > - crtc_id = -1; > - break; > - } > - } > - > - if (crtc_id > 0) { > - debug("encoder #%d uses crtc #%d\n", > - enc->encoder_id, enc->crtc_id); > - drmModeFreeEncoder(enc); > - dev->crtc_id = crtc_id; > - return 0; > - } else { > - debug("encoder #%d used crtc #%d, but that's in use\n", > - enc->encoder_id, iter->crtc_id); > - } > - } else { > - debug("encoder #%d doesn't have an active crtc\n", > - enc->encoder_id); > - } > - > - drmModeFreeEncoder(enc); > - } > - > - /* If the connector is not currently bound to an encoder or if the > - * encoder+crtc is already used by another connector (actually unlikely > - * but let's be safe), iterate all other available encoders to find a > - * matching CRTC. */ > - for (i = 0; i < conn->count_encoders; ++i) { > - enc = drmModeGetEncoder(fd, conn->encoders[i]); > - if (!enc) { > - error("Cannot retrieve encoder %u: %m\n", > - conn->encoders[i]); > - continue; > - } > - assert(enc->encoder_id == conn->encoders[i]); > - > - /* iterate all global CRTCs */ > - for (j = 0; j < res->count_crtcs; ++j) { > - /* check whether this CRTC works with the encoder */ > - if (!(enc->possible_crtcs & (1 << j))) > - continue; > - > - /* check that no other device already uses this CRTC */ > - crtc_id = res->crtcs[j]; > - for (iter = modeset_list; iter; iter = iter->next) { > - if (iter->crtc_id == crtc_id) { > - crtc_id = -1; > - break; > - } > - } > - > - /* we have found a CRTC, so save it and return */ > - if (crtc_id >= 0) { > - debug("encoder #%d will use crtc #%d\n", > - enc->encoder_id, crtc_id); > - drmModeFreeEncoder(enc); > - dev->crtc_id = crtc_id; > - return 0; > - } > - > - } > - drmModeFreeEncoder(enc); > - } > - > - error("Cannot find suitable CRTC for connector #%u\n", > - conn->connector_id); > - return -ENOENT; > -} > - > -static int modeset_create_fb(int fd, struct modeset_dev *dev) > -{ > - struct drm_mode_create_dumb creq; > - struct drm_mode_destroy_dumb dreq; > - struct drm_mode_map_dumb mreq; > - int ret; > - > - /* create dumb buffer */ > - memset(&creq, 0, sizeof(creq)); > - creq.width = dev->width; > - creq.height = dev->height; > - creq.bpp = dev->format->bpp; > - ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); > - if (ret < 0) { > - error("Cannot create dumb buffer: %m\n"); > - return -errno; > - } > - dev->stride = creq.pitch; > - dev->size = creq.size; > - dev->handle = creq.handle; > - > - /* create framebuffer object for the dumb-buffer */ > - ret = drmModeAddFB2(fd, dev->width, dev->height, > - dev->format->format, > - (uint32_t[4]){ dev->handle, }, > - (uint32_t[4]){ dev->stride, }, > - (uint32_t[4]){ 0, }, > - &dev->fb_id, 0); > - if (ret) { > - ret = -errno; > - error("Cannot create framebuffer: %m\n"); > - goto err_destroy; > - } > - > - /* prepare buffer for memory mapping */ > - memset(&mreq, 0, sizeof(mreq)); > - mreq.handle = dev->handle; > - ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); > - if (ret) { > - ret = -errno; > - error("Cannot get mmap offset: %m\n"); > - goto err_fb; > - } > - > - /* perform actual memory mapping */ > - dev->map = mmap(0, dev->size, PROT_READ | PROT_WRITE, MAP_SHARED, > - fd, mreq.offset); > - if (dev->map == MAP_FAILED) { > - ret = -errno; > - error("Cannot mmap dumb buffer: %m\n"); > - goto err_fb; > - } > - > - /* > - * Clear the framebuffer. Normally it's overwritten later with some > - * image data, but in case this fails, initialize to all-black. > - */ > - memset(dev->map, 0x0, dev->size); > - > - return 0; > - > -err_fb: > - drmModeRmFB(fd, dev->fb_id); > -err_destroy: > - memset(&dreq, 0, sizeof(dreq)); > - dreq.handle = dev->handle; > - drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); > - return ret; > -} > - > -/* Returns lowercase connector type names with '_' for '-' */ > -static char *get_normalized_conn_type_name(uint32_t connector_type) > -{ > - int i; > - const char *connector_name; > - char *normalized_name; > - > - connector_name = drmModeGetConnectorTypeName(connector_type); > - if (!connector_name) > - return NULL; > - > - normalized_name = strdup(connector_name); > - > - for (i = 0; normalized_name[i]; i++) { > - normalized_name[i] = tolower(normalized_name[i]); > - if (normalized_name[i] == '-') > - normalized_name[i] = '_'; > - } > - > - return normalized_name; > -} > - > -static const struct platsch_format *platsch_format_find(const char *name) > -{ > - int i; > - > - for (i = 0; i < ARRAY_SIZE(platsch_formats); i++) > - if (!strcmp(platsch_formats[i].name, name)) > - return &platsch_formats[i]; > - > - return NULL; > -} > - > -static int set_env_connector_mode(drmModeConnector *conn, > - struct modeset_dev *dev) > -{ > - int ret, i = 0; > - u_int32_t width = 0, height = 0; > - const char *mode; > - char *connector_type_name, mode_env_name[32], fmt_specifier[32] = ""; > - const struct platsch_format *format = NULL; > - > - connector_type_name = get_normalized_conn_type_name(conn->connector_type); > - if (!connector_type_name) { > - error("could not look up name for connector type %u\n", > - conn->connector_type); > - goto fallback; > - } > - > - ret = snprintf(mode_env_name, sizeof(mode_env_name), "platsch_%s%u_mode", > - connector_type_name, conn->connector_type_id); > - free(connector_type_name); > - if (ret >= sizeof(mode_env_name)) { > - error("failed to fit platsch env mode variable name into buffer\n"); > - return -EFAULT; > - } > - > - /* check for connector mode configuration in environment */ > - debug("looking up %s env variable\n", mode_env_name); > - mode = getenv(mode_env_name); > - if (!mode) > - goto fallback; > - > - /* format suffix is optional */ > - ret = sscanf(mode, "%ux%u@%s", &width, &height, fmt_specifier); > - if (ret < 2) { > - error("error while scanning %s for mode\n", mode_env_name); > - return -EFAULT; > - } > - > - /* use first mode matching given resolution */ > - for (i = 0; i < conn->count_modes; i++) { > - drmModeModeInfo mode = conn->modes[i]; > - if (mode.hdisplay == width && mode.vdisplay == height) { > - memcpy(&dev->mode, &mode, sizeof(dev->mode)); > - dev->width = width; > - dev->height = height; > - break; > - } > - } > - > - if (i == conn->count_modes) { > - error("no mode available matching %ux%u\n", width, height); > - return -ENOENT; > - } > - > - format = platsch_format_find(fmt_specifier); > - if (!format) { > - if (strlen(fmt_specifier)) > - error("unknown format specifier %s\n", fmt_specifier); > - goto fallback_format; > - } > - > - dev->format = format; > - > - return 0; > - > -fallback: > - memcpy(&dev->mode, &conn->modes[0], sizeof(dev->mode)); > - dev->width = conn->modes[0].hdisplay; > - dev->height = conn->modes[0].vdisplay; > - debug("using default mode for connector #%u\n", conn->connector_id); > - > -fallback_format: > - dev->format = &platsch_formats[0]; > - debug("using default format %s for connector #%u\n", dev->format->name, > - conn->connector_id); > - > - return 0; > -} > - > -static int drmprepare_connector(int fd, drmModeRes *res, drmModeConnector *conn, > - struct modeset_dev *dev) > -{ > - int ret; > - > - /* check if a monitor is connected */ > - if (conn->connection != DRM_MODE_CONNECTED) { > - error("Ignoring unused connector #%u\n", conn->connector_id); > - return -ENOENT; > - } > - > - /* check if there is at least one valid mode */ > - if (conn->count_modes == 0) { > - error("no valid mode for connector #%u\n", conn->connector_id); > - return -EFAULT; > - } > - > - /* configure mode information in our device structure */ > - ret = set_env_connector_mode(conn, dev); > - if (ret) { > - error("no valid mode for connector #%u\n", conn->connector_id); > - return ret; > - } > - debug("mode for connector #%u is %ux%u@%s\n", > - conn->connector_id, dev->width, dev->height, dev->format->name); > - > - /* find a crtc for this connector */ > - ret = drmprepare_crtc(fd, res, conn, dev); > - if (ret) { > - error("no valid crtc for connector #%u\n", conn->connector_id); > - return ret; > - } > - > - /* create a framebuffer for this CRTC */ > - ret = modeset_create_fb(fd, dev); > - if (ret) { > - error("cannot create framebuffer for connector #%u\n", > - conn->connector_id); > - return ret; > - } > - > - return 0; > -} > - > -static int drmprepare(int fd) > -{ > - drmModeRes *res; > - drmModeConnector *conn; > - unsigned int i; > - struct modeset_dev *dev; > - int ret; > - > - /* retrieve resources */ > - res = drmModeGetResources(fd); > - if (!res) { > - error("cannot retrieve DRM resources: %m\n"); > - return -errno; > - } > - > - debug("Found %d connectors\n", res->count_connectors); > - > - /* iterate all connectors */ > - for (i = 0; i < res->count_connectors; ++i) { > - /* get information for each connector */ > - conn = drmModeGetConnector(fd, res->connectors[i]); > - if (!conn) { > - error("Cannot retrieve DRM connector #%u: %m\n", > - res->connectors[i]); > - continue; > - } > - assert(conn->connector_id == res->connectors[i]); > - > - debug("Connector #%u has type %s\n", conn->connector_id, > - drmModeGetConnectorTypeName(conn->connector_type)); > - > - /* create a device structure */ > - dev = malloc(sizeof(*dev)); > - if (!dev) { > - error("Cannot allocate memory for connector #%u: %m\n", > - res->connectors[i]); > - continue; > - } > - memset(dev, 0, sizeof(*dev)); > - dev->conn_id = conn->connector_id; > - > - ret = drmprepare_connector(fd, res, conn, dev); > - if (ret) { > - if (ret != -ENOENT) { > - error("Cannot setup device for connector #%u: %m\n", > - res->connectors[i]); > - } > - free(dev); > - drmModeFreeConnector(conn); > - continue; > - } > - > - /* free connector data and link device into global list */ > - drmModeFreeConnector(conn); > - dev->next = modeset_list; > - modeset_list = dev; > - } > - > - /* free resources again */ > - drmModeFreeResources(res); > - return 0; > -} > > static struct option longopts[] = > { > @@ -539,14 +75,14 @@ static void usage(const char *prog) > int main(int argc, char *argv[]) > { > char **initsargv; > - int drmfd; > - char drmdev[128]; > + //int drmfd; > + //char drmdev[128]; > struct modeset_dev *iter; > bool pid1 = getpid() == 1; > const char *dir = "/usr/share/platsch"; > const char *base = "splash"; > const char *env; > - int ret = 0, c, i; > + int ret = 0, c; > > env = getenv("platsch_directory"); > if (env) > @@ -582,67 +118,17 @@ int main(int argc, char *argv[]) > } > } > > - for (i = 0; i < 64; i++) { > - struct drm_mode_card_res res = {0}; > - > - /* > - * XXX: Maybe use drmOpen instead? > - * (Where should name/busid come from?) > - * XXX: Loop through drm devices to find one with connectors. > - */ > - ret = snprintf(drmdev, sizeof(drmdev), DRM_DEV_NAME, DRM_DIR_NAME, i); > - if (ret >= sizeof(drmdev)) { > - error("Huh, device name overflowed buffer\n"); > - goto execinit; > - } > - > - drmfd = open(drmdev, O_RDWR | O_CLOEXEC, 0); > - if (drmfd < 0) { > - error("Failed to open drm device: %m\n"); > - goto execinit; > - } > - > - ret = drmIoctl(drmfd, DRM_IOCTL_MODE_GETRESOURCES, &res); > - if (ret < 0) { > - close(drmfd); > - continue; > - } else { > - /* Device found */ > - break; > - } > + struct modeset_dev *modeset_list = init(); > + if (!modeset_list) { > + error("Failed to initialize modeset\n"); > + return EXIT_FAILURE; > } > - > - ret = drmprepare(drmfd); > - assert(!ret); > - > for (iter = modeset_list; iter; iter = iter->next) { > - > - /* draw first then set the mode */ > - draw_buffer(iter, dir, base); > - > - if (iter->setmode) { > - debug("set crtc\n"); > - > - ret = drmModeSetCrtc(drmfd, iter->crtc_id, iter->fb_id, > - 0, 0, &iter->conn_id, 1, &iter->mode); > - if (ret) > - error("Cannot set CRTC for connector #%u: %m\n", > - iter->conn_id); > - } else { > - debug("page flip\n"); > - ret = drmModePageFlip(drmfd, iter->crtc_id, iter->fb_id, > - 0, NULL); > - if (ret) > - error("Page flip failed on connector #%u: %m\n", > - iter->conn_id); > - } > + draw(iter,dir,base); > } > > - ret = drmDropMaster(drmfd); > - if (ret) > - error("Failed to drop master on drm device\n"); > + finish(); > > -execinit: > if (pid1) { > ret = fork(); > if (ret < 0) { > -- > 2.34.1 > > ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-06-12 9:26 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2024-06-12 4:06 [OSS-Tools] [PATCH platsch 1/3] convert to meson build LI Qingwu 2024-06-12 4:06 ` [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch LI Qingwu 2024-06-12 4:06 ` [OSS-Tools] [PATCH platsch 3/3] Add spinner executable for boot animation and text show LI Qingwu 2024-06-12 7:53 [OSS-Tools] [PATCH platsch 1/3] convert to meson build LI Qingwu 2024-06-12 7:53 ` [OSS-Tools] [PATCH platsch 2/3] platsch: split into platsch and libplatsch LI Qingwu 2024-06-12 8:09 ` Marco Felsch
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox