Merge tag '11.0.3' into dev
11.0.3
This commit is contained in:
+4
-5
@@ -43,7 +43,7 @@
|
||||
variables:
|
||||
FDO_UPSTREAM_REPO: wayland/weston
|
||||
FDO_REPO_SUFFIX: "$BUILD_OS/$BUILD_ARCH"
|
||||
FDO_DISTRIBUTION_TAG: '2021-11-25.0-dmabuf-feedback'
|
||||
FDO_DISTRIBUTION_TAG: '2022-07-13.00-wayland-protocols-1.26'
|
||||
|
||||
|
||||
include:
|
||||
@@ -202,7 +202,7 @@ aarch64-debian-container_prep:
|
||||
- $BUILDDIR/weston-virtme
|
||||
- $PREFIX
|
||||
reports:
|
||||
junit: $BUILDDIR/meson-logs/testlog.junit.xml
|
||||
junit: $BUILDDIR/meson-logs/testlog-per-test-asan.sh.junit.xml
|
||||
|
||||
# Same as above, but without running any tests.
|
||||
.build-no-test:
|
||||
@@ -283,8 +283,8 @@ aarch64-debian-container_prep:
|
||||
-Dwerror=true
|
||||
-Dtest-skip-is-failure=true
|
||||
-Dlauncher-libseat=true
|
||||
-Ddeprecated-backend-fbdev=true
|
||||
-Ddeprecated-weston-launch=true
|
||||
-Ddeprecated-color-management-static=true
|
||||
-Ddeprecated-color-management-colord=true
|
||||
after_script:
|
||||
- ninja -C "$BUILDDIR" coverage-html > "$BUILDDIR/meson-logs/ninja-coverage-html.txt"
|
||||
- ninja -C "$BUILDDIR" coverage-xml
|
||||
@@ -340,7 +340,6 @@ docs-build:
|
||||
-Dpipewire=false
|
||||
-Dwerror=true
|
||||
-Dlauncher-libseat=true
|
||||
-Ddeprecated-weston-launch=true
|
||||
|
||||
x86_64-debian-no-gl-build:
|
||||
extends:
|
||||
|
||||
@@ -75,7 +75,8 @@ if [[ -n "$KERNEL_DEFCONFIG" ]]; then
|
||||
--enable CONFIG_DRM \
|
||||
--enable CONFIG_DRM_KMS_HELPER \
|
||||
--enable CONFIG_DRM_KMS_FB_HELPER \
|
||||
--enable CONFIG_DRM_VKMS
|
||||
--enable CONFIG_DRM_VKMS \
|
||||
--enable CONFIG_DRM_VGEM
|
||||
make ARCH=${LINUX_ARCH} oldconfig
|
||||
make ARCH=${LINUX_ARCH}
|
||||
|
||||
@@ -94,7 +95,7 @@ fi
|
||||
|
||||
# Build and install Wayland; keep this version in sync with our dependency
|
||||
# in meson.build.
|
||||
git clone --branch 1.18.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
|
||||
git clone --branch 1.20.0 --depth=1 https://gitlab.freedesktop.org/wayland/wayland
|
||||
cd wayland
|
||||
git show -s HEAD
|
||||
mkdir build
|
||||
@@ -106,7 +107,7 @@ rm -rf wayland
|
||||
# Keep this version in sync with our dependency in meson.build. If you wish to
|
||||
# raise a MR against custom protocol, please change this reference to clone
|
||||
# your relevant tree, and make sure you bump $FDO_DISTRIBUTION_TAG.
|
||||
git clone --branch 1.24 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
|
||||
git clone --branch 1.26 --depth=1 https://gitlab.freedesktop.org/wayland/wayland-protocols
|
||||
cd wayland-protocols
|
||||
git show -s HEAD
|
||||
meson build
|
||||
@@ -129,6 +130,17 @@ ninja ${NINJAFLAGS} -C build install
|
||||
cd ..
|
||||
rm -rf mesa
|
||||
|
||||
# Build and install our own version of libdrm. Debian 11 (bullseye) provides
|
||||
# libdrm 2.4.104 which doesn't have the IN_FORMATS iterator api. We can stop
|
||||
# building and installing libdrm as soon as we move to Debian 12.
|
||||
git clone --branch libdrm-2.4.108 --depth=1 https://gitlab.freedesktop.org/mesa/drm.git
|
||||
cd drm
|
||||
meson build -Dauto_features=disabled \
|
||||
-Dvc4=false -Dfreedreno=false -Detnaviv=false
|
||||
ninja ${NINJAFLAGS} -C build install
|
||||
cd ..
|
||||
rm -rf drm
|
||||
|
||||
# PipeWire is used for remoting support. Unlike our other dependencies its
|
||||
# behaviour will be stable, however as a pre-1.0 project its API is not yet
|
||||
# stable, so again we lock it to a fixed version.
|
||||
@@ -142,9 +154,8 @@ ninja ${NINJAFLAGS} -C build install
|
||||
cd ..
|
||||
rm -rf pipewire-src
|
||||
|
||||
# seatd lets us avoid the pain of handling VTs manually through weston-launch
|
||||
# or open-coding TTY assignment within Weston. We use this for our tests using
|
||||
# the DRM backend.
|
||||
# seatd lets us avoid the pain of open-coding TTY assignment within Weston.
|
||||
# We use this for our tests using the DRM backend.
|
||||
git clone --depth=1 --branch 0.6.1 https://git.sr.ht/~kennylevinsen/seatd
|
||||
cd seatd
|
||||
meson build -Dauto_features=disabled \
|
||||
|
||||
@@ -39,6 +39,7 @@ apt-get -y --no-install-recommends install \
|
||||
clang-11 \
|
||||
curl \
|
||||
doxygen \
|
||||
graphviz \
|
||||
freerdp2-dev \
|
||||
gcovr \
|
||||
git \
|
||||
@@ -68,6 +69,7 @@ apt-get -y --no-install-recommends install \
|
||||
libmtdev-dev \
|
||||
libpam0g-dev \
|
||||
libpango1.0-dev \
|
||||
libpciaccess-dev \
|
||||
libpixman-1-dev \
|
||||
libpng-dev \
|
||||
libpulse-dev \
|
||||
|
||||
@@ -3,3 +3,9 @@
|
||||
# Cairo internal leaks from weston-keyboard
|
||||
leak:cairo_select_font_face
|
||||
leak:cairo_text_extents
|
||||
|
||||
# Pango thread-global state (not destroyable?)
|
||||
leak:pango_language_get_default
|
||||
|
||||
# This leaks in Debian's fontconfig/Xwayland setup?
|
||||
leak:FcConfigSubstituteWithPat
|
||||
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# When running Debian's Xwayland and fontconfig, we hit memory leaks which
|
||||
# aren't visible on other setups. We do have suppressions for these tests, but
|
||||
# regrettably ASan can't see through omitted frame pointers in Expat, so for
|
||||
# Xwayland specifically, we disable fast-unwind.
|
||||
#
|
||||
# Doing it globally makes the other tests far, far, too slow to run.
|
||||
case "$1" in
|
||||
*xwayland*)
|
||||
export ASAN_OPTIONS="detect_leaks=0,fast_unwind_on_malloc=0"
|
||||
;;
|
||||
*)
|
||||
export ASAN_OPTIONS="detect_leaks=0"
|
||||
;;
|
||||
esac
|
||||
|
||||
export ASAN_OPTIONS
|
||||
|
||||
exec "$@"
|
||||
@@ -7,8 +7,15 @@ chmod -R 0700 /tmp
|
||||
|
||||
# set environment variables to run Weston tests
|
||||
export XDG_RUNTIME_DIR=/tmp/tests
|
||||
export WESTON_TEST_SUITE_DRM_DEVICE=card0
|
||||
export LIBSEAT_BACKEND=seatd
|
||||
# In our test suite, we use VKMS to run DRM-backend tests. The order in which
|
||||
# devices are loaded is not predictable, so the DRM node that VKMS takes can
|
||||
# change across each boot. That's why we have this one-liner shell script to get
|
||||
# the appropriate node for VKMS.
|
||||
export WESTON_TEST_SUITE_DRM_DEVICE=$(basename /sys/devices/platform/vkms/drm/card*)
|
||||
# To run tests in the CI that exercise the zwp_linux_dmabuf_v1 implementation in
|
||||
# Weston, we use VGEM to allocate buffers.
|
||||
export WESTON_TEST_SUITE_ALLOC_DEVICE=$(basename /sys/devices/platform/vgem/drm/card*)
|
||||
|
||||
# ninja test depends on meson, and meson itself looks for its modules on folder
|
||||
# $HOME/.local/lib/pythonX.Y/site-packages (the Python version may differ).
|
||||
@@ -19,13 +26,17 @@ export HOME=/root
|
||||
export PATH=$HOME/.local/bin:$PATH
|
||||
export PATH=/usr/local/bin:$PATH
|
||||
|
||||
export ASAN_OPTIONS=detect_leaks=0,atexit=1
|
||||
export SEATD_LOGLEVEL=debug
|
||||
|
||||
# Terrible hack, per comment in weston-test-runner.c's main(): find Mesa's
|
||||
# llvmpipe driver module location
|
||||
export WESTON_CI_LEAK_DL_HANDLE=$(find /usr/local -name swrast_dri.so -print 2>/dev/null || true)
|
||||
|
||||
# run the tests and save the exit status
|
||||
# we give ourselves a very generous timeout multiplier due to ASan overhead
|
||||
echo 0x1f > /sys/module/drm/parameters/debug
|
||||
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4
|
||||
seatd-launch -- meson test --no-rebuild --timeout-multiplier 4 \
|
||||
--wrapper $(pwd)/../.gitlab-ci/virtme-scripts/per-test-asan.sh
|
||||
# note that we need to store the return value from the tests in order to
|
||||
# determine if the test suite ran successfully or not.
|
||||
TEST_RES=$?
|
||||
|
||||
@@ -208,6 +208,10 @@ my_function(void)
|
||||
parameter3, parameter4);
|
||||
```
|
||||
|
||||
- do not write fallback paths for failed simple memory allocations, use the
|
||||
`x*alloc()` wrappers from `shared/xalloc.h` instead or use
|
||||
`abort_oom_if_null()`
|
||||
|
||||
Conduct
|
||||
=======
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ bugs and shortcomings, we avoid unknown or variable behaviour as much as
|
||||
possible, including variable performance such as occasional spikes in frame
|
||||
display time.
|
||||
|
||||
Weston and libweston are not suitable for memory constrained environments
|
||||
where the compositor is expected to continue running even in the face of
|
||||
trivial memory allocations failing. If standard functions like `malloc()`
|
||||
fail for small allocations,
|
||||
[you can expect libweston to abort](https://gitlab.freedesktop.org/wayland/weston/-/issues/631).
|
||||
|
||||
A small suite of example or demo clients are also provided: though they can be
|
||||
useful in themselves, their main purpose is to be an example or test case for
|
||||
others building compositors or clients.
|
||||
@@ -93,13 +99,6 @@ the available configuration options and display backends. It can also be
|
||||
configured through a file on disk; more information on this can be found through
|
||||
`man weston.ini`.
|
||||
|
||||
In some special cases, such as when running remotely or without logind's session
|
||||
control, Weston may not be able to run directly from a text console. In these
|
||||
situations, you can instead execute the `weston-launch` helper, which will gain
|
||||
privileged access to input and output devices by running as root, then granting
|
||||
access to the main Weston binary running as your user. Running Weston this way
|
||||
is not recommended unless necessary.
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
@@ -294,7 +293,7 @@ Details:
|
||||
|
||||
- Child process execution and management will be outside of libweston.
|
||||
|
||||
- The different backends (drm, fbdev, x11, etc) will be an internal
|
||||
- The different backends (drm, x11, etc) will be an internal
|
||||
detail of libweston. Libweston will not support third party
|
||||
backends. However, hosting programs need to handle
|
||||
backend-specific configuration due to differences in behaviour and
|
||||
@@ -308,19 +307,12 @@ Details:
|
||||
|
||||
- xwayland ???
|
||||
|
||||
- weston-launch is still with libweston even though it can only launch
|
||||
Weston and nothing else. We would like to allow it to launch any compositor,
|
||||
but since it gives by design root access to input devices and DRM, how can
|
||||
we restrict it to intended programs?
|
||||
|
||||
There are still many more details to be decided.
|
||||
|
||||
|
||||
For packagers
|
||||
-------------
|
||||
|
||||
Always build Weston with --with-cairo=image.
|
||||
|
||||
The Weston project is (will be) intended to be split into several
|
||||
binary packages, each with its own dependencies. The maximal split
|
||||
would be roughly like this:
|
||||
@@ -337,15 +329,13 @@ would be roughly like this:
|
||||
|
||||
- xwayland (depends on X11/xcb libs)
|
||||
|
||||
- fbdev-backend (depends on libudev...)
|
||||
|
||||
- rdp-backend (depends on freerdp)
|
||||
|
||||
- weston (the executable, not parallel-installable):
|
||||
+ desktop shell
|
||||
+ ivi-shell
|
||||
+ fullscreen shell
|
||||
+ weston-info (deprecated), weston-terminal, etc. we install by default
|
||||
+ weston-terminal, etc. we install by default
|
||||
+ screen-share
|
||||
|
||||
- weston demos (not parallel-installable)
|
||||
|
||||
+29
-63
@@ -41,22 +41,23 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include "window.h"
|
||||
#include "shared/cairo-util.h"
|
||||
|
||||
#include <libweston/config-parser.h>
|
||||
#include <libweston/zalloc.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/xalloc.h"
|
||||
#include <libweston/zalloc.h>
|
||||
#include "shared/cairo-util.h"
|
||||
#include "shared/file-util.h"
|
||||
#include "shared/process-util.h"
|
||||
#include "shared/timespec-util.h"
|
||||
|
||||
#include "window.h"
|
||||
|
||||
#include "weston-desktop-shell-client-protocol.h"
|
||||
|
||||
#define DEFAULT_CLOCK_FORMAT CLOCK_FORMAT_MINUTES
|
||||
#define DEFAULT_SPACING 10
|
||||
|
||||
extern char **environ; /* defined by libc */
|
||||
|
||||
enum clock_format {
|
||||
CLOCK_FORMAT_MINUTES,
|
||||
CLOCK_FORMAT_SECONDS,
|
||||
@@ -142,9 +143,11 @@ struct panel_launcher {
|
||||
cairo_surface_t *icon;
|
||||
int focused, pressed;
|
||||
char *path;
|
||||
char *displayname;
|
||||
struct wl_list link;
|
||||
struct wl_array envp;
|
||||
struct wl_array argv;
|
||||
struct custom_env env;
|
||||
char * const *argp;
|
||||
char * const *envp;
|
||||
};
|
||||
|
||||
struct panel_clock {
|
||||
@@ -211,7 +214,6 @@ check_desktop_ready(struct window *window)
|
||||
static void
|
||||
panel_launcher_activate(struct panel_launcher *widget)
|
||||
{
|
||||
char **argv;
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
@@ -223,13 +225,11 @@ panel_launcher_activate(struct panel_launcher *widget)
|
||||
if (pid)
|
||||
return;
|
||||
|
||||
argv = widget->argv.data;
|
||||
|
||||
if (setsid() == -1)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (execve(argv[0], argv, widget->envp.data) < 0) {
|
||||
fprintf(stderr, "execl '%s' failed: %s\n", argv[0],
|
||||
if (execve(widget->argp[0], widget->argp, widget->envp) < 0) {
|
||||
fprintf(stderr, "execl '%s' failed: %s\n", widget->argp[0],
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
@@ -277,7 +277,7 @@ panel_launcher_motion_handler(struct widget *widget, struct input *input,
|
||||
{
|
||||
struct panel_launcher *launcher = data;
|
||||
|
||||
widget_set_tooltip(widget, basename((char *)launcher->path), x, y);
|
||||
widget_set_tooltip(widget, launcher->displayname, x, y);
|
||||
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
@@ -575,10 +575,10 @@ panel_configure(void *data,
|
||||
static void
|
||||
panel_destroy_launcher(struct panel_launcher *launcher)
|
||||
{
|
||||
wl_array_release(&launcher->argv);
|
||||
wl_array_release(&launcher->envp);
|
||||
custom_env_fini(&launcher->env);
|
||||
|
||||
free(launcher->path);
|
||||
free(launcher->displayname);
|
||||
|
||||
cairo_surface_destroy(launcher->icon);
|
||||
|
||||
@@ -678,58 +678,19 @@ load_icon_or_fallback(const char *icon)
|
||||
}
|
||||
|
||||
static void
|
||||
panel_add_launcher(struct panel *panel, const char *icon, const char *path)
|
||||
panel_add_launcher(struct panel *panel, const char *icon, const char *path, const char *displayname)
|
||||
{
|
||||
struct panel_launcher *launcher;
|
||||
char *start, *p, *eq, **ps;
|
||||
int i, j, k;
|
||||
|
||||
launcher = xzalloc(sizeof *launcher);
|
||||
launcher->icon = load_icon_or_fallback(icon);
|
||||
launcher->path = xstrdup(path);
|
||||
launcher->displayname = xstrdup(displayname);
|
||||
|
||||
wl_array_init(&launcher->envp);
|
||||
wl_array_init(&launcher->argv);
|
||||
for (i = 0; environ[i]; i++) {
|
||||
ps = wl_array_add(&launcher->envp, sizeof *ps);
|
||||
*ps = environ[i];
|
||||
}
|
||||
j = 0;
|
||||
|
||||
start = launcher->path;
|
||||
while (*start) {
|
||||
for (p = start, eq = NULL; *p && !isspace(*p); p++)
|
||||
if (*p == '=')
|
||||
eq = p;
|
||||
|
||||
if (eq && j == 0) {
|
||||
ps = launcher->envp.data;
|
||||
for (k = 0; k < i; k++)
|
||||
if (strncmp(ps[k], start, eq - start) == 0) {
|
||||
ps[k] = start;
|
||||
break;
|
||||
}
|
||||
if (k == i) {
|
||||
ps = wl_array_add(&launcher->envp, sizeof *ps);
|
||||
*ps = start;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
ps = wl_array_add(&launcher->argv, sizeof *ps);
|
||||
*ps = start;
|
||||
j++;
|
||||
}
|
||||
|
||||
while (*p && isspace(*p))
|
||||
*p++ = '\0';
|
||||
|
||||
start = p;
|
||||
}
|
||||
|
||||
ps = wl_array_add(&launcher->envp, sizeof *ps);
|
||||
*ps = NULL;
|
||||
ps = wl_array_add(&launcher->argv, sizeof *ps);
|
||||
*ps = NULL;
|
||||
custom_env_init_from_environ(&launcher->env);
|
||||
custom_env_add_from_exec_string(&launcher->env, launcher->path);
|
||||
launcher->envp = custom_env_get_envp(&launcher->env);
|
||||
launcher->argp = custom_env_get_argp(&launcher->env);
|
||||
|
||||
launcher->panel = panel;
|
||||
wl_list_insert(panel->launcher_list.prev, &launcher->link);
|
||||
@@ -1447,7 +1408,7 @@ static void
|
||||
panel_add_launchers(struct panel *panel, struct desktop *desktop)
|
||||
{
|
||||
struct weston_config_section *s;
|
||||
char *icon, *path;
|
||||
char *icon, *path, *displayname;
|
||||
const char *name;
|
||||
int count;
|
||||
|
||||
@@ -1459,9 +1420,12 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
|
||||
|
||||
weston_config_section_get_string(s, "icon", &icon, NULL);
|
||||
weston_config_section_get_string(s, "path", &path, NULL);
|
||||
weston_config_section_get_string(s, "displayname", &displayname, NULL);
|
||||
if (displayname == NULL)
|
||||
displayname = xstrdup(basename(path));
|
||||
|
||||
if (icon != NULL && path != NULL) {
|
||||
panel_add_launcher(panel, icon, path);
|
||||
panel_add_launcher(panel, icon, path, displayname);
|
||||
count++;
|
||||
} else {
|
||||
fprintf(stderr, "invalid launcher section\n");
|
||||
@@ -1469,6 +1433,7 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
|
||||
|
||||
free(icon);
|
||||
free(path);
|
||||
free(displayname);
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
@@ -1477,7 +1442,8 @@ panel_add_launchers(struct panel *panel, struct desktop *desktop)
|
||||
/* add default launcher */
|
||||
panel_add_launcher(panel,
|
||||
name,
|
||||
BINDIR "/weston-terminal");
|
||||
BINDIR "/weston-terminal",
|
||||
"Terminal");
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,8 +351,6 @@ axis_discrete_handler(struct widget *widget, struct input *input,
|
||||
* \param widget widget
|
||||
* \param input input device that caused the motion event
|
||||
* \param time time the event happened
|
||||
* \param x absolute x position
|
||||
* \param y absolute y position
|
||||
* \param x x position relative to the window
|
||||
* \param y y position relative to the window
|
||||
* \param data user data associated to the window
|
||||
|
||||
-504
@@ -1,504 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
struct gears {
|
||||
struct window *window;
|
||||
struct widget *widget;
|
||||
|
||||
struct display *d;
|
||||
|
||||
EGLDisplay display;
|
||||
EGLDisplay config;
|
||||
EGLContext context;
|
||||
GLfloat angle;
|
||||
|
||||
struct {
|
||||
GLfloat rotx;
|
||||
GLfloat roty;
|
||||
} view;
|
||||
|
||||
int button_down;
|
||||
int last_x, last_y;
|
||||
|
||||
GLint gear_list[3];
|
||||
int fullscreen;
|
||||
int frames;
|
||||
uint32_t last_fps;
|
||||
};
|
||||
|
||||
struct gear_template {
|
||||
GLfloat material[4];
|
||||
GLfloat inner_radius;
|
||||
GLfloat outer_radius;
|
||||
GLfloat width;
|
||||
GLint teeth;
|
||||
GLfloat tooth_depth;
|
||||
};
|
||||
|
||||
static const struct gear_template gear_templates[] = {
|
||||
{ { 0.8, 0.1, 0.0, 1.0 }, 1.0, 4.0, 1.0, 20, 0.7 },
|
||||
{ { 0.0, 0.8, 0.2, 1.0 }, 0.5, 2.0, 2.0, 10, 0.7 },
|
||||
{ { 0.2, 0.2, 1.0, 1.0 }, 1.3, 2.0, 0.5, 10, 0.7 },
|
||||
};
|
||||
|
||||
static GLfloat light_pos[4] = {5.0, 5.0, 10.0, 0.0};
|
||||
|
||||
static void die(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s", msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void
|
||||
make_gear(const struct gear_template *t)
|
||||
{
|
||||
GLint i;
|
||||
GLfloat r0, r1, r2;
|
||||
GLfloat angle, da;
|
||||
GLfloat u, v, len;
|
||||
|
||||
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, t->material);
|
||||
|
||||
r0 = t->inner_radius;
|
||||
r1 = t->outer_radius - t->tooth_depth / 2.0;
|
||||
r2 = t->outer_radius + t->tooth_depth / 2.0;
|
||||
|
||||
da = 2.0 * M_PI / t->teeth / 4.0;
|
||||
|
||||
glShadeModel(GL_FLAT);
|
||||
|
||||
glNormal3f(0.0, 0.0, 1.0);
|
||||
|
||||
/* draw front face */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
|
||||
if (i < t->teeth) {
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw front sides of teeth */
|
||||
glBegin(GL_QUADS);
|
||||
da = 2.0 * M_PI / t->teeth / 4.0;
|
||||
for (i = 0; i < t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
glNormal3f(0.0, 0.0, -1.0);
|
||||
|
||||
/* draw back face */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
|
||||
if (i < t->teeth) {
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
|
||||
}
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw back sides of teeth */
|
||||
glBegin(GL_QUADS);
|
||||
da = 2.0 * M_PI / t->teeth / 4.0;
|
||||
for (i = 0; i < t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
|
||||
/* draw outward faces of teeth */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i < t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle), r1 * sin(angle), -t->width * 0.5);
|
||||
u = r2 * cos(angle + da) - r1 * cos(angle);
|
||||
v = r2 * sin(angle + da) - r1 * sin(angle);
|
||||
len = sqrt(u * u + v * v);
|
||||
u /= len;
|
||||
v /= len;
|
||||
glNormal3f(v, -u, 0.0);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -t->width * 0.5);
|
||||
glNormal3f(cos(angle), sin(angle), 0.0);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), t->width * 0.5);
|
||||
glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -t->width * 0.5);
|
||||
u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
|
||||
v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
|
||||
glNormal3f(v, -u, 0.0);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -t->width * 0.5);
|
||||
glNormal3f(cos(angle), sin(angle), 0.0);
|
||||
}
|
||||
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), t->width * 0.5);
|
||||
glVertex3f(r1 * cos(0), r1 * sin(0), -t->width * 0.5);
|
||||
|
||||
glEnd();
|
||||
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
/* draw inside radius cylinder */
|
||||
glBegin(GL_QUAD_STRIP);
|
||||
for (i = 0; i <= t->teeth; i++) {
|
||||
angle = i * 2.0 * M_PI / t->teeth;
|
||||
glNormal3f(-cos(angle), -sin(angle), 0.0);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), -t->width * 0.5);
|
||||
glVertex3f(r0 * cos(angle), r0 * sin(angle), t->width * 0.5);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
|
||||
static void
|
||||
update_fps(struct gears *gears, uint32_t time)
|
||||
{
|
||||
long diff_ms;
|
||||
static bool first_call = true;
|
||||
|
||||
if (first_call) {
|
||||
gears->last_fps = time;
|
||||
first_call = false;
|
||||
} else
|
||||
gears->frames++;
|
||||
|
||||
diff_ms = time - gears->last_fps;
|
||||
|
||||
if (diff_ms > 5000) {
|
||||
float seconds = diff_ms / 1000.0;
|
||||
float fps = gears->frames / seconds;
|
||||
|
||||
printf("%d frames in %6.3f seconds = %6.3f FPS\n", gears->frames, seconds, fps);
|
||||
fflush(stdout);
|
||||
|
||||
gears->frames = 0;
|
||||
gears->last_fps = time;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
|
||||
update_fps(gears, time);
|
||||
|
||||
gears->angle = (GLfloat) (time % 8192) * 360 / 8192.0;
|
||||
|
||||
window_schedule_redraw(gears->window);
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener listener = {
|
||||
frame_callback
|
||||
};
|
||||
|
||||
static int
|
||||
motion_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, float x, float y, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
int offset_x, offset_y;
|
||||
float step = 0.5;
|
||||
|
||||
if (gears->button_down) {
|
||||
offset_x = x - gears->last_x;
|
||||
offset_y = y - gears->last_y;
|
||||
gears->last_x = x;
|
||||
gears->last_y = y;
|
||||
gears->view.roty += offset_x * step;
|
||||
gears->view.rotx += offset_y * step;
|
||||
if (gears->view.roty >= 360)
|
||||
gears->view.roty = gears->view.roty - 360;
|
||||
if (gears->view.roty <= 0)
|
||||
gears->view.roty = gears->view.roty + 360;
|
||||
if (gears->view.rotx >= 360)
|
||||
gears->view.rotx = gears->view.rotx - 360;
|
||||
if (gears->view.rotx <= 0)
|
||||
gears->view.rotx = gears->view.rotx + 360;
|
||||
}
|
||||
|
||||
return CURSOR_LEFT_PTR;
|
||||
}
|
||||
|
||||
static void
|
||||
button_handler(struct widget *widget, struct input *input,
|
||||
uint32_t time, uint32_t button,
|
||||
enum wl_pointer_button_state state, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
|
||||
if (button == BTN_LEFT) {
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
gears->button_down = 1;
|
||||
input_get_position(input,
|
||||
&gears->last_x, &gears->last_y);
|
||||
} else {
|
||||
gears->button_down = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
redraw_handler(struct widget *widget, void *data)
|
||||
{
|
||||
struct rectangle window_allocation;
|
||||
struct rectangle allocation;
|
||||
struct wl_callback *callback;
|
||||
struct gears *gears = data;
|
||||
|
||||
widget_get_allocation(gears->widget, &allocation);
|
||||
window_get_allocation(gears->window, &window_allocation);
|
||||
|
||||
if (display_acquire_window_surface(gears->d,
|
||||
gears->window,
|
||||
gears->context) < 0) {
|
||||
die("Unable to acquire window surface, "
|
||||
"compiled without cairo-egl?\n");
|
||||
}
|
||||
|
||||
glViewport(allocation.x,
|
||||
window_allocation.height - allocation.height - allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
glScissor(allocation.x,
|
||||
window_allocation.height - allocation.height - allocation.y,
|
||||
allocation.width, allocation.height);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glPushMatrix();
|
||||
|
||||
glTranslatef(0.0, 0.0, -50);
|
||||
|
||||
glRotatef(gears->view.rotx, 1.0, 0.0, 0.0);
|
||||
glRotatef(gears->view.roty, 0.0, 1.0, 0.0);
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-3.0, -2.0, 0.0);
|
||||
glRotatef(gears->angle, 0.0, 0.0, 1.0);
|
||||
glCallList(gears->gear_list[0]);
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(3.1, -2.0, 0.0);
|
||||
glRotatef(-2.0 * gears->angle - 9.0, 0.0, 0.0, 1.0);
|
||||
glCallList(gears->gear_list[1]);
|
||||
glPopMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glTranslatef(-3.1, 4.2, 0.0);
|
||||
glRotatef(-2.0 * gears->angle - 25.0, 0.0, 0.0, 1.0);
|
||||
glCallList(gears->gear_list[2]);
|
||||
glPopMatrix();
|
||||
|
||||
glPopMatrix();
|
||||
|
||||
glFlush();
|
||||
|
||||
display_release_window_surface(gears->d, gears->window);
|
||||
|
||||
callback = wl_surface_frame(window_get_wl_surface(gears->window));
|
||||
wl_callback_add_listener(callback, &listener, gears);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_handler(struct widget *widget,
|
||||
int32_t width, int32_t height, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
int32_t size, big, small;
|
||||
|
||||
/* Constrain child size to be square and at least 300x300 */
|
||||
if (width < height) {
|
||||
small = width;
|
||||
big = height;
|
||||
} else {
|
||||
small = height;
|
||||
big = width;
|
||||
}
|
||||
|
||||
if (gears->fullscreen)
|
||||
size = small;
|
||||
else
|
||||
size = big;
|
||||
|
||||
widget_set_size(gears->widget, size, size);
|
||||
}
|
||||
|
||||
static void
|
||||
keyboard_focus_handler(struct window *window,
|
||||
struct input *device, void *data)
|
||||
{
|
||||
window_schedule_redraw(window);
|
||||
}
|
||||
|
||||
static void
|
||||
fullscreen_handler(struct window *window, void *data)
|
||||
{
|
||||
struct gears *gears = data;
|
||||
|
||||
gears->fullscreen ^= 1;
|
||||
window_set_fullscreen(window, gears->fullscreen);
|
||||
}
|
||||
|
||||
static struct gears *
|
||||
gears_create(struct display *display)
|
||||
{
|
||||
const int width = 450, height = 500;
|
||||
struct gears *gears;
|
||||
int i;
|
||||
|
||||
gears = zalloc(sizeof *gears);
|
||||
gears->d = display;
|
||||
gears->window = window_create(display);
|
||||
gears->widget = window_frame_create(gears->window, gears);
|
||||
window_set_title(gears->window, "Wayland Gears");
|
||||
window_set_appid(gears->window, "org.freedesktop.weston.wayland-gears");
|
||||
|
||||
gears->display = display_get_egl_display(gears->d);
|
||||
if (gears->display == NULL)
|
||||
die("failed to create egl display\n");
|
||||
|
||||
eglBindAPI(EGL_OPENGL_API);
|
||||
|
||||
gears->config = display_get_argb_egl_config(gears->d);
|
||||
|
||||
gears->context = eglCreateContext(gears->display, gears->config,
|
||||
EGL_NO_CONTEXT, NULL);
|
||||
if (gears->context == NULL)
|
||||
die("failed to create context\n");
|
||||
|
||||
if (!eglMakeCurrent(gears->display, NULL, NULL, gears->context))
|
||||
die("failed to make context current\n");
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
gears->gear_list[i] = glGenLists(1);
|
||||
glNewList(gears->gear_list[i], GL_COMPILE);
|
||||
make_gear(&gear_templates[i]);
|
||||
glEndList();
|
||||
}
|
||||
|
||||
gears->button_down = 0;
|
||||
gears->last_x = 0;
|
||||
gears->last_y = 0;
|
||||
|
||||
gears->view.rotx = 20.0;
|
||||
gears->view.roty = 30.0;
|
||||
|
||||
printf("Warning: FPS count is limited by the wayland compositor or monitor refresh rate\n");
|
||||
|
||||
glEnable(GL_NORMALIZE);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 200.0);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_LIGHT0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glClearColor(0, 0, 0, 0.92);
|
||||
|
||||
window_set_user_data(gears->window, gears);
|
||||
widget_set_resize_handler(gears->widget, resize_handler);
|
||||
widget_set_redraw_handler(gears->widget, redraw_handler);
|
||||
widget_set_button_handler(gears->widget, button_handler);
|
||||
widget_set_motion_handler(gears->widget, motion_handler);
|
||||
window_set_keyboard_focus_handler(gears->window,
|
||||
keyboard_focus_handler);
|
||||
window_set_fullscreen_handler(gears->window, fullscreen_handler);
|
||||
|
||||
window_schedule_resize(gears->window, width, height);
|
||||
|
||||
return gears;
|
||||
}
|
||||
|
||||
static void
|
||||
gears_destroy(struct gears *gears)
|
||||
{
|
||||
widget_destroy(gears->widget);
|
||||
window_destroy(gears->window);
|
||||
free(gears);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
struct gears *gears;
|
||||
|
||||
d = display_create(&argc, argv);
|
||||
if (d == NULL) {
|
||||
fprintf(stderr, "failed to create display: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
gears = gears_create(d);
|
||||
display_run(d);
|
||||
|
||||
gears_destroy(gears);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+12
-16
@@ -35,6 +35,10 @@ dep_toytoolkit = declare_dependency(
|
||||
link_with: lib_toytoolkit,
|
||||
dependencies: deps_toytoolkit,
|
||||
)
|
||||
dep_gbm = dependency('gbm', required: false)
|
||||
if dep_gbm.found() and dep_gbm.version().version_compare('>= 21.3')
|
||||
config_h.set('HAVE_GBM_BO_CREATE_WITH_MODIFIERS2', '1')
|
||||
endif
|
||||
|
||||
simple_clients_enabled = get_option('simple-clients')
|
||||
simple_build_all = simple_clients_enabled.contains('all')
|
||||
@@ -60,6 +64,8 @@ simple_clients = [
|
||||
'../libweston/pixel-formats.c',
|
||||
linux_dmabuf_unstable_v1_client_protocol_h,
|
||||
linux_dmabuf_unstable_v1_protocol_c,
|
||||
presentation_time_client_protocol_h,
|
||||
presentation_time_protocol_c,
|
||||
xdg_shell_client_protocol_h,
|
||||
xdg_shell_protocol_c,
|
||||
],
|
||||
@@ -125,7 +131,12 @@ simple_clients = [
|
||||
ivi_application_client_protocol_h,
|
||||
ivi_application_protocol_c,
|
||||
],
|
||||
'dep_objs': [ dep_wayland_client, dep_libshared, dep_libm ],
|
||||
'dep_objs': [
|
||||
dep_libm,
|
||||
dep_libshared,
|
||||
dep_matrix_c,
|
||||
dep_wayland_client,
|
||||
],
|
||||
'deps': [ 'egl', 'wayland-egl', 'glesv2', 'wayland-cursor' ],
|
||||
'options': [ 'renderer-gl' ]
|
||||
},
|
||||
@@ -217,21 +228,6 @@ tools_list = [
|
||||
],
|
||||
'deps': [ dep_wayland_client ]
|
||||
},
|
||||
{
|
||||
'name': 'info',
|
||||
'sources': [
|
||||
'weston-info.c',
|
||||
presentation_time_client_protocol_h,
|
||||
presentation_time_protocol_c,
|
||||
linux_dmabuf_unstable_v1_client_protocol_h,
|
||||
linux_dmabuf_unstable_v1_protocol_c,
|
||||
tablet_unstable_v2_client_protocol_h,
|
||||
tablet_unstable_v2_protocol_c,
|
||||
xdg_output_unstable_v1_client_protocol_h,
|
||||
xdg_output_unstable_v1_protocol_c,
|
||||
],
|
||||
'deps': [ dep_wayland_client, dep_libshared ]
|
||||
},
|
||||
{
|
||||
'name': 'terminal',
|
||||
'sources': [ 'terminal.c' ],
|
||||
|
||||
@@ -1,374 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2013 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <wayland-egl.h>
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
#include <GLES2/gl2.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#include "shared/platform.h"
|
||||
|
||||
struct window;
|
||||
struct seat;
|
||||
|
||||
struct nested_client {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
|
||||
EGLDisplay egl_display;
|
||||
EGLContext egl_context;
|
||||
EGLConfig egl_config;
|
||||
EGLSurface egl_surface;
|
||||
struct program *color_program;
|
||||
|
||||
GLuint vert, frag, program;
|
||||
GLuint rotation;
|
||||
GLuint pos;
|
||||
GLuint col;
|
||||
|
||||
struct wl_surface *surface;
|
||||
struct wl_egl_window *native;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
#define POS 0
|
||||
#define COL 1
|
||||
|
||||
static GLuint
|
||||
create_shader(const char *source, GLenum shader_type)
|
||||
{
|
||||
GLuint shader;
|
||||
GLint status;
|
||||
|
||||
shader = glCreateShader(shader_type);
|
||||
if (shader == 0)
|
||||
return 0;
|
||||
|
||||
glShaderSource(shader, 1, (const char **) &source, NULL);
|
||||
glCompileShader(shader);
|
||||
|
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[1000];
|
||||
GLsizei len;
|
||||
glGetShaderInfoLog(shader, 1000, &len, log);
|
||||
fprintf(stderr, "Error: compiling %s: %.*s\n",
|
||||
shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
|
||||
len, log);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
static void
|
||||
create_program(struct nested_client *client,
|
||||
const char *vert, const char *frag)
|
||||
{
|
||||
GLint status;
|
||||
|
||||
client->vert = create_shader(vert, GL_VERTEX_SHADER);
|
||||
client->frag = create_shader(frag, GL_FRAGMENT_SHADER);
|
||||
|
||||
client->program = glCreateProgram();
|
||||
glAttachShader(client->program, client->frag);
|
||||
glAttachShader(client->program, client->vert);
|
||||
glBindAttribLocation(client->program, POS, "pos");
|
||||
glBindAttribLocation(client->program, COL, "color");
|
||||
glLinkProgram(client->program);
|
||||
|
||||
glGetProgramiv(client->program, GL_LINK_STATUS, &status);
|
||||
if (!status) {
|
||||
char log[1000];
|
||||
GLsizei len;
|
||||
glGetProgramInfoLog(client->program, 1000, &len, log);
|
||||
fprintf(stderr, "Error: linking:\n%.*s\n", len, log);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
client->rotation =
|
||||
glGetUniformLocation(client->program, "rotation");
|
||||
}
|
||||
|
||||
static const char vertex_shader_text[] =
|
||||
"uniform mat4 rotation;\n"
|
||||
"attribute vec4 pos;\n"
|
||||
"attribute vec4 color;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_Position = rotation * pos;\n"
|
||||
" v_color = color;\n"
|
||||
"}\n";
|
||||
|
||||
static const char color_fragment_shader_text[] =
|
||||
"precision mediump float;\n"
|
||||
"varying vec4 v_color;\n"
|
||||
"void main() {\n"
|
||||
" gl_FragColor = v_color;\n"
|
||||
"}\n";
|
||||
|
||||
static void
|
||||
render_triangle(struct nested_client *client, uint32_t time)
|
||||
{
|
||||
static const GLfloat verts[3][2] = {
|
||||
{ -0.5, -0.5 },
|
||||
{ 0.5, -0.5 },
|
||||
{ 0, 0.5 }
|
||||
};
|
||||
static const GLfloat colors[3][3] = {
|
||||
{ 1, 0, 0 },
|
||||
{ 0, 1, 0 },
|
||||
{ 0, 0, 1 }
|
||||
};
|
||||
GLfloat angle;
|
||||
GLfloat rotation[4][4] = {
|
||||
{ 1, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0 },
|
||||
{ 0, 0, 1, 0 },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
static const int32_t speed_div = 5;
|
||||
static uint32_t start_time = 0;
|
||||
|
||||
if (client->program == 0)
|
||||
create_program(client, vertex_shader_text,
|
||||
color_fragment_shader_text);
|
||||
|
||||
if (start_time == 0)
|
||||
start_time = time;
|
||||
|
||||
angle = ((time - start_time) / speed_div) % 360 * M_PI / 180.0;
|
||||
rotation[0][0] = cos(angle);
|
||||
rotation[0][2] = sin(angle);
|
||||
rotation[2][0] = -sin(angle);
|
||||
rotation[2][2] = cos(angle);
|
||||
|
||||
glClearColor(0.4, 0.4, 0.4, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
glUseProgram(client->program);
|
||||
|
||||
glViewport(0, 0, client->width, client->height);
|
||||
|
||||
glUniformMatrix4fv(client->rotation, 1, GL_FALSE,
|
||||
(GLfloat *) rotation);
|
||||
|
||||
glVertexAttribPointer(POS, 2, GL_FLOAT, GL_FALSE, 0, verts);
|
||||
glVertexAttribPointer(COL, 3, GL_FLOAT, GL_FALSE, 0, colors);
|
||||
glEnableVertexAttribArray(POS);
|
||||
glEnableVertexAttribArray(COL);
|
||||
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
glDisableVertexAttribArray(POS);
|
||||
glDisableVertexAttribArray(COL);
|
||||
|
||||
glFlush();
|
||||
}
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time);
|
||||
|
||||
static const struct wl_callback_listener frame_listener = {
|
||||
frame_callback
|
||||
};
|
||||
|
||||
static void
|
||||
frame_callback(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{
|
||||
struct nested_client *client = data;
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
|
||||
callback = wl_surface_frame(client->surface);
|
||||
wl_callback_add_listener(callback, &frame_listener, client);
|
||||
|
||||
render_triangle(client, time);
|
||||
|
||||
eglSwapBuffers(client->egl_display, client->egl_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
{
|
||||
struct nested_client *client = data;
|
||||
|
||||
if (strcmp(interface, "wl_compositor") == 0) {
|
||||
client->compositor =
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_compositor_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
registry_handle_global,
|
||||
registry_handle_global_remove
|
||||
};
|
||||
|
||||
static struct nested_client *
|
||||
nested_client_create(void)
|
||||
{
|
||||
static const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
static const EGLint config_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint major, minor, n;
|
||||
EGLBoolean ret;
|
||||
|
||||
struct nested_client *client;
|
||||
|
||||
client = malloc(sizeof *client);
|
||||
if (client == NULL)
|
||||
return NULL;
|
||||
|
||||
client->width = 250;
|
||||
client->height = 250;
|
||||
|
||||
client->display = wl_display_connect(NULL);
|
||||
|
||||
client->registry = wl_display_get_registry(client->display);
|
||||
wl_registry_add_listener(client->registry,
|
||||
®istry_listener, client);
|
||||
|
||||
/* get globals */
|
||||
wl_display_roundtrip(client->display);
|
||||
|
||||
client->egl_display =
|
||||
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
|
||||
client->display, NULL);
|
||||
if (client->egl_display == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = eglInitialize(client->egl_display, &major, &minor);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
ret = eglBindAPI(EGL_OPENGL_ES_API);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret = eglChooseConfig(client->egl_display, config_attribs,
|
||||
&client->egl_config, 1, &n);
|
||||
if (!ret || n != 1)
|
||||
return NULL;
|
||||
|
||||
client->egl_context = eglCreateContext(client->egl_display,
|
||||
client->egl_config,
|
||||
EGL_NO_CONTEXT,
|
||||
context_attribs);
|
||||
if (!client->egl_context)
|
||||
return NULL;
|
||||
|
||||
client->surface = wl_compositor_create_surface(client->compositor);
|
||||
|
||||
client->native = wl_egl_window_create(client->surface,
|
||||
client->width, client->height);
|
||||
|
||||
client->egl_surface = weston_platform_create_egl_surface(client->egl_display,
|
||||
client->egl_config,
|
||||
client->native, NULL);
|
||||
|
||||
eglMakeCurrent(client->egl_display, client->egl_surface,
|
||||
client->egl_surface, client->egl_context);
|
||||
|
||||
wl_egl_window_resize(client->native,
|
||||
client->width, client->height, 0, 0);
|
||||
|
||||
frame_callback(client, NULL, 0);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
static void
|
||||
nested_client_destroy(struct nested_client *client)
|
||||
{
|
||||
eglMakeCurrent(client->egl_display,
|
||||
EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
weston_platform_destroy_egl_surface(client->egl_display,
|
||||
client->egl_surface);
|
||||
wl_egl_window_destroy(client->native);
|
||||
|
||||
wl_surface_destroy(client->surface);
|
||||
|
||||
if (client->compositor)
|
||||
wl_compositor_destroy(client->compositor);
|
||||
|
||||
wl_registry_destroy(client->registry);
|
||||
eglTerminate(client->egl_display);
|
||||
eglReleaseThread();
|
||||
wl_display_flush(client->display);
|
||||
wl_display_disconnect(client->display);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
struct nested_client *client;
|
||||
int ret = 0;
|
||||
|
||||
if (getenv("WAYLAND_SOCKET") == NULL) {
|
||||
fprintf(stderr,
|
||||
"must be run by nested, don't run standalone\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
client = nested_client_create();
|
||||
if (client == NULL)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
while (ret != -1)
|
||||
ret = wl_display_dispatch(client->display);
|
||||
|
||||
nested_client_destroy(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
-1139
File diff suppressed because it is too large
Load Diff
@@ -765,7 +765,7 @@ registry_handle_global(void *data, struct wl_registry *registry,
|
||||
} else if (strcmp(interface, "xdg_wm_base") == 0) {
|
||||
d->wm_base =
|
||||
wl_registry_bind(registry, name,
|
||||
&xdg_wm_base_interface, version);
|
||||
&xdg_wm_base_interface, 1);
|
||||
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||
d->shm = wl_registry_bind(registry,
|
||||
name, &wl_shm_interface, 1);
|
||||
|
||||
@@ -343,12 +343,22 @@ create_dmabuf_buffer(struct display *display, struct buffer *buffer,
|
||||
|
||||
#ifdef HAVE_GBM_MODIFIERS
|
||||
if (display->modifiers_count > 0) {
|
||||
#ifdef HAVE_GBM_BO_CREATE_WITH_MODIFIERS2
|
||||
buffer->bo = gbm_bo_create_with_modifiers2(display->gbm.device,
|
||||
buffer->width,
|
||||
buffer->height,
|
||||
buffer->format,
|
||||
display->modifiers,
|
||||
display->modifiers_count,
|
||||
GBM_BO_USE_RENDERING);
|
||||
#else
|
||||
buffer->bo = gbm_bo_create_with_modifiers(display->gbm.device,
|
||||
buffer->width,
|
||||
buffer->height,
|
||||
buffer->format,
|
||||
display->modifiers,
|
||||
display->modifiers_count);
|
||||
#endif
|
||||
if (buffer->bo)
|
||||
buffer->modifier = gbm_bo_get_modifier(buffer->bo);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <libweston/pixel-formats.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
||||
#include "presentation-time-client-protocol.h"
|
||||
|
||||
#include <xf86drm.h>
|
||||
#include <gbm.h>
|
||||
@@ -49,6 +50,11 @@
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
#define L_LINE "│ "
|
||||
#define L_VAL "├───"
|
||||
#define L_LAST "└───"
|
||||
#define L_GAP " "
|
||||
|
||||
#define NUM_BUFFERS 4
|
||||
|
||||
/* We have to hack the DRM-backend to pretend that planes of the underlying
|
||||
@@ -135,6 +141,7 @@ struct display {
|
||||
struct output output;
|
||||
struct xdg_wm_base *wm_base;
|
||||
struct zwp_linux_dmabuf_v1 *dmabuf;
|
||||
struct wp_presentation *presentation;
|
||||
struct gbm_device *gbm_device;
|
||||
struct egl egl;
|
||||
};
|
||||
@@ -166,12 +173,14 @@ struct window {
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct wl_callback *callback;
|
||||
struct wp_presentation_feedback *presentation_feedback;
|
||||
bool wait_for_configure;
|
||||
uint32_t n_redraws;
|
||||
bool presented_zero_copy;
|
||||
struct zwp_linux_dmabuf_feedback_v1 *dmabuf_feedback_obj;
|
||||
struct dmabuf_feedback dmabuf_feedback, pending_dmabuf_feedback;
|
||||
int card_fd;
|
||||
struct drm_format format;
|
||||
uint32_t bo_flags;
|
||||
struct buffer buffers[NUM_BUFFERS];
|
||||
};
|
||||
|
||||
@@ -453,7 +462,7 @@ buffer_free(struct buffer *buf)
|
||||
static void
|
||||
create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
|
||||
uint32_t height, uint32_t format, unsigned int count_modifiers,
|
||||
uint64_t *modifiers);
|
||||
uint64_t *modifiers, uint32_t bo_flags);
|
||||
|
||||
static void
|
||||
buffer_recreate(struct buffer *buf)
|
||||
@@ -466,7 +475,7 @@ buffer_recreate(struct buffer *buf)
|
||||
create_dmabuf_buffer(window, buf, width, height,
|
||||
window->format.format,
|
||||
window->format.modifiers.size / sizeof(uint64_t),
|
||||
window->format.modifiers.data);
|
||||
window->format.modifiers.data, window->bo_flags);
|
||||
buf->recreate = false;
|
||||
}
|
||||
|
||||
@@ -516,7 +525,7 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
||||
static void
|
||||
create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
|
||||
uint32_t height, uint32_t format, unsigned int count_modifiers,
|
||||
uint64_t *modifiers)
|
||||
uint64_t *modifiers, uint32_t bo_flags)
|
||||
{
|
||||
struct display *display = window->display;
|
||||
static uint32_t flags = 0;
|
||||
@@ -531,10 +540,18 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
|
||||
|
||||
#ifdef HAVE_GBM_MODIFIERS
|
||||
if (count_modifiers > 0) {
|
||||
#ifdef HAVE_GBM_BO_CREATE_WITH_MODIFIERS2
|
||||
buf->bo = gbm_bo_create_with_modifiers2(display->gbm_device,
|
||||
buf->width, buf->height,
|
||||
format, modifiers,
|
||||
count_modifiers,
|
||||
bo_flags);
|
||||
#else
|
||||
buf->bo = gbm_bo_create_with_modifiers(display->gbm_device,
|
||||
buf->width, buf->height,
|
||||
format, modifiers,
|
||||
count_modifiers);
|
||||
#endif
|
||||
if (buf->bo)
|
||||
buf->modifier = gbm_bo_get_modifier(buf->bo);
|
||||
}
|
||||
@@ -543,7 +560,7 @@ create_dmabuf_buffer(struct window *window, struct buffer *buf, uint32_t width,
|
||||
if (!buf->bo) {
|
||||
buf->bo = gbm_bo_create(display->gbm_device, buf->width,
|
||||
buf->height, buf->format,
|
||||
GBM_BO_USE_RENDERING);
|
||||
bo_flags);
|
||||
buf->modifier = DRM_FORMAT_MOD_INVALID;
|
||||
}
|
||||
|
||||
@@ -639,6 +656,7 @@ render(struct buffer *buffer)
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener;
|
||||
static const struct wp_presentation_feedback_listener presentation_feedback_listener;
|
||||
|
||||
static void
|
||||
redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
@@ -660,6 +678,18 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
|
||||
window->callback = wl_surface_frame(window->surface);
|
||||
wl_callback_add_listener(window->callback, &frame_listener, window);
|
||||
|
||||
if (window->presentation_feedback)
|
||||
wp_presentation_feedback_destroy(window->presentation_feedback);
|
||||
if (window->display->presentation) {
|
||||
window->presentation_feedback =
|
||||
wp_presentation_feedback(window->display->presentation,
|
||||
window->surface);
|
||||
wp_presentation_feedback_add_listener(window->presentation_feedback,
|
||||
&presentation_feedback_listener,
|
||||
window);
|
||||
}
|
||||
|
||||
wl_surface_commit(window->surface);
|
||||
buf->status = IN_USE;
|
||||
|
||||
@@ -674,6 +704,48 @@ static const struct wl_callback_listener frame_listener = {
|
||||
redraw
|
||||
};
|
||||
|
||||
static void presentation_feedback_handle_sync_output(void *data,
|
||||
struct wp_presentation_feedback *feedback,
|
||||
struct wl_output *output)
|
||||
{
|
||||
}
|
||||
|
||||
static void presentation_feedback_handle_presented(void *data,
|
||||
struct wp_presentation_feedback *feedback,
|
||||
uint32_t tv_sec_hi,
|
||||
uint32_t tv_sec_lo,
|
||||
uint32_t tv_nsec,
|
||||
uint32_t refresh,
|
||||
uint32_t seq_hi,
|
||||
uint32_t seq_lo,
|
||||
uint32_t flags)
|
||||
{
|
||||
struct window *window = data;
|
||||
bool zero_copy = flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
|
||||
|
||||
if (zero_copy && !window->presented_zero_copy) {
|
||||
fprintf(stderr, "Presenting in zero-copy mode\n");
|
||||
}
|
||||
if (!zero_copy && window->presented_zero_copy) {
|
||||
fprintf(stderr, "Stopped presenting in zero-copy mode\n");
|
||||
}
|
||||
|
||||
window->presented_zero_copy = zero_copy;
|
||||
wp_presentation_feedback_destroy(feedback);
|
||||
}
|
||||
|
||||
static void presentation_feedback_handle_discarded(void *data,
|
||||
struct wp_presentation_feedback *feedback)
|
||||
{
|
||||
wp_presentation_feedback_destroy(feedback);
|
||||
}
|
||||
|
||||
static const struct wp_presentation_feedback_listener presentation_feedback_listener = {
|
||||
.sync_output = presentation_feedback_handle_sync_output,
|
||||
.presented = presentation_feedback_handle_presented,
|
||||
.discarded = presentation_feedback_handle_discarded,
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_surface_handle_configure(void *data, struct xdg_surface *surface,
|
||||
uint32_t serial)
|
||||
@@ -810,6 +882,8 @@ destroy_window(struct window *window)
|
||||
|
||||
if (window->callback)
|
||||
wl_callback_destroy(window->callback);
|
||||
if (window->presentation_feedback)
|
||||
wp_presentation_feedback_destroy(window->presentation_feedback);
|
||||
|
||||
for (i = 0; i < NUM_BUFFERS; i++)
|
||||
if (window->buffers[i].buffer)
|
||||
@@ -873,7 +947,7 @@ create_window(struct display *display)
|
||||
create_dmabuf_buffer(window, &window->buffers[i], width, height,
|
||||
window->format.format,
|
||||
window->format.modifiers.size / sizeof(uint64_t),
|
||||
window->format.modifiers.data);
|
||||
window->format.modifiers.data, window->bo_flags);
|
||||
|
||||
|
||||
window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
|
||||
@@ -1000,8 +1074,7 @@ dmabuf_feedback_main_device(void *data,
|
||||
drm_node = get_drm_node(feedback->main_device, false);
|
||||
assert(drm_node && "error: failed to retrieve drm node");
|
||||
|
||||
fprintf(stderr, "compositor sent main_device event for dma-buf feedback - %s\n",
|
||||
drm_node);
|
||||
fprintf(stderr, "feedback: main device %s\n", drm_node);
|
||||
|
||||
if (!window->card_fd) {
|
||||
window->card_fd = open(drm_node, O_RDWR | O_CLOEXEC);
|
||||
@@ -1110,11 +1183,11 @@ print_tranche_format_modifier(uint32_t format, uint64_t modifier)
|
||||
char fourcc_str[5];
|
||||
|
||||
fourcc2str(format, fourcc_str, sizeof(fourcc_str));
|
||||
len = asprintf(&format_str, "0x%08x (%s)", format, fourcc_str);
|
||||
len = asprintf(&format_str, "%s (0x%08x)", fourcc_str, format);
|
||||
}
|
||||
assert(len > 0);
|
||||
|
||||
fprintf(stderr, "│ ├────────tranche format/modifier pair - format %s, modifier %s\n",
|
||||
fprintf(stderr, L_LINE L_VAL " format %s, modifier %s\n",
|
||||
format_str, mod_name);
|
||||
|
||||
free(format_str);
|
||||
@@ -1131,14 +1204,14 @@ print_dmabuf_feedback_tranche(struct dmabuf_feedback_tranche *tranche)
|
||||
drm_node = get_drm_node(tranche->target_device, tranche->is_scanout_tranche);
|
||||
assert(drm_node && "error: could not retrieve drm node");
|
||||
|
||||
fprintf(stderr, "├──────target_device for tranche - %s\n", drm_node);
|
||||
fprintf(stderr, "│ └scanout tranche? %s\n", tranche->is_scanout_tranche ? "yes" : "no");
|
||||
fprintf(stderr, L_VAL " tranche: target device %s, %s\n",
|
||||
drm_node, tranche->is_scanout_tranche ? "scanout" : "no flags");
|
||||
|
||||
wl_array_for_each(fmt, &tranche->formats.arr)
|
||||
wl_array_for_each(mod, &fmt->modifiers)
|
||||
print_tranche_format_modifier(fmt->format, *mod);
|
||||
|
||||
fprintf(stderr, "│ └end of tranche\n");
|
||||
fprintf(stderr, L_LINE L_LAST " end of tranche\n");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1173,6 +1246,8 @@ pick_initial_format_from_renderer_tranche(struct window *window,
|
||||
window->format.format = fmt->format;
|
||||
wl_array_copy(&window->format.modifiers, &fmt->modifiers);
|
||||
|
||||
window->bo_flags = GBM_BO_USE_RENDERING;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1203,6 +1278,8 @@ pick_format_from_scanout_tranche(struct window *window,
|
||||
window->format.format = fmt->format;
|
||||
wl_array_copy(&window->format.modifiers, &fmt->modifiers);
|
||||
|
||||
window->bo_flags = GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1216,7 +1293,7 @@ dmabuf_feedback_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *dmabuf_fee
|
||||
bool got_scanout_tranche = false;
|
||||
unsigned int i;
|
||||
|
||||
fprintf(stderr, "└end of dma-buf feedback\n\n");
|
||||
fprintf(stderr, L_LAST " end of dma-buf feedback\n\n");
|
||||
|
||||
/* The first time that we receive dma-buf feedback for a surface it
|
||||
* contains only the renderer tranches. We pick the INITIAL_BUFFER_FORMAT
|
||||
@@ -1344,6 +1421,10 @@ registry_handle_global(void *data, struct wl_registry *registry,
|
||||
d->dmabuf = wl_registry_bind(registry, id,
|
||||
&zwp_linux_dmabuf_v1_interface,
|
||||
MIN(version, 4));
|
||||
} else if (strcmp(interface, "wp_presentation") == 0) {
|
||||
d->presentation = wl_registry_bind(registry, id,
|
||||
&wp_presentation_interface,
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1368,6 +1449,9 @@ destroy_display(struct display *display)
|
||||
if (display->egl.display != EGL_NO_DISPLAY)
|
||||
eglTerminate(display->egl.display);
|
||||
|
||||
if (display->presentation)
|
||||
wp_presentation_destroy(display->presentation);
|
||||
|
||||
zwp_linux_dmabuf_v1_destroy(display->dmabuf);
|
||||
xdg_wm_base_destroy(display->wm_base);
|
||||
|
||||
|
||||
+366
-60
@@ -46,9 +46,11 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libweston/matrix.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/platform.h"
|
||||
#include "shared/weston-egl-ext.h"
|
||||
#include "shared/xalloc.h"
|
||||
|
||||
struct window;
|
||||
struct seat;
|
||||
@@ -73,6 +75,8 @@ struct display {
|
||||
} egl;
|
||||
struct window *window;
|
||||
|
||||
struct wl_list output_list; /* struct output::link */
|
||||
|
||||
PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
|
||||
};
|
||||
|
||||
@@ -82,22 +86,45 @@ struct geometry {
|
||||
|
||||
struct window {
|
||||
struct display *display;
|
||||
struct geometry geometry, window_size;
|
||||
struct geometry window_size;
|
||||
struct geometry logical_size;
|
||||
struct geometry buffer_size;
|
||||
int32_t buffer_scale;
|
||||
enum wl_output_transform buffer_transform;
|
||||
bool needs_buffer_geometry_update;
|
||||
|
||||
struct {
|
||||
GLuint rotation_uniform;
|
||||
GLuint pos;
|
||||
GLuint col;
|
||||
} gl;
|
||||
|
||||
uint32_t benchmark_time, frames;
|
||||
uint32_t frames;
|
||||
uint32_t initial_frame_time;
|
||||
uint32_t benchmark_time;
|
||||
struct wl_egl_window *native;
|
||||
struct wl_surface *surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
EGLSurface egl_surface;
|
||||
struct wl_callback *callback;
|
||||
int fullscreen, maximized, opaque, buffer_size, frame_sync, delay;
|
||||
int fullscreen, maximized, opaque, buffer_bpp, frame_sync, delay;
|
||||
bool wait_for_configure;
|
||||
|
||||
struct wl_list window_output_list; /* struct window_output::link */
|
||||
};
|
||||
|
||||
struct output {
|
||||
struct display *display;
|
||||
struct wl_output *wl_output;
|
||||
uint32_t name;
|
||||
struct wl_list link; /* struct display::output_list */
|
||||
enum wl_output_transform transform;
|
||||
int32_t scale;
|
||||
};
|
||||
|
||||
struct window_output {
|
||||
struct output *output;
|
||||
struct wl_list link; /* struct window::window_output_list */
|
||||
};
|
||||
|
||||
static const char *vert_shader_text =
|
||||
@@ -155,7 +182,7 @@ init_egl(struct display *display, struct window *window)
|
||||
EGLConfig *configs;
|
||||
EGLBoolean ret;
|
||||
|
||||
if (window->opaque || window->buffer_size == 16)
|
||||
if (window->opaque || window->buffer_bpp == 16)
|
||||
config_attribs[9] = 0;
|
||||
|
||||
display->egl.dpy =
|
||||
@@ -179,13 +206,13 @@ init_egl(struct display *display, struct window *window)
|
||||
assert(ret && n >= 1);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
EGLint buffer_size, red_size;
|
||||
EGLint buffer_bpp, red_size;
|
||||
eglGetConfigAttrib(display->egl.dpy,
|
||||
configs[i], EGL_BUFFER_SIZE, &buffer_size);
|
||||
configs[i], EGL_BUFFER_SIZE, &buffer_bpp);
|
||||
eglGetConfigAttrib(display->egl.dpy,
|
||||
configs[i], EGL_RED_SIZE, &red_size);
|
||||
if ((window->buffer_size == 0 ||
|
||||
window->buffer_size == buffer_size) && red_size < 10) {
|
||||
if ((window->buffer_bpp == 0 ||
|
||||
window->buffer_bpp == buffer_bpp) && red_size < 10) {
|
||||
display->egl.conf = configs[i];
|
||||
break;
|
||||
}
|
||||
@@ -193,7 +220,7 @@ init_egl(struct display *display, struct window *window)
|
||||
free(configs);
|
||||
if (display->egl.conf == NULL) {
|
||||
fprintf(stderr, "did not find config with buffer size %d\n",
|
||||
window->buffer_size);
|
||||
window->buffer_bpp);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@@ -256,6 +283,92 @@ create_shader(struct window *window, const char *source, GLenum shader_type)
|
||||
return shader;
|
||||
}
|
||||
|
||||
static int32_t
|
||||
compute_buffer_scale(struct window *window)
|
||||
{
|
||||
struct window_output *window_output;
|
||||
int32_t scale = 1;
|
||||
|
||||
wl_list_for_each(window_output, &window->window_output_list, link) {
|
||||
if (window_output->output->scale > scale)
|
||||
scale = window_output->output->scale;
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
static enum wl_output_transform
|
||||
compute_buffer_transform(struct window *window)
|
||||
{
|
||||
struct window_output *window_output;
|
||||
enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
|
||||
wl_list_for_each(window_output, &window->window_output_list, link) {
|
||||
/* If the surface spans over multiple outputs the optimal
|
||||
* transform value can be ambiguous. Thus just return the value
|
||||
* from the oldest entered output.
|
||||
*/
|
||||
transform = window_output->output->transform;
|
||||
break;
|
||||
}
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
static void
|
||||
update_buffer_geometry(struct window *window)
|
||||
{
|
||||
enum wl_output_transform new_buffer_transform;
|
||||
int32_t new_buffer_scale;
|
||||
struct geometry new_buffer_size;
|
||||
|
||||
new_buffer_transform = compute_buffer_transform(window);
|
||||
if (window->buffer_transform != new_buffer_transform) {
|
||||
window->buffer_transform = new_buffer_transform;
|
||||
wl_surface_set_buffer_transform(window->surface,
|
||||
window->buffer_transform);
|
||||
}
|
||||
|
||||
new_buffer_scale = compute_buffer_scale(window);
|
||||
if (window->buffer_scale != new_buffer_scale) {
|
||||
window->buffer_scale = new_buffer_scale;
|
||||
wl_surface_set_buffer_scale(window->surface,
|
||||
window->buffer_scale);
|
||||
}
|
||||
|
||||
switch (window->buffer_transform) {
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
case WL_OUTPUT_TRANSFORM_180:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
new_buffer_size.width = window->logical_size.width;
|
||||
new_buffer_size.height = window->logical_size.height;
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
new_buffer_size.width = window->logical_size.height;
|
||||
new_buffer_size.height = window->logical_size.width;
|
||||
break;
|
||||
}
|
||||
|
||||
new_buffer_size.width *= window->buffer_scale;
|
||||
new_buffer_size.height *= window->buffer_scale;
|
||||
|
||||
if (window->buffer_size.width != new_buffer_size.width ||
|
||||
window->buffer_size.height != new_buffer_size.height) {
|
||||
window->buffer_size = new_buffer_size;
|
||||
if (window->native)
|
||||
wl_egl_window_resize(window->native,
|
||||
window->buffer_size.width,
|
||||
window->buffer_size.height, 0, 0);
|
||||
}
|
||||
|
||||
window->needs_buffer_geometry_update = false;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
init_gl(struct window *window)
|
||||
{
|
||||
@@ -264,9 +377,12 @@ init_gl(struct window *window)
|
||||
GLint status;
|
||||
EGLBoolean ret;
|
||||
|
||||
if (window->needs_buffer_geometry_update)
|
||||
update_buffer_geometry(window);
|
||||
|
||||
window->native = wl_egl_window_create(window->surface,
|
||||
window->geometry.width,
|
||||
window->geometry.height);
|
||||
window->buffer_size.width,
|
||||
window->buffer_size.height);
|
||||
window->egl_surface =
|
||||
weston_platform_create_egl_surface(window->display->egl.dpy,
|
||||
window->display->egl.conf,
|
||||
@@ -276,6 +392,9 @@ init_gl(struct window *window)
|
||||
window->egl_surface, window->display->egl.ctx);
|
||||
assert(ret == EGL_TRUE);
|
||||
|
||||
if (!window->frame_sync)
|
||||
eglSwapInterval(window->display->egl.dpy, 0);
|
||||
|
||||
frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
|
||||
vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
|
||||
|
||||
@@ -348,16 +467,13 @@ handle_toplevel_configure(void *data, struct xdg_toplevel *toplevel,
|
||||
window->window_size.width = width;
|
||||
window->window_size.height = height;
|
||||
}
|
||||
window->geometry.width = width;
|
||||
window->geometry.height = height;
|
||||
window->logical_size.width = width;
|
||||
window->logical_size.height = height;
|
||||
} else if (!window->fullscreen && !window->maximized) {
|
||||
window->geometry = window->window_size;
|
||||
window->logical_size = window->window_size;
|
||||
}
|
||||
|
||||
if (window->native)
|
||||
wl_egl_window_resize(window->native,
|
||||
window->geometry.width,
|
||||
window->geometry.height, 0, 0);
|
||||
window->needs_buffer_geometry_update = true;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -371,12 +487,80 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
handle_toplevel_close,
|
||||
};
|
||||
|
||||
static void
|
||||
add_window_output(struct window *window, struct wl_output *wl_output)
|
||||
{
|
||||
struct output *output;
|
||||
struct output *output_found = NULL;
|
||||
struct window_output *window_output;
|
||||
|
||||
wl_list_for_each(output, &window->display->output_list, link) {
|
||||
if (output->wl_output == wl_output) {
|
||||
output_found = output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output_found)
|
||||
return;
|
||||
|
||||
window_output = xmalloc(sizeof *window_output);
|
||||
window_output->output = output_found;
|
||||
|
||||
wl_list_insert(window->window_output_list.prev, &window_output->link);
|
||||
window->needs_buffer_geometry_update = true;
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_window_output(struct window *window, struct wl_output *wl_output)
|
||||
{
|
||||
struct window_output *window_output;
|
||||
struct window_output *window_output_found = NULL;
|
||||
|
||||
wl_list_for_each(window_output, &window->window_output_list, link) {
|
||||
if (window_output->output->wl_output == wl_output) {
|
||||
window_output_found = window_output;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window_output_found) {
|
||||
wl_list_remove(&window_output_found->link);
|
||||
free(window_output_found);
|
||||
window->needs_buffer_geometry_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
surface_enter(void *data,
|
||||
struct wl_surface *wl_surface, struct wl_output *wl_output)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
add_window_output(window, wl_output);
|
||||
}
|
||||
|
||||
static void
|
||||
surface_leave(void *data,
|
||||
struct wl_surface *wl_surface, struct wl_output *wl_output)
|
||||
{
|
||||
struct window *window = data;
|
||||
|
||||
destroy_window_output(window, wl_output);
|
||||
}
|
||||
|
||||
static const struct wl_surface_listener surface_listener = {
|
||||
surface_enter,
|
||||
surface_leave
|
||||
};
|
||||
|
||||
static void
|
||||
create_surface(struct window *window)
|
||||
{
|
||||
struct display *display = window->display;
|
||||
|
||||
window->surface = wl_compositor_create_surface(display->compositor);
|
||||
wl_surface_add_listener(window->surface, &surface_listener, window);
|
||||
|
||||
window->xdg_surface = xdg_wm_base_get_xdg_surface(display->wm_base,
|
||||
window->surface);
|
||||
@@ -399,9 +583,6 @@ create_surface(struct window *window)
|
||||
|
||||
window->wait_for_configure = true;
|
||||
wl_surface_commit(window->surface);
|
||||
|
||||
if (!window->frame_sync)
|
||||
eglSwapInterval(display->egl.dpy, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -421,15 +602,12 @@ destroy_surface(struct window *window)
|
||||
if (window->xdg_surface)
|
||||
xdg_surface_destroy(window->xdg_surface);
|
||||
wl_surface_destroy(window->surface);
|
||||
|
||||
if (window->callback)
|
||||
wl_callback_destroy(window->callback);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
redraw(struct window *window)
|
||||
{
|
||||
struct window *window = data;
|
||||
struct display *display = window->display;
|
||||
static const GLfloat verts[3][2] = {
|
||||
{ -0.5, -0.5 },
|
||||
@@ -442,28 +620,22 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
{ 0, 0, 1 }
|
||||
};
|
||||
GLfloat angle;
|
||||
GLfloat rotation[4][4] = {
|
||||
{ 1, 0, 0, 0 },
|
||||
{ 0, 1, 0, 0 },
|
||||
{ 0, 0, 1, 0 },
|
||||
{ 0, 0, 0, 1 }
|
||||
};
|
||||
struct weston_matrix rotation;
|
||||
static const uint32_t speed_div = 5, benchmark_interval = 5;
|
||||
struct wl_region *region;
|
||||
EGLint rect[4];
|
||||
EGLint buffer_age = 0;
|
||||
struct timeval tv;
|
||||
|
||||
assert(window->callback == callback);
|
||||
window->callback = NULL;
|
||||
|
||||
if (callback)
|
||||
wl_callback_destroy(callback);
|
||||
if (window->needs_buffer_geometry_update)
|
||||
update_buffer_geometry(window);
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
if (window->frames == 0)
|
||||
uint32_t time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
if (window->frames == 0) {
|
||||
window->initial_frame_time = time;
|
||||
window->benchmark_time = time;
|
||||
}
|
||||
if (time - window->benchmark_time > (benchmark_interval * 1000)) {
|
||||
printf("%d frames in %d seconds: %f fps\n",
|
||||
window->frames,
|
||||
@@ -473,20 +645,51 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
window->frames = 0;
|
||||
}
|
||||
|
||||
angle = (time / speed_div) % 360 * M_PI / 180.0;
|
||||
rotation[0][0] = cos(angle);
|
||||
rotation[0][2] = sin(angle);
|
||||
rotation[2][0] = -sin(angle);
|
||||
rotation[2][2] = cos(angle);
|
||||
weston_matrix_init(&rotation);
|
||||
angle = ((time - window->initial_frame_time) / speed_div) % 360 * M_PI / 180.0;
|
||||
rotation.d[0] = cos(angle);
|
||||
rotation.d[2] = sin(angle);
|
||||
rotation.d[8] = -sin(angle);
|
||||
rotation.d[10] = cos(angle);
|
||||
|
||||
switch (window->buffer_transform) {
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
weston_matrix_scale(&rotation, -1, 1, 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (window->buffer_transform) {
|
||||
default:
|
||||
case WL_OUTPUT_TRANSFORM_NORMAL:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED:
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_90:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
|
||||
weston_matrix_rotate_xy(&rotation, 0, 1);
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_180:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
|
||||
weston_matrix_rotate_xy(&rotation, -1, 0);
|
||||
break;
|
||||
case WL_OUTPUT_TRANSFORM_270:
|
||||
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
|
||||
weston_matrix_rotate_xy(&rotation, 0, -1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (display->swap_buffers_with_damage)
|
||||
eglQuerySurface(display->egl.dpy, window->egl_surface,
|
||||
EGL_BUFFER_AGE_EXT, &buffer_age);
|
||||
|
||||
glViewport(0, 0, window->geometry.width, window->geometry.height);
|
||||
glViewport(0, 0, window->buffer_size.width, window->buffer_size.height);
|
||||
|
||||
glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
|
||||
(GLfloat *) rotation);
|
||||
(GLfloat *) rotation.d);
|
||||
|
||||
if (window->opaque || window->fullscreen)
|
||||
glClearColor(0.0, 0.0, 0.0, 1);
|
||||
@@ -508,9 +711,7 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
|
||||
if (window->opaque || window->fullscreen) {
|
||||
region = wl_compositor_create_region(window->display->compositor);
|
||||
wl_region_add(region, 0, 0,
|
||||
window->geometry.width,
|
||||
window->geometry.height);
|
||||
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
|
||||
wl_surface_set_opaque_region(window->surface, region);
|
||||
wl_region_destroy(region);
|
||||
} else {
|
||||
@@ -518,10 +719,10 @@ redraw(void *data, struct wl_callback *callback, uint32_t time)
|
||||
}
|
||||
|
||||
if (display->swap_buffers_with_damage && buffer_age > 0) {
|
||||
rect[0] = window->geometry.width / 4 - 1;
|
||||
rect[1] = window->geometry.height / 4 - 1;
|
||||
rect[2] = window->geometry.width / 2 + 2;
|
||||
rect[3] = window->geometry.height / 2 + 2;
|
||||
rect[0] = window->buffer_size.width / 4 - 1;
|
||||
rect[1] = window->buffer_size.height / 4 - 1;
|
||||
rect[2] = window->buffer_size.width / 2 + 2;
|
||||
rect[3] = window->buffer_size.height / 2 + 2;
|
||||
display->swap_buffers_with_damage(display->egl.dpy,
|
||||
window->egl_surface,
|
||||
rect, 1);
|
||||
@@ -745,6 +946,92 @@ static const struct xdg_wm_base_listener wm_base_listener = {
|
||||
xdg_wm_base_ping,
|
||||
};
|
||||
|
||||
static void
|
||||
display_handle_geometry(void *data,
|
||||
struct wl_output *wl_output,
|
||||
int32_t x, int32_t y,
|
||||
int32_t physical_width,
|
||||
int32_t physical_height,
|
||||
int32_t subpixel,
|
||||
const char *make,
|
||||
const char *model,
|
||||
int32_t transform)
|
||||
{
|
||||
struct output *output = data;
|
||||
|
||||
output->transform = transform;
|
||||
output->display->window->needs_buffer_geometry_update = true;
|
||||
}
|
||||
|
||||
static void
|
||||
display_handle_mode(void *data,
|
||||
struct wl_output *wl_output,
|
||||
uint32_t flags,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
int32_t refresh)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
display_handle_done(void *data,
|
||||
struct wl_output *wl_output)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
display_handle_scale(void *data,
|
||||
struct wl_output *wl_output,
|
||||
int32_t scale)
|
||||
{
|
||||
struct output *output = data;
|
||||
|
||||
output->scale = scale;
|
||||
output->display->window->needs_buffer_geometry_update = true;
|
||||
}
|
||||
|
||||
static const struct wl_output_listener output_listener = {
|
||||
display_handle_geometry,
|
||||
display_handle_mode,
|
||||
display_handle_done,
|
||||
display_handle_scale
|
||||
};
|
||||
|
||||
static void
|
||||
display_add_output(struct display *d, uint32_t name)
|
||||
{
|
||||
struct output *output;
|
||||
|
||||
output = xzalloc(sizeof *output);
|
||||
output->display = d;
|
||||
output->scale = 1;
|
||||
output->wl_output =
|
||||
wl_registry_bind(d->registry, name, &wl_output_interface, 2);
|
||||
output->name = name;
|
||||
wl_list_insert(d->output_list.prev, &output->link);
|
||||
|
||||
wl_output_add_listener(output->wl_output, &output_listener, output);
|
||||
}
|
||||
|
||||
static void
|
||||
display_destroy_output(struct display *d, struct output *output)
|
||||
{
|
||||
destroy_window_output(d->window, output->wl_output);
|
||||
wl_output_destroy(output->wl_output);
|
||||
wl_list_remove(&output->link);
|
||||
free(output);
|
||||
}
|
||||
|
||||
static void
|
||||
display_destroy_outputs(struct display *d)
|
||||
{
|
||||
struct output *tmp;
|
||||
struct output *output;
|
||||
|
||||
wl_list_for_each_safe(output, tmp, &d->output_list, link)
|
||||
display_destroy_output(d, output);
|
||||
}
|
||||
|
||||
static void
|
||||
registry_handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version)
|
||||
@@ -778,6 +1065,8 @@ registry_handle_global(void *data, struct wl_registry *registry,
|
||||
fprintf(stderr, "unable to load default left pointer\n");
|
||||
// TODO: abort ?
|
||||
}
|
||||
} else if (strcmp(interface, "wl_output") == 0 && version >= 2) {
|
||||
display_add_output(d, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,6 +1074,15 @@ static void
|
||||
registry_handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name)
|
||||
{
|
||||
struct display *d = data;
|
||||
struct output *output;
|
||||
|
||||
wl_list_for_each(output, &d->output_list, link) {
|
||||
if (output->name == name) {
|
||||
display_destroy_output(d, output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
@@ -823,13 +1121,19 @@ main(int argc, char **argv)
|
||||
|
||||
window.display = &display;
|
||||
display.window = &window;
|
||||
window.geometry.width = 250;
|
||||
window.geometry.height = 250;
|
||||
window.window_size = window.geometry;
|
||||
window.buffer_size = 0;
|
||||
window.buffer_size.width = 250;
|
||||
window.buffer_size.height = 250;
|
||||
window.window_size = window.buffer_size;
|
||||
window.buffer_scale = 1;
|
||||
window.buffer_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
window.needs_buffer_geometry_update = false;
|
||||
window.buffer_bpp = 0;
|
||||
window.frame_sync = 1;
|
||||
window.delay = 0;
|
||||
|
||||
wl_list_init(&display.output_list);
|
||||
wl_list_init(&window.window_output_list);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp("-d", argv[i]) == 0 && i+1 < argc)
|
||||
window.delay = atoi(argv[++i]);
|
||||
@@ -840,7 +1144,7 @@ main(int argc, char **argv)
|
||||
else if (strcmp("-o", argv[i]) == 0)
|
||||
window.opaque = 1;
|
||||
else if (strcmp("-s", argv[i]) == 0)
|
||||
window.buffer_size = 16;
|
||||
window.buffer_bpp = 16;
|
||||
else if (strcmp("-b", argv[i]) == 0)
|
||||
window.frame_sync = 0;
|
||||
else if (strcmp("-h", argv[i]) == 0)
|
||||
@@ -887,7 +1191,7 @@ main(int argc, char **argv)
|
||||
|
||||
while (running && ret != -1) {
|
||||
ret = wl_display_dispatch_pending(display.display);
|
||||
redraw(&window, NULL, 0);
|
||||
redraw(&window);
|
||||
}
|
||||
|
||||
fprintf(stderr, "simple-egl exiting\n");
|
||||
@@ -897,6 +1201,8 @@ main(int argc, char **argv)
|
||||
|
||||
wl_surface_destroy(display.cursor_surface);
|
||||
out_no_xdg_shell:
|
||||
display_destroy_outputs(&display);
|
||||
|
||||
if (display.cursor_theme)
|
||||
wl_cursor_theme_destroy(display.cursor_theme);
|
||||
|
||||
|
||||
@@ -75,11 +75,9 @@ create_shm_buffer(struct touch *touch)
|
||||
struct wl_shm_pool *pool;
|
||||
int fd, size, stride;
|
||||
void *data;
|
||||
struct buffer *buffer = NULL;
|
||||
struct buffer *buffer;
|
||||
|
||||
buffer = zalloc(sizeof(*buffer));
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
buffer = xzalloc(sizeof(*buffer));
|
||||
|
||||
stride = touch->width * 4;
|
||||
size = stride * touch->height;
|
||||
|
||||
+19
-4
@@ -3023,13 +3023,22 @@ static void
|
||||
terminal_destroy(struct terminal *terminal)
|
||||
{
|
||||
display_unwatch_fd(terminal->display, terminal->master);
|
||||
window_destroy(terminal->window);
|
||||
close(terminal->master);
|
||||
|
||||
cairo_scaled_font_destroy(terminal->font_bold);
|
||||
cairo_scaled_font_destroy(terminal->font_normal);
|
||||
|
||||
widget_destroy(terminal->widget);
|
||||
window_destroy(terminal->window);
|
||||
|
||||
wl_list_remove(&terminal->link);
|
||||
|
||||
if (wl_list_empty(&terminal_list))
|
||||
display_exit(terminal->display);
|
||||
|
||||
free(terminal->data);
|
||||
free(terminal->data_attr);
|
||||
free(terminal->tab_ruler);
|
||||
free(terminal->title);
|
||||
free(terminal);
|
||||
}
|
||||
@@ -3048,9 +3057,11 @@ io_handler(struct task *task, uint32_t events)
|
||||
}
|
||||
|
||||
len = read(terminal->master, buffer, sizeof buffer);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
terminal_destroy(terminal);
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
terminal_data(terminal, buffer, len);
|
||||
}
|
||||
|
||||
@@ -3128,7 +3139,7 @@ static const struct weston_option terminal_options[] = {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct display *d;
|
||||
struct terminal *terminal;
|
||||
struct terminal *terminal, *tmp;
|
||||
const char *config_file;
|
||||
struct sigaction sigpipe;
|
||||
struct weston_config *config;
|
||||
@@ -3183,5 +3194,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
display_run(d);
|
||||
|
||||
wl_list_for_each_safe(terminal, tmp, &terminal_list, link)
|
||||
terminal_destroy(terminal);
|
||||
display_destroy(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+38
-371
@@ -42,25 +42,6 @@
|
||||
#include <sys/timerfd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef HAVE_CAIRO_EGL
|
||||
#include <wayland-egl.h>
|
||||
|
||||
#ifdef USE_CAIRO_GLESV2
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <cairo-gl.h>
|
||||
#elif !defined(ENABLE_EGL) /* platform.h defines these if EGL is enabled */
|
||||
typedef void *EGLDisplay;
|
||||
typedef void *EGLConfig;
|
||||
typedef void *EGLContext;
|
||||
#define EGL_NO_DISPLAY ((EGLDisplay)0)
|
||||
#endif /* no HAVE_CAIRO_EGL */
|
||||
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#ifdef HAVE_XKBCOMMON_COMPOSE
|
||||
@@ -109,10 +90,6 @@ struct display {
|
||||
struct xdg_wm_base *xdg_shell;
|
||||
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
|
||||
struct zwp_pointer_constraints_v1 *pointer_constraints;
|
||||
EGLDisplay dpy;
|
||||
EGLConfig argb_config;
|
||||
EGLContext argb_ctx;
|
||||
cairo_device_t *argb_device;
|
||||
uint32_t serial;
|
||||
|
||||
int display_fd;
|
||||
@@ -178,18 +155,6 @@ struct toysurface {
|
||||
enum wl_output_transform buffer_transform, int32_t buffer_scale,
|
||||
struct rectangle *server_allocation);
|
||||
|
||||
/*
|
||||
* Make the toysurface current with the given EGL context.
|
||||
* Returns 0 on success, and negative on failure.
|
||||
*/
|
||||
int (*acquire)(struct toysurface *base, EGLContext ctx);
|
||||
|
||||
/*
|
||||
* Release the toysurface from the EGL context, returning control
|
||||
* to Cairo.
|
||||
*/
|
||||
void (*release)(struct toysurface *base);
|
||||
|
||||
/*
|
||||
* Destroy the toysurface, including the Cairo surface, any
|
||||
* backing storage, and the Wayland protocol objects.
|
||||
@@ -240,6 +205,7 @@ struct window {
|
||||
int redraw_needed;
|
||||
int redraw_task_scheduled;
|
||||
struct task redraw_task;
|
||||
struct task close_task;
|
||||
int resize_needed;
|
||||
int custom;
|
||||
int focused;
|
||||
@@ -545,167 +511,6 @@ buffer_to_surface_size (enum wl_output_transform buffer_transform, int32_t buffe
|
||||
*height /= buffer_scale;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CAIRO_EGL
|
||||
|
||||
struct egl_window_surface {
|
||||
struct toysurface base;
|
||||
cairo_surface_t *cairo_surface;
|
||||
struct display *display;
|
||||
struct wl_surface *surface;
|
||||
struct wl_egl_window *egl_window;
|
||||
EGLSurface egl_surface;
|
||||
};
|
||||
|
||||
static struct egl_window_surface *
|
||||
to_egl_window_surface(struct toysurface *base)
|
||||
{
|
||||
return container_of(base, struct egl_window_surface, base);
|
||||
}
|
||||
|
||||
static cairo_surface_t *
|
||||
egl_window_surface_prepare(struct toysurface *base, int dx, int dy,
|
||||
int32_t width, int32_t height, uint32_t flags,
|
||||
enum wl_output_transform buffer_transform, int32_t buffer_scale)
|
||||
{
|
||||
struct egl_window_surface *surface = to_egl_window_surface(base);
|
||||
|
||||
surface_to_buffer_size (buffer_transform, buffer_scale, &width, &height);
|
||||
|
||||
wl_egl_window_resize(surface->egl_window, width, height, dx, dy);
|
||||
cairo_gl_surface_set_size(surface->cairo_surface, width, height);
|
||||
|
||||
return cairo_surface_reference(surface->cairo_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
egl_window_surface_swap(struct toysurface *base,
|
||||
enum wl_output_transform buffer_transform, int32_t buffer_scale,
|
||||
struct rectangle *server_allocation)
|
||||
{
|
||||
struct egl_window_surface *surface = to_egl_window_surface(base);
|
||||
|
||||
cairo_gl_surface_swapbuffers(surface->cairo_surface);
|
||||
wl_egl_window_get_attached_size(surface->egl_window,
|
||||
&server_allocation->width,
|
||||
&server_allocation->height);
|
||||
|
||||
buffer_to_surface_size (buffer_transform, buffer_scale,
|
||||
&server_allocation->width,
|
||||
&server_allocation->height);
|
||||
}
|
||||
|
||||
static int
|
||||
egl_window_surface_acquire(struct toysurface *base, EGLContext ctx)
|
||||
{
|
||||
struct egl_window_surface *surface = to_egl_window_surface(base);
|
||||
cairo_device_t *device;
|
||||
|
||||
device = cairo_surface_get_device(surface->cairo_surface);
|
||||
if (!device)
|
||||
return -1;
|
||||
|
||||
if (!ctx) {
|
||||
if (device == surface->display->argb_device)
|
||||
ctx = surface->display->argb_ctx;
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
|
||||
cairo_device_flush(device);
|
||||
cairo_device_acquire(device);
|
||||
if (!eglMakeCurrent(surface->display->dpy, surface->egl_surface,
|
||||
surface->egl_surface, ctx))
|
||||
fprintf(stderr, "failed to make surface current\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
egl_window_surface_release(struct toysurface *base)
|
||||
{
|
||||
struct egl_window_surface *surface = to_egl_window_surface(base);
|
||||
cairo_device_t *device;
|
||||
|
||||
device = cairo_surface_get_device(surface->cairo_surface);
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
if (!eglMakeCurrent(surface->display->dpy,
|
||||
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT))
|
||||
fprintf(stderr, "failed to make context current\n");
|
||||
|
||||
cairo_device_release(device);
|
||||
}
|
||||
|
||||
static void
|
||||
egl_window_surface_destroy(struct toysurface *base)
|
||||
{
|
||||
struct egl_window_surface *surface = to_egl_window_surface(base);
|
||||
struct display *d = surface->display;
|
||||
|
||||
cairo_surface_destroy(surface->cairo_surface);
|
||||
weston_platform_destroy_egl_surface(d->dpy, surface->egl_surface);
|
||||
wl_egl_window_destroy(surface->egl_window);
|
||||
surface->surface = NULL;
|
||||
|
||||
free(surface);
|
||||
}
|
||||
|
||||
static struct toysurface *
|
||||
egl_window_surface_create(struct display *display,
|
||||
struct wl_surface *wl_surface,
|
||||
uint32_t flags,
|
||||
struct rectangle *rectangle)
|
||||
{
|
||||
struct egl_window_surface *surface;
|
||||
|
||||
if (display->dpy == EGL_NO_DISPLAY)
|
||||
return NULL;
|
||||
|
||||
surface = zalloc(sizeof *surface);
|
||||
if (!surface)
|
||||
return NULL;
|
||||
|
||||
surface->base.prepare = egl_window_surface_prepare;
|
||||
surface->base.swap = egl_window_surface_swap;
|
||||
surface->base.acquire = egl_window_surface_acquire;
|
||||
surface->base.release = egl_window_surface_release;
|
||||
surface->base.destroy = egl_window_surface_destroy;
|
||||
|
||||
surface->display = display;
|
||||
surface->surface = wl_surface;
|
||||
|
||||
surface->egl_window = wl_egl_window_create(surface->surface,
|
||||
rectangle->width,
|
||||
rectangle->height);
|
||||
|
||||
surface->egl_surface =
|
||||
weston_platform_create_egl_surface(display->dpy,
|
||||
display->argb_config,
|
||||
surface->egl_window, NULL);
|
||||
|
||||
surface->cairo_surface =
|
||||
cairo_gl_surface_create_for_egl(display->argb_device,
|
||||
surface->egl_surface,
|
||||
rectangle->width,
|
||||
rectangle->height);
|
||||
|
||||
return &surface->base;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct toysurface *
|
||||
egl_window_surface_create(struct display *display,
|
||||
struct wl_surface *wl_surface,
|
||||
uint32_t flags,
|
||||
struct rectangle *rectangle)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct shm_surface_data {
|
||||
struct wl_buffer *buffer;
|
||||
struct shm_pool *pool;
|
||||
@@ -1159,17 +964,6 @@ shm_surface_swap(struct toysurface *base,
|
||||
surface->current = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
shm_surface_acquire(struct toysurface *base, EGLContext ctx)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
shm_surface_release(struct toysurface *base)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
shm_surface_destroy(struct toysurface *base)
|
||||
{
|
||||
@@ -1192,8 +986,6 @@ shm_surface_create(struct display *display, struct wl_surface *wl_surface,
|
||||
surface = xzalloc(sizeof *surface);
|
||||
surface->base.prepare = shm_surface_prepare;
|
||||
surface->base.swap = shm_surface_swap;
|
||||
surface->base.acquire = shm_surface_acquire;
|
||||
surface->base.release = shm_surface_release;
|
||||
surface->base.destroy = shm_surface_destroy;
|
||||
|
||||
surface->display = display;
|
||||
@@ -1432,13 +1224,23 @@ window_has_focus(struct window *window)
|
||||
return window->focused;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
close_task_run(struct task *task, uint32_t events)
|
||||
{
|
||||
struct window *window = container_of(task, struct window, close_task);
|
||||
window->close_handler(window->user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
window_close(struct window *window)
|
||||
{
|
||||
if (window->close_handler)
|
||||
window->close_handler(window->user_data);
|
||||
else
|
||||
if (window->close_handler && !window->close_task.run) {
|
||||
window->close_task.run = close_task_run;
|
||||
display_defer(window->display, &window->close_task);
|
||||
} else {
|
||||
display_exit(window->display);
|
||||
}
|
||||
}
|
||||
|
||||
struct display *
|
||||
@@ -1453,15 +1255,6 @@ surface_create_surface(struct surface *surface, uint32_t flags)
|
||||
struct display *display = surface->window->display;
|
||||
struct rectangle allocation = surface->allocation;
|
||||
|
||||
if (!surface->toysurface && display->dpy &&
|
||||
surface->buffer_type == WINDOW_BUFFER_TYPE_EGL_WINDOW) {
|
||||
surface->toysurface =
|
||||
egl_window_surface_create(display,
|
||||
surface->surface,
|
||||
flags,
|
||||
&allocation);
|
||||
}
|
||||
|
||||
if (!surface->toysurface)
|
||||
surface->toysurface = shm_surface_create(display,
|
||||
surface->surface,
|
||||
@@ -4439,9 +4232,24 @@ xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_surface)
|
||||
window_close(window);
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel,
|
||||
struct wl_array *caps)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
xdg_toplevel_handle_configure,
|
||||
xdg_toplevel_handle_close,
|
||||
xdg_toplevel_handle_configure_bounds,
|
||||
xdg_toplevel_handle_wm_capabilities,
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -5272,11 +5080,6 @@ surface_create(struct window *window)
|
||||
static enum window_buffer_type
|
||||
get_preferred_buffer_type(struct display *display)
|
||||
{
|
||||
#ifdef HAVE_CAIRO_EGL
|
||||
if (display->argb_device && !getenv("TOYTOOLKIT_NO_EGL"))
|
||||
return WINDOW_BUFFER_TYPE_EGL_WINDOW;
|
||||
#endif
|
||||
|
||||
return WINDOW_BUFFER_TYPE_SHM;
|
||||
}
|
||||
|
||||
@@ -5319,14 +5122,14 @@ window_create(struct display *display)
|
||||
window->xdg_surface =
|
||||
xdg_wm_base_get_xdg_surface(window->display->xdg_shell,
|
||||
window->main_surface->surface);
|
||||
fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__);
|
||||
abort_oom_if_null(window->xdg_surface);
|
||||
|
||||
xdg_surface_add_listener(window->xdg_surface,
|
||||
&xdg_surface_listener, window);
|
||||
|
||||
window->xdg_toplevel =
|
||||
xdg_surface_get_toplevel(window->xdg_surface);
|
||||
fail_on_null(window->xdg_toplevel, 0, __FILE__, __LINE__);
|
||||
abort_oom_if_null(window->xdg_toplevel);
|
||||
|
||||
xdg_toplevel_add_listener(window->xdg_toplevel,
|
||||
&xdg_toplevel_listener, window);
|
||||
@@ -5531,7 +5334,7 @@ create_menu(struct display *display,
|
||||
menu->widget = window_add_widget(menu->window, menu);
|
||||
menu->frame = frame_create(window->display->theme, 0, 0,
|
||||
FRAME_BUTTON_NONE, NULL, NULL);
|
||||
fail_on_null(menu->frame, 0, __FILE__, __LINE__);
|
||||
abort_oom_if_null(menu->frame);
|
||||
menu->entries = entries;
|
||||
menu->count = count;
|
||||
menu->release_count = 0;
|
||||
@@ -5565,7 +5368,7 @@ create_simple_positioner(struct display *display,
|
||||
struct xdg_positioner *positioner;
|
||||
|
||||
positioner = xdg_wm_base_create_positioner(display->xdg_shell);
|
||||
fail_on_null(positioner, 0, __FILE__, __LINE__);
|
||||
abort_oom_if_null(positioner);
|
||||
xdg_positioner_set_anchor_rect(positioner, x, y, 1, 1);
|
||||
xdg_positioner_set_size(positioner, w, h);
|
||||
xdg_positioner_set_anchor(positioner,
|
||||
@@ -5610,7 +5413,7 @@ window_show_menu(struct display *display,
|
||||
window->xdg_surface =
|
||||
xdg_wm_base_get_xdg_surface(display->xdg_shell,
|
||||
window->main_surface->surface);
|
||||
fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__);
|
||||
abort_oom_if_null(window->xdg_surface);
|
||||
|
||||
xdg_surface_add_listener(window->xdg_surface,
|
||||
&xdg_surface_listener, window);
|
||||
@@ -5623,7 +5426,7 @@ window_show_menu(struct display *display,
|
||||
window->xdg_popup = xdg_surface_get_popup(window->xdg_surface,
|
||||
parent->xdg_surface,
|
||||
positioner);
|
||||
fail_on_null(window->xdg_popup, 0, __FILE__, __LINE__);
|
||||
abort_oom_if_null(window->xdg_popup);
|
||||
xdg_positioner_destroy(positioner);
|
||||
xdg_popup_grab(window->xdg_popup, input->seat,
|
||||
display_get_serial(window->display));
|
||||
@@ -6089,7 +5892,8 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
|
||||
display_add_data_device(d, id, version);
|
||||
} else if (strcmp(interface, "xdg_wm_base") == 0) {
|
||||
d->xdg_shell = wl_registry_bind(registry, id,
|
||||
&xdg_wm_base_interface, 1);
|
||||
&xdg_wm_base_interface,
|
||||
MIN(version, 5));
|
||||
xdg_wm_base_add_listener(d->xdg_shell, &wm_base_listener, d);
|
||||
} else if (strcmp(interface, "text_cursor_position") == 0) {
|
||||
d->text_cursor_position =
|
||||
@@ -6140,90 +5944,6 @@ static const struct wl_registry_listener registry_listener = {
|
||||
registry_handle_global_remove
|
||||
};
|
||||
|
||||
#ifdef HAVE_CAIRO_EGL
|
||||
static int
|
||||
init_egl(struct display *d)
|
||||
{
|
||||
EGLint major, minor;
|
||||
EGLint n;
|
||||
|
||||
#ifdef USE_CAIRO_GLESV2
|
||||
# define GL_BIT EGL_OPENGL_ES2_BIT
|
||||
#else
|
||||
# define GL_BIT EGL_OPENGL_BIT
|
||||
#endif
|
||||
|
||||
static const EGLint argb_cfg_attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, 1,
|
||||
EGL_DEPTH_SIZE, 1,
|
||||
EGL_RENDERABLE_TYPE, GL_BIT,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
#ifdef USE_CAIRO_GLESV2
|
||||
static const EGLint context_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
EGLint api = EGL_OPENGL_ES_API;
|
||||
#else
|
||||
EGLint *context_attribs = NULL;
|
||||
EGLint api = EGL_OPENGL_API;
|
||||
#endif
|
||||
|
||||
d->dpy =
|
||||
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
|
||||
d->display, NULL);
|
||||
|
||||
if (!eglInitialize(d->dpy, &major, &minor)) {
|
||||
fprintf(stderr, "failed to initialize EGL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!eglBindAPI(api)) {
|
||||
fprintf(stderr, "failed to bind EGL client API\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!eglChooseConfig(d->dpy, argb_cfg_attribs,
|
||||
&d->argb_config, 1, &n) || n != 1) {
|
||||
fprintf(stderr, "failed to choose argb EGL config\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
d->argb_ctx = eglCreateContext(d->dpy, d->argb_config,
|
||||
EGL_NO_CONTEXT, context_attribs);
|
||||
if (d->argb_ctx == NULL) {
|
||||
fprintf(stderr, "failed to create EGL context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
d->argb_device = cairo_egl_device_create(d->dpy, d->argb_ctx);
|
||||
if (cairo_device_status(d->argb_device) != CAIRO_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "failed to get cairo EGL argb device\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fini_egl(struct display *display)
|
||||
{
|
||||
cairo_device_destroy(display->argb_device);
|
||||
|
||||
eglMakeCurrent(display->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT);
|
||||
|
||||
eglTerminate(display->dpy);
|
||||
eglReleaseThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
init_dummy_surface(struct display *display)
|
||||
{
|
||||
@@ -6329,12 +6049,6 @@ display_create(int *argc, char *argv[])
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CAIRO_EGL
|
||||
if (init_egl(d) < 0)
|
||||
fprintf(stderr, "EGL does not seem to work, "
|
||||
"falling back to software rendering and wl_shm.\n");
|
||||
#endif
|
||||
|
||||
create_cursors(d);
|
||||
|
||||
d->theme = theme_create();
|
||||
@@ -6393,10 +6107,7 @@ display_destroy(struct display *display)
|
||||
theme_destroy(display->theme);
|
||||
destroy_cursors(display);
|
||||
|
||||
#ifdef HAVE_CAIRO_EGL
|
||||
if (display->argb_device)
|
||||
fini_egl(display);
|
||||
#endif
|
||||
cleanup_after_cairo();
|
||||
|
||||
if (display->relative_pointer_manager)
|
||||
zwp_relative_pointer_manager_v1_destroy(display->relative_pointer_manager);
|
||||
@@ -6462,12 +6173,6 @@ display_has_subcompositor(struct display *display)
|
||||
return display->subcompositor != NULL;
|
||||
}
|
||||
|
||||
cairo_device_t *
|
||||
display_get_cairo_device(struct display *display)
|
||||
{
|
||||
return display->argb_device;
|
||||
}
|
||||
|
||||
struct output *
|
||||
display_get_output(struct display *display)
|
||||
{
|
||||
@@ -6489,12 +6194,6 @@ display_get_serial(struct display *display)
|
||||
return display->serial;
|
||||
}
|
||||
|
||||
EGLDisplay
|
||||
display_get_egl_display(struct display *d)
|
||||
{
|
||||
return d->dpy;
|
||||
}
|
||||
|
||||
struct wl_data_source *
|
||||
display_create_data_source(struct display *display)
|
||||
{
|
||||
@@ -6504,38 +6203,6 @@ display_create_data_source(struct display *display)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EGLConfig
|
||||
display_get_argb_egl_config(struct display *d)
|
||||
{
|
||||
return d->argb_config;
|
||||
}
|
||||
|
||||
int
|
||||
display_acquire_window_surface(struct display *display,
|
||||
struct window *window,
|
||||
EGLContext ctx)
|
||||
{
|
||||
struct surface *surface = window->main_surface;
|
||||
|
||||
if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
|
||||
return -1;
|
||||
|
||||
widget_get_cairo_surface(window->main_surface->widget);
|
||||
return surface->toysurface->acquire(surface->toysurface, ctx);
|
||||
}
|
||||
|
||||
void
|
||||
display_release_window_surface(struct display *display,
|
||||
struct window *window)
|
||||
{
|
||||
struct surface *surface = window->main_surface;
|
||||
|
||||
if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
|
||||
return;
|
||||
|
||||
surface->toysurface->release(surface->toysurface);
|
||||
}
|
||||
|
||||
void
|
||||
display_defer(struct display *display, struct task *task)
|
||||
{
|
||||
|
||||
@@ -71,9 +71,6 @@ display_get_display(struct display *display);
|
||||
int
|
||||
display_has_subcompositor(struct display *display);
|
||||
|
||||
cairo_device_t *
|
||||
display_get_cairo_device(struct display *display);
|
||||
|
||||
struct wl_compositor *
|
||||
display_get_compositor(struct display *display);
|
||||
|
||||
@@ -114,22 +111,6 @@ display_set_output_configure_handler(struct display *display,
|
||||
struct wl_data_source *
|
||||
display_create_data_source(struct display *display);
|
||||
|
||||
#ifdef EGL_NO_DISPLAY
|
||||
EGLDisplay
|
||||
display_get_egl_display(struct display *d);
|
||||
|
||||
EGLConfig
|
||||
display_get_argb_egl_config(struct display *d);
|
||||
|
||||
int
|
||||
display_acquire_window_surface(struct display *display,
|
||||
struct window *window,
|
||||
EGLContext ctx);
|
||||
void
|
||||
display_release_window_surface(struct display *display,
|
||||
struct window *window);
|
||||
#endif
|
||||
|
||||
#define SURFACE_OPAQUE 0x01
|
||||
#define SURFACE_SHM 0x02
|
||||
|
||||
@@ -416,7 +397,6 @@ struct wl_subsurface *
|
||||
widget_get_wl_subsurface(struct widget *widget);
|
||||
|
||||
enum window_buffer_type {
|
||||
WINDOW_BUFFER_TYPE_EGL_WINDOW,
|
||||
WINDOW_BUFFER_TYPE_SHM,
|
||||
};
|
||||
|
||||
|
||||
+447
-183
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2010-2011 Intel Corporation
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
* Copyright © 2012-2018 Collabora, Ltd.
|
||||
* Copyright © 2012-2018,2022 Collabora, Ltd.
|
||||
* Copyright © 2010-2011 Benjamin Franzke
|
||||
* Copyright © 2013 Jason Ekstrand
|
||||
* Copyright © 2017, 2018 General Electric Company
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <dlfcn.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
@@ -50,15 +51,16 @@
|
||||
#include <libweston/libweston.h>
|
||||
#include "shared/os-compatibility.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/process-util.h"
|
||||
#include "shared/string-helpers.h"
|
||||
#include "git-version.h"
|
||||
#include <libweston/version.h>
|
||||
#include "weston.h"
|
||||
#include "weston-private.h"
|
||||
|
||||
#include <libweston/backend-drm.h>
|
||||
#include <libweston/backend-headless.h>
|
||||
#include <libweston/backend-rdp.h>
|
||||
#include <libweston/backend-fbdev.h>
|
||||
#include <libweston/backend-x11.h>
|
||||
#include <libweston/backend-wayland.h>
|
||||
#include <libweston/windowed-output-api.h>
|
||||
@@ -134,37 +136,6 @@ static struct weston_log_scope *log_scope;
|
||||
static struct weston_log_scope *protocol_scope;
|
||||
static int cached_tm_mday = -1;
|
||||
|
||||
static char *
|
||||
weston_log_timestamp(char *buf, size_t len)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct tm *brokendown_time;
|
||||
char datestr[128];
|
||||
char timestr[128];
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
brokendown_time = localtime(&tv.tv_sec);
|
||||
if (brokendown_time == NULL) {
|
||||
snprintf(buf, len, "%s", "[(NULL)localtime] ");
|
||||
return buf;
|
||||
}
|
||||
|
||||
memset(datestr, 0, sizeof(datestr));
|
||||
if (brokendown_time->tm_mday != cached_tm_mday) {
|
||||
strftime(datestr, sizeof(datestr), "Date: %Y-%m-%d %Z\n",
|
||||
brokendown_time);
|
||||
cached_tm_mday = brokendown_time->tm_mday;
|
||||
}
|
||||
|
||||
strftime(timestr, sizeof(timestr), "%H:%M:%S", brokendown_time);
|
||||
/* if datestr is empty it prints only timestr*/
|
||||
snprintf(buf, len, "%s[%s.%03li]", datestr,
|
||||
timestr, (tv.tv_usec / 1000));
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void
|
||||
custom_handler(const char *fmt, va_list arg)
|
||||
{
|
||||
@@ -172,7 +143,7 @@ custom_handler(const char *fmt, va_list arg)
|
||||
|
||||
weston_log_scope_printf(log_scope, "%s libwayland: ",
|
||||
weston_log_timestamp(timestr,
|
||||
sizeof(timestr)));
|
||||
sizeof(timestr), &cached_tm_mday));
|
||||
weston_log_scope_vprintf(log_scope, fmt, arg);
|
||||
}
|
||||
|
||||
@@ -218,7 +189,8 @@ vlog(const char *fmt, va_list ap)
|
||||
if (weston_log_scope_is_enabled(log_scope)) {
|
||||
int len_va;
|
||||
char *log_timestamp = weston_log_timestamp(timestr,
|
||||
sizeof(timestr));
|
||||
sizeof(timestr),
|
||||
&cached_tm_mday);
|
||||
len_va = vasprintf(&str, fmt, ap);
|
||||
if (len_va >= 0) {
|
||||
len = weston_log_scope_printf(log_scope, "%s %s",
|
||||
@@ -270,6 +242,8 @@ protocol_log_fn(void *user_data,
|
||||
size_t logsize;
|
||||
char timestr[128];
|
||||
struct wl_resource *res = message->resource;
|
||||
struct wl_client *client = wl_resource_get_client(res);
|
||||
pid_t pid = 0;
|
||||
const char *signature = message->message->signature;
|
||||
int i;
|
||||
char type;
|
||||
@@ -281,10 +255,12 @@ protocol_log_fn(void *user_data,
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||
|
||||
weston_log_scope_timestamp(protocol_scope,
|
||||
timestr, sizeof timestr);
|
||||
fprintf(fp, "%s ", timestr);
|
||||
fprintf(fp, "client %p %s ", wl_resource_get_client(res),
|
||||
fprintf(fp, "client %p (PID %d) %s ", client, pid,
|
||||
direction == WL_PROTOCOL_LOGGER_REQUEST ? "rq" : "ev");
|
||||
fprintf(fp, "%s@%u.%s(",
|
||||
wl_resource_get_class(res),
|
||||
@@ -382,6 +358,7 @@ sigchld_handler(int signal_number, void *data)
|
||||
}
|
||||
|
||||
wl_list_remove(&p->link);
|
||||
wl_list_init(&p->link);
|
||||
p->cleanup(p, status);
|
||||
}
|
||||
|
||||
@@ -391,12 +368,56 @@ sigchld_handler(int signal_number, void *data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
child_client_exec(int sockfd, const char *path)
|
||||
WL_EXPORT struct wl_client *
|
||||
weston_client_launch(struct weston_compositor *compositor,
|
||||
struct weston_process *proc,
|
||||
const char *path,
|
||||
weston_process_cleanup_func_t cleanup)
|
||||
{
|
||||
int clientfd;
|
||||
char s[32];
|
||||
struct wl_client *client = NULL;
|
||||
struct custom_env child_env;
|
||||
struct fdstr wayland_socket;
|
||||
const char *fail_cloexec = "Couldn't unset CLOEXEC on client socket";
|
||||
const char *fail_seteuid = "Couldn't call seteuid";
|
||||
char *fail_exec;
|
||||
char * const *argp;
|
||||
char * const *envp;
|
||||
sigset_t allsigs;
|
||||
pid_t pid;
|
||||
bool ret;
|
||||
size_t written __attribute__((unused));
|
||||
|
||||
weston_log("launching '%s'\n", path);
|
||||
str_printf(&fail_exec, "Error: Couldn't launch client '%s'\n", path);
|
||||
|
||||
custom_env_init_from_environ(&child_env);
|
||||
custom_env_add_arg(&child_env, path);
|
||||
|
||||
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0,
|
||||
wayland_socket.fds) < 0) {
|
||||
weston_log("weston_client_launch: "
|
||||
"socketpair failed while launching '%s': %s\n",
|
||||
path, strerror(errno));
|
||||
custom_env_fini(&child_env);
|
||||
return NULL;
|
||||
}
|
||||
fdstr_update_str1(&wayland_socket);
|
||||
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET",
|
||||
wayland_socket.str1);
|
||||
|
||||
argp = custom_env_get_argp(&child_env);
|
||||
envp = custom_env_get_envp(&child_env);
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case 0:
|
||||
/* Put the client in a new session so it won't catch signals
|
||||
* intended for the parent. Sharing a session can be
|
||||
* confusing when launching weston under gdb, as the ctrl-c
|
||||
* intended for gdb will pass to the child, and weston
|
||||
* will cleanly shut down when the child exits.
|
||||
*/
|
||||
setsid();
|
||||
|
||||
/* do not give our signal mask to the new process */
|
||||
sigfillset(&allsigs);
|
||||
@@ -404,65 +425,33 @@ child_client_exec(int sockfd, const char *path)
|
||||
|
||||
/* Launch clients as the user. Do not launch clients with wrong euid. */
|
||||
if (seteuid(getuid()) == -1) {
|
||||
weston_log("compositor: failed seteuid\n");
|
||||
return;
|
||||
written = write(STDERR_FILENO, fail_seteuid,
|
||||
strlen(fail_seteuid));
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* SOCK_CLOEXEC closes both ends, so we dup the fd to get a
|
||||
* non-CLOEXEC fd to pass through exec. */
|
||||
clientfd = dup(sockfd);
|
||||
if (clientfd == -1) {
|
||||
weston_log("compositor: dup failed: %s\n", strerror(errno));
|
||||
return;
|
||||
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
|
||||
if (!ret) {
|
||||
written = write(STDERR_FILENO, fail_cloexec,
|
||||
strlen(fail_cloexec));
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
snprintf(s, sizeof s, "%d", clientfd);
|
||||
setenv("WAYLAND_SOCKET", s, 1);
|
||||
execve(argp[0], argp, envp);
|
||||
|
||||
if (execl(path, path, NULL) < 0)
|
||||
weston_log("compositor: executing '%s' failed: %s\n",
|
||||
path, strerror(errno));
|
||||
}
|
||||
if (fail_exec)
|
||||
written = write(STDERR_FILENO, fail_exec,
|
||||
strlen(fail_exec));
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
WL_EXPORT struct wl_client *
|
||||
weston_client_launch(struct weston_compositor *compositor,
|
||||
struct weston_process *proc,
|
||||
const char *path,
|
||||
weston_process_cleanup_func_t cleanup)
|
||||
{
|
||||
int sv[2];
|
||||
pid_t pid;
|
||||
struct wl_client *client;
|
||||
|
||||
weston_log("launching '%s'\n", path);
|
||||
|
||||
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
|
||||
weston_log("weston_client_launch: "
|
||||
"socketpair failed while launching '%s': %s\n",
|
||||
path, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
close(sv[0]);
|
||||
close(sv[1]);
|
||||
weston_log("weston_client_launch: "
|
||||
"fork failed while launching '%s': %s\n", path,
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
child_client_exec(sv[1], path);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
close(sv[1]);
|
||||
|
||||
client = wl_client_create(compositor->wl_display, sv[0]);
|
||||
default:
|
||||
close(wayland_socket.fds[1]);
|
||||
client = wl_client_create(compositor->wl_display,
|
||||
wayland_socket.fds[0]);
|
||||
if (!client) {
|
||||
close(sv[0]);
|
||||
custom_env_fini(&child_env);
|
||||
close(wayland_socket.fds[0]);
|
||||
free(fail_exec);
|
||||
weston_log("weston_client_launch: "
|
||||
"wl_client_create failed while launching '%s'.\n",
|
||||
path);
|
||||
@@ -472,6 +461,18 @@ weston_client_launch(struct weston_compositor *compositor,
|
||||
proc->pid = pid;
|
||||
proc->cleanup = cleanup;
|
||||
wet_watch_process(compositor, proc);
|
||||
break;
|
||||
|
||||
case -1:
|
||||
fdstr_close_all(&wayland_socket);
|
||||
weston_log("weston_client_launch: "
|
||||
"fork failed while launching '%s': %s\n", path,
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
custom_env_fini(&child_env);
|
||||
free(fail_exec);
|
||||
|
||||
return client;
|
||||
}
|
||||
@@ -646,9 +647,6 @@ usage(int error_code)
|
||||
#if defined(BUILD_DRM_COMPOSITOR)
|
||||
"\t\t\t\tdrm-backend.so\n"
|
||||
#endif
|
||||
#if defined(BUILD_FBDEV_COMPOSITOR)
|
||||
"\t\t\t\tfbdev-backend.so\n"
|
||||
#endif
|
||||
#if defined(BUILD_HEADLESS_COMPOSITOR)
|
||||
"\t\t\t\theadless-backend.so\n"
|
||||
#endif
|
||||
@@ -685,22 +683,12 @@ usage(int error_code)
|
||||
fprintf(out,
|
||||
"Options for drm-backend.so:\n\n"
|
||||
" --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n"
|
||||
" --tty=TTY\t\tThe tty to use\n"
|
||||
" --drm-device=CARD\tThe DRM device to use, e.g. \"card0\".\n"
|
||||
" --use-pixman\t\tUse the pixman (CPU) renderer\n"
|
||||
" --current-mode\tPrefer current KMS mode over EDID preferred mode\n"
|
||||
" --continue-without-input\tAllow the compositor to start without input devices\n\n");
|
||||
#endif
|
||||
|
||||
#if defined(BUILD_FBDEV_COMPOSITOR)
|
||||
fprintf(out,
|
||||
"Options for fbdev-backend.so:\n\n"
|
||||
" --tty=TTY\t\tThe tty to use\n"
|
||||
" --device=DEVICE\tThe framebuffer device to use\n"
|
||||
" --seat=SEAT\t\tThe seat that weston should run on, instead of the seat defined in XDG_SEAT\n"
|
||||
"\n");
|
||||
#endif
|
||||
|
||||
#if defined(BUILD_HEADLESS_COMPOSITOR)
|
||||
fprintf(out,
|
||||
"Options for headless-backend.so:\n\n"
|
||||
@@ -721,6 +709,7 @@ usage(int error_code)
|
||||
" --width=WIDTH\t\tWidth of desktop\n"
|
||||
" --height=HEIGHT\tHeight of desktop\n"
|
||||
" --env-socket\t\tUse socket defined in RDP_FD env variable as peer connection\n"
|
||||
" --external-listener-fd=FD\tUse socket as listener connection\n"
|
||||
" --address=ADDR\tThe address to bind\n"
|
||||
" --port=PORT\t\tThe port to listen on\n"
|
||||
" --no-clients-resize\tThe RDP peers will be forced to the size of the desktop\n"
|
||||
@@ -869,7 +858,7 @@ handle_primary_client_destroyed(struct wl_listener *listener, void *data)
|
||||
static int
|
||||
weston_create_listening_socket(struct wl_display *display, const char *socket_name)
|
||||
{
|
||||
char name_candidate[16];
|
||||
char name_candidate[32];
|
||||
|
||||
if (socket_name) {
|
||||
if (wl_display_add_socket(display, socket_name)) {
|
||||
@@ -1002,7 +991,7 @@ wet_get_bindir_path(const char *name)
|
||||
|
||||
static int
|
||||
load_modules(struct weston_compositor *ec, const char *modules,
|
||||
int *argc, char *argv[], bool *xwayland)
|
||||
int *argc, char *argv[])
|
||||
{
|
||||
const char *p, *end;
|
||||
char buffer[256];
|
||||
@@ -1016,11 +1005,11 @@ load_modules(struct weston_compositor *ec, const char *modules,
|
||||
snprintf(buffer, sizeof buffer, "%.*s", (int) (end - p), p);
|
||||
|
||||
if (strstr(buffer, "xwayland.so")) {
|
||||
weston_log("Old Xwayland module loading detected: "
|
||||
weston_log("fatal: Old Xwayland module loading detected: "
|
||||
"Please use --xwayland command line option "
|
||||
"or set xwayland=true in the [core] section "
|
||||
"in weston.ini\n");
|
||||
*xwayland = true;
|
||||
return -1;
|
||||
} else {
|
||||
if (wet_load_module(ec, buffer, argc, argv) < 0)
|
||||
return -1;
|
||||
@@ -1348,6 +1337,223 @@ wet_output_set_color_profile(struct weston_output *output,
|
||||
return ok ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
wet_output_set_eotf_mode(struct weston_output *output,
|
||||
struct weston_config_section *section)
|
||||
{
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum weston_eotf_mode eotf_mode;
|
||||
} modes[] = {
|
||||
{ "sdr", WESTON_EOTF_MODE_SDR },
|
||||
{ "hdr-gamma", WESTON_EOTF_MODE_TRADITIONAL_HDR },
|
||||
{ "st2084", WESTON_EOTF_MODE_ST2084 },
|
||||
{ "hlg", WESTON_EOTF_MODE_HLG },
|
||||
};
|
||||
struct wet_compositor *compositor;
|
||||
enum weston_eotf_mode eotf_mode = WESTON_EOTF_MODE_SDR;
|
||||
char *str = NULL;
|
||||
unsigned i;
|
||||
|
||||
compositor = to_wet_compositor(output->compositor);
|
||||
|
||||
if (section) {
|
||||
weston_config_section_get_string(section, "eotf-mode",
|
||||
&str, NULL);
|
||||
}
|
||||
|
||||
if (!str) {
|
||||
/* The default SDR mode is always supported. */
|
||||
assert(weston_output_get_supported_eotf_modes(output) & eotf_mode);
|
||||
weston_output_set_eotf_mode(output, eotf_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LENGTH(modes); i++)
|
||||
if (strcmp(str, modes[i].name) == 0)
|
||||
break;
|
||||
|
||||
if (i == ARRAY_LENGTH(modes)) {
|
||||
weston_log("Error in config for output '%s': '%s' is not a valid EOTF mode. Try one of:",
|
||||
output->name, str);
|
||||
for (i = 0; i < ARRAY_LENGTH(modes); i++)
|
||||
weston_log_continue(" %s", modes[i].name);
|
||||
weston_log_continue("\n");
|
||||
return -1;
|
||||
}
|
||||
eotf_mode = modes[i].eotf_mode;
|
||||
|
||||
if ((weston_output_get_supported_eotf_modes(output) & eotf_mode) == 0) {
|
||||
weston_log("Error: output '%s' does not support EOTF mode %s.\n",
|
||||
output->name, str);
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (eotf_mode != WESTON_EOTF_MODE_SDR &&
|
||||
!compositor->use_color_manager) {
|
||||
weston_log("Error: EOTF mode %s on output '%s' requires color-management=true in weston.ini\n",
|
||||
str, output->name);
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
weston_output_set_eotf_mode(output, eotf_mode);
|
||||
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct wet_color_characteristics_keys {
|
||||
const char *name;
|
||||
enum weston_color_characteristics_groups group;
|
||||
float minval;
|
||||
float maxval;
|
||||
};
|
||||
|
||||
#define COLOR_CHARAC_NAME "color_characteristics"
|
||||
|
||||
static int
|
||||
parse_color_characteristics(struct weston_color_characteristics *cc_out,
|
||||
struct weston_config_section *section)
|
||||
{
|
||||
static const struct wet_color_characteristics_keys keys[] = {
|
||||
{ "red_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "red_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "green_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "green_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "blue_x", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "blue_y", WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES, 0.0f, 1.0f },
|
||||
{ "white_x", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f },
|
||||
{ "white_y", WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE, 0.0f, 1.0f },
|
||||
{ "max_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL, 0.0f, 1e5f },
|
||||
{ "min_L", WESTON_COLOR_CHARACTERISTICS_GROUP_MINL, 0.0f, 1e5f },
|
||||
{ "maxFALL", WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL, 0.0f, 1e5f },
|
||||
};
|
||||
static const char *msgpfx = "Config error in weston.ini [" COLOR_CHARAC_NAME "]";
|
||||
struct weston_color_characteristics cc = {};
|
||||
float *const keyvalp[ARRAY_LENGTH(keys)] = {
|
||||
/* These must be in the same order as keys[]. */
|
||||
&cc.primary[0].x, &cc.primary[0].y,
|
||||
&cc.primary[1].x, &cc.primary[1].y,
|
||||
&cc.primary[2].x, &cc.primary[2].y,
|
||||
&cc.white.x, &cc.white.y,
|
||||
&cc.max_luminance,
|
||||
&cc.min_luminance,
|
||||
&cc.maxFALL,
|
||||
};
|
||||
bool found[ARRAY_LENGTH(keys)] = {};
|
||||
uint32_t missing_group_mask = 0;
|
||||
unsigned i;
|
||||
char *section_name;
|
||||
int ret = 0;
|
||||
|
||||
weston_config_section_get_string(section, "name",
|
||||
§ion_name, "<unnamed>");
|
||||
if (strchr(section_name, ':') != NULL) {
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: reserved name. Do not use ':' character in the name.\n",
|
||||
msgpfx, section_name);
|
||||
}
|
||||
|
||||
/* Parse keys if they exist */
|
||||
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
|
||||
double value;
|
||||
|
||||
if (weston_config_section_get_double(section, keys[i].name,
|
||||
&value, NAN) == 0) {
|
||||
float f = value;
|
||||
|
||||
found[i] = true;
|
||||
|
||||
/* Range check, NaN shall not pass. */
|
||||
if (f >= keys[i].minval && f <= keys[i].maxval) {
|
||||
/* Key found, parsed, and good value. */
|
||||
*keyvalp[i] = f;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: %s value %f is outside of the range %f - %f.\n",
|
||||
msgpfx, section_name, keys[i].name, value,
|
||||
keys[i].minval, keys[i].maxval);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno == EINVAL) {
|
||||
found[i] = true;
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: failed to parse the value of key %s.\n",
|
||||
msgpfx, section_name, keys[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
/* Collect set and unset groups */
|
||||
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
|
||||
uint32_t group = keys[i].group;
|
||||
|
||||
if (found[i])
|
||||
cc.group_mask |= group;
|
||||
else
|
||||
missing_group_mask |= group;
|
||||
}
|
||||
|
||||
/* Ensure groups are given fully or not at all. */
|
||||
for (i = 0; i < ARRAY_LENGTH(keys); i++) {
|
||||
uint32_t group = keys[i].group;
|
||||
|
||||
if ((cc.group_mask & group) && (missing_group_mask & group)) {
|
||||
ret = -1;
|
||||
weston_log("%s name=%s: group %d key %s is %s. "
|
||||
"You must set either none or all keys of a group.\n",
|
||||
msgpfx, section_name, ffs(group), keys[i].name,
|
||||
found[i] ? "set" : "missing");
|
||||
}
|
||||
}
|
||||
|
||||
free(section_name);
|
||||
|
||||
if (ret == 0)
|
||||
*cc_out = cc;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
WESTON_EXPORT_FOR_TESTS int
|
||||
wet_output_set_color_characteristics(struct weston_output *output,
|
||||
struct weston_config *wc,
|
||||
struct weston_config_section *section)
|
||||
{
|
||||
char *cc_name = NULL;
|
||||
struct weston_config_section *cc_section;
|
||||
struct weston_color_characteristics cc;
|
||||
|
||||
weston_config_section_get_string(section, COLOR_CHARAC_NAME,
|
||||
&cc_name, NULL);
|
||||
if (!cc_name)
|
||||
return 0;
|
||||
|
||||
cc_section = weston_config_get_section(wc, COLOR_CHARAC_NAME,
|
||||
"name", cc_name);
|
||||
if (!cc_section) {
|
||||
weston_log("Config error in weston.ini, output %s: "
|
||||
"no [" COLOR_CHARAC_NAME "] section with 'name=%s' found.\n",
|
||||
output->name, cc_name);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (parse_color_characteristics(&cc, cc_section) < 0)
|
||||
goto out_error;
|
||||
|
||||
weston_output_set_color_characteristics(output, &cc);
|
||||
free(cc_name);
|
||||
return 0;
|
||||
|
||||
out_error:
|
||||
free(cc_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
allow_content_protection(struct weston_output *output,
|
||||
struct weston_config_section *section)
|
||||
@@ -1503,14 +1709,46 @@ wet_head_tracker_create(struct wet_compositor *compositor,
|
||||
weston_head_add_destroy_listener(head, &track->head_destroy_listener);
|
||||
}
|
||||
|
||||
/* Place output exactly to the right of the most recently enabled output.
|
||||
*
|
||||
* Historically, we haven't given much thought to output placement,
|
||||
* simply adding outputs in a horizontal line as they're enabled. This
|
||||
* function simply sets an output's x coordinate to the right of the
|
||||
* most recently enabled output, and its y to zero.
|
||||
*
|
||||
* If you're adding new calls to this function, you're also not giving
|
||||
* much thought to output placement, so please consider carefully if
|
||||
* it's really doing what you want.
|
||||
*
|
||||
* You especially don't want to use this for any code that won't
|
||||
* immediately enable the passed output.
|
||||
*/
|
||||
static void
|
||||
weston_output_lazy_align(struct weston_output *output)
|
||||
{
|
||||
struct weston_compositor *c;
|
||||
struct weston_output *peer;
|
||||
int next_x = 0;
|
||||
|
||||
/* Put this output to the right of the most recently enabled output */
|
||||
c = output->compositor;
|
||||
if (!wl_list_empty(&c->output_list)) {
|
||||
peer = container_of(c->output_list.prev,
|
||||
struct weston_output, link);
|
||||
next_x = peer->x + peer->width;
|
||||
}
|
||||
output->x = next_x;
|
||||
output->y = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
simple_head_enable(struct wet_compositor *wet, struct weston_head *head)
|
||||
{
|
||||
struct weston_output *output;
|
||||
int ret = 0;
|
||||
|
||||
output = weston_compositor_create_output_with_head(wet->compositor,
|
||||
head);
|
||||
output = weston_compositor_create_output(wet->compositor, head,
|
||||
head->name);
|
||||
if (!output) {
|
||||
weston_log("Could not create an output for head \"%s\".\n",
|
||||
weston_head_get_name(head));
|
||||
@@ -1519,6 +1757,8 @@ simple_head_enable(struct wet_compositor *wet, struct weston_head *head)
|
||||
return;
|
||||
}
|
||||
|
||||
weston_output_lazy_align(output);
|
||||
|
||||
if (wet->simple_output_configure)
|
||||
ret = wet->simple_output_configure(output);
|
||||
if (ret < 0) {
|
||||
@@ -1813,6 +2053,8 @@ drm_backend_output_configure(struct weston_output *output,
|
||||
enum weston_drm_backend_output_mode mode =
|
||||
WESTON_DRM_BACKEND_OUTPUT_PREFERRED;
|
||||
uint32_t transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
uint32_t max_bpc = 0;
|
||||
bool max_bpc_specified = false;
|
||||
char *s;
|
||||
char *modeline = NULL;
|
||||
char *gbm_format = NULL;
|
||||
@@ -1825,12 +2067,18 @@ drm_backend_output_configure(struct weston_output *output,
|
||||
}
|
||||
|
||||
weston_config_section_get_string(section, "mode", &s, "preferred");
|
||||
if (weston_config_section_get_uint(section, "max-bpc", &max_bpc, 16) == 0)
|
||||
max_bpc_specified = true;
|
||||
|
||||
if (strcmp(s, "off") == 0) {
|
||||
assert(0 && "off was supposed to be pruned");
|
||||
return -1;
|
||||
} else if (wet->drm_use_current_mode || strcmp(s, "current") == 0) {
|
||||
mode = WESTON_DRM_BACKEND_OUTPUT_CURRENT;
|
||||
/* If mode=current and no max-bpc was specfied on the .ini file,
|
||||
use current max_bpc so full modeset is not done. */
|
||||
if (!max_bpc_specified)
|
||||
max_bpc = 0;
|
||||
} else if (strcmp(s, "preferred") != 0) {
|
||||
modeline = s;
|
||||
s = NULL;
|
||||
@@ -1844,6 +2092,8 @@ drm_backend_output_configure(struct weston_output *output,
|
||||
}
|
||||
free(modeline);
|
||||
|
||||
api->set_max_bpc(output, max_bpc);
|
||||
|
||||
if (count_remaining_heads(output, NULL) == 1) {
|
||||
struct weston_head *head = weston_output_get_first_head(output);
|
||||
transform = weston_head_get_transform(head);
|
||||
@@ -1871,6 +2121,13 @@ drm_backend_output_configure(struct weston_output *output,
|
||||
|
||||
allow_content_protection(output, section);
|
||||
|
||||
if (wet_output_set_eotf_mode(output, section) < 0)
|
||||
return -1;
|
||||
|
||||
if (wet_output_set_color_characteristics(output,
|
||||
wet->config, section) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1958,7 +2215,9 @@ wet_output_handle_destroy(struct wl_listener *listener, void *data)
|
||||
}
|
||||
|
||||
static struct wet_output *
|
||||
wet_layoutput_create_output(struct wet_layoutput *lo, const char *name)
|
||||
wet_layoutput_create_output_with_head(struct wet_layoutput *lo,
|
||||
const char *name,
|
||||
struct weston_head *head)
|
||||
{
|
||||
struct wet_output *output;
|
||||
|
||||
@@ -1968,7 +2227,7 @@ wet_layoutput_create_output(struct wet_layoutput *lo, const char *name)
|
||||
|
||||
output->output =
|
||||
weston_compositor_create_output(lo->compositor->compositor,
|
||||
name);
|
||||
head, name);
|
||||
if (!output->output) {
|
||||
free(output);
|
||||
return NULL;
|
||||
@@ -2121,8 +2380,8 @@ drm_try_attach(struct weston_output *output,
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
/* try to attach all heads, this probably succeeds */
|
||||
for (i = 0; i < add->n; i++) {
|
||||
/* try to attach remaining heads, this probably succeeds */
|
||||
for (i = 1; i < add->n; i++) {
|
||||
if (!add->heads[i])
|
||||
continue;
|
||||
|
||||
@@ -2142,6 +2401,8 @@ drm_try_enable(struct weston_output *output,
|
||||
{
|
||||
/* Try to enable, and detach heads one by one until it succeeds. */
|
||||
while (!output->enabled) {
|
||||
weston_output_lazy_align(output);
|
||||
|
||||
if (weston_output_enable(output) == 0)
|
||||
return 0;
|
||||
|
||||
@@ -2238,7 +2499,8 @@ drm_process_layoutput(struct wet_compositor *wet, struct wet_layoutput *lo)
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
output = wet_layoutput_create_output(lo, name);
|
||||
output = wet_layoutput_create_output_with_head(lo, name,
|
||||
lo->add.heads[0]);
|
||||
free(name);
|
||||
name = NULL;
|
||||
|
||||
@@ -2625,17 +2887,26 @@ load_drm_backend(struct weston_compositor *c,
|
||||
struct weston_config_section *section;
|
||||
struct wet_compositor *wet = to_wet_compositor(c);
|
||||
bool without_input = false;
|
||||
bool use_pixman_default;
|
||||
int ret = 0;
|
||||
|
||||
wet->drm_use_current_mode = false;
|
||||
|
||||
section = weston_config_get_section(wc, "core", NULL, NULL);
|
||||
|
||||
/* Use the pixman renderer by default when GBM/EGL support is
|
||||
* not enabled */
|
||||
#if defined(BUILD_DRM_GBM)
|
||||
use_pixman_default = false;
|
||||
#else
|
||||
use_pixman_default = true;
|
||||
#endif
|
||||
|
||||
weston_config_section_get_bool(section, "use-pixman", &config.use_pixman,
|
||||
false);
|
||||
use_pixman_default);
|
||||
|
||||
const struct weston_option options[] = {
|
||||
{ WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
|
||||
{ WESTON_OPTION_INTEGER, "tty", 0, &config.tty },
|
||||
{ WESTON_OPTION_STRING, "drm-device", 0, &config.specific_device },
|
||||
{ WESTON_OPTION_BOOLEAN, "current-mode", 0, &wet->drm_use_current_mode },
|
||||
{ WESTON_OPTION_BOOLEAN, "use-pixman", 0, &config.use_pixman },
|
||||
@@ -2688,6 +2959,15 @@ headless_backend_output_configure(struct weston_output *output)
|
||||
.scale = 1,
|
||||
.transform = WL_OUTPUT_TRANSFORM_NORMAL
|
||||
};
|
||||
struct weston_config *wc = wet_get_config(output->compositor);
|
||||
struct weston_config_section *section;
|
||||
|
||||
section = weston_config_get_section(wc, "output", "name", output->name);
|
||||
if (wet_output_set_eotf_mode(output, section) < 0)
|
||||
return -1;
|
||||
|
||||
if (wet_output_set_color_characteristics(output, wc, section) < 0)
|
||||
return -1;
|
||||
|
||||
return wet_configure_windowed_output_from_config(output, &defaults);
|
||||
}
|
||||
@@ -2806,8 +3086,11 @@ weston_rdp_backend_config_init(struct weston_rdp_backend_config *config)
|
||||
config->server_cert = NULL;
|
||||
config->server_key = NULL;
|
||||
config->env_socket = 0;
|
||||
config->external_listener_fd = -1;
|
||||
config->no_clients_resize = 0;
|
||||
config->force_no_compression = 0;
|
||||
config->remotefx_codec = true;
|
||||
config->refresh_rate = RDP_DEFAULT_FREQ;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -2815,7 +3098,9 @@ load_rdp_backend(struct weston_compositor *c,
|
||||
int *argc, char *argv[], struct weston_config *wc)
|
||||
{
|
||||
struct weston_rdp_backend_config config = {{ 0, }};
|
||||
struct weston_config_section *section;
|
||||
int ret = 0;
|
||||
bool no_remotefx_codec = false;
|
||||
|
||||
struct wet_output_config *parsed_options = wet_init_parsed_options(c);
|
||||
if (!parsed_options)
|
||||
@@ -2825,6 +3110,7 @@ load_rdp_backend(struct weston_compositor *c,
|
||||
|
||||
const struct weston_option rdp_options[] = {
|
||||
{ WESTON_OPTION_BOOLEAN, "env-socket", 0, &config.env_socket },
|
||||
{ WESTON_OPTION_INTEGER, "external-listener-fd", 0, &config.external_listener_fd },
|
||||
{ WESTON_OPTION_INTEGER, "width", 0, &parsed_options->width },
|
||||
{ WESTON_OPTION_INTEGER, "height", 0, &parsed_options->height },
|
||||
{ WESTON_OPTION_STRING, "address", 0, &config.bind_address },
|
||||
@@ -2834,11 +3120,17 @@ load_rdp_backend(struct weston_compositor *c,
|
||||
{ WESTON_OPTION_STRING, "rdp-tls-cert", 0, &config.server_cert },
|
||||
{ WESTON_OPTION_STRING, "rdp-tls-key", 0, &config.server_key },
|
||||
{ WESTON_OPTION_BOOLEAN, "force-no-compression", 0, &config.force_no_compression },
|
||||
{ WESTON_OPTION_BOOLEAN, "no-remotefx-codec", 0, &no_remotefx_codec },
|
||||
};
|
||||
|
||||
parse_options(rdp_options, ARRAY_LENGTH(rdp_options), argc, argv);
|
||||
config.remotefx_codec = !no_remotefx_codec;
|
||||
|
||||
wet_set_simple_head_configurator(c, rdp_backend_output_configure);
|
||||
section = weston_config_get_section(wc, "rdp", NULL, NULL);
|
||||
weston_config_section_get_int(section, "refresh-rate",
|
||||
&config.refresh_rate,
|
||||
RDP_DEFAULT_FREQ);
|
||||
|
||||
ret = weston_compositor_load_backend(c, WESTON_BACKEND_RDP,
|
||||
&config.base);
|
||||
@@ -2851,54 +3143,6 @@ load_rdp_backend(struct weston_compositor *c,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_backend_output_configure(struct weston_output *output)
|
||||
{
|
||||
struct weston_config *wc = wet_get_config(output->compositor);
|
||||
struct weston_config_section *section;
|
||||
|
||||
section = weston_config_get_section(wc, "output", "name", "fbdev");
|
||||
|
||||
if (wet_output_set_transform(output, section,
|
||||
WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
UINT32_MAX) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
weston_output_set_scale(output, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
load_fbdev_backend(struct weston_compositor *c,
|
||||
int *argc, char **argv, struct weston_config *wc)
|
||||
{
|
||||
struct weston_fbdev_backend_config config = {{ 0, }};
|
||||
int ret = 0;
|
||||
|
||||
const struct weston_option fbdev_options[] = {
|
||||
{ WESTON_OPTION_INTEGER, "tty", 0, &config.tty },
|
||||
{ WESTON_OPTION_STRING, "device", 0, &config.device },
|
||||
{ WESTON_OPTION_STRING, "seat", 0, &config.seat_id },
|
||||
};
|
||||
|
||||
parse_options(fbdev_options, ARRAY_LENGTH(fbdev_options), argc, argv);
|
||||
|
||||
config.base.struct_version = WESTON_FBDEV_BACKEND_CONFIG_VERSION;
|
||||
config.base.struct_size = sizeof(struct weston_fbdev_backend_config);
|
||||
config.configure_device = configure_input_device;
|
||||
|
||||
wet_set_simple_head_configurator(c, fbdev_backend_output_configure);
|
||||
|
||||
/* load the actual wayland backend and configure it */
|
||||
ret = weston_compositor_load_backend(c, WESTON_BACKEND_FBDEV,
|
||||
&config.base);
|
||||
|
||||
free(config.device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
x11_backend_output_configure(struct weston_output *output)
|
||||
{
|
||||
@@ -3144,8 +3388,6 @@ load_backend(struct weston_compositor *compositor, const char *backend,
|
||||
return load_headless_backend(compositor, argc, argv, config);
|
||||
else if (strstr(backend, "rdp-backend.so"))
|
||||
return load_rdp_backend(compositor, argc, argv, config);
|
||||
else if (strstr(backend, "fbdev-backend.so"))
|
||||
return load_fbdev_backend(compositor, argc, argv, config);
|
||||
else if (strstr(backend, "drm-backend.so"))
|
||||
return load_drm_backend(compositor, argc, argv, config);
|
||||
else if (strstr(backend, "x11-backend.so"))
|
||||
@@ -3266,13 +3508,19 @@ weston_log_subscribe_to_scopes(struct weston_log_context *log_ctx,
|
||||
weston_log_setup_scopes(log_ctx, flight_rec, flight_rec_scopes);
|
||||
}
|
||||
|
||||
static void
|
||||
sigint_helper(int sig)
|
||||
{
|
||||
raise(SIGUSR2);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
|
||||
{
|
||||
int ret = EXIT_FAILURE;
|
||||
char *cmdline;
|
||||
struct wl_display *display;
|
||||
struct wl_event_source *signals[4];
|
||||
struct wl_event_source *signals[3];
|
||||
struct wl_event_loop *loop;
|
||||
int i, fd;
|
||||
char *backend = NULL;
|
||||
@@ -3302,6 +3550,7 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
|
||||
struct weston_log_subscriber *logger = NULL;
|
||||
struct weston_log_subscriber *flight_rec = NULL;
|
||||
sigset_t mask;
|
||||
struct sigaction action;
|
||||
|
||||
bool wait_for_debugger = false;
|
||||
struct wl_protocol_logger *protologger = NULL;
|
||||
@@ -3392,16 +3641,28 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
|
||||
loop = wl_display_get_event_loop(display);
|
||||
signals[0] = wl_event_loop_add_signal(loop, SIGTERM, on_term_signal,
|
||||
display);
|
||||
signals[1] = wl_event_loop_add_signal(loop, SIGINT, on_term_signal,
|
||||
display);
|
||||
signals[2] = wl_event_loop_add_signal(loop, SIGQUIT, on_term_signal,
|
||||
signals[1] = wl_event_loop_add_signal(loop, SIGUSR2, on_term_signal,
|
||||
display);
|
||||
|
||||
wl_list_init(&wet.child_process_list);
|
||||
signals[3] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler,
|
||||
signals[2] = wl_event_loop_add_signal(loop, SIGCHLD, sigchld_handler,
|
||||
&wet);
|
||||
|
||||
if (!signals[0] || !signals[1] || !signals[2] || !signals[3])
|
||||
/* When debugging weston, if use wl_event_loop_add_signal() to catch
|
||||
* SIGINT, the debugger can't catch it, and attempting to stop
|
||||
* weston from within the debugger results in weston exiting
|
||||
* cleanly.
|
||||
*
|
||||
* Instead, use the sigaction() function, which sets up the signal
|
||||
* in a way that gdb can successfully catch, but have the handler
|
||||
* for SIGINT send SIGUSR2 (xwayland uses SIGUSR1), which we catch
|
||||
* via wl_event_loop_add_signal().
|
||||
*/
|
||||
action.sa_handler = sigint_helper;
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_flags = 0;
|
||||
sigaction(SIGINT, &action, NULL);
|
||||
if (!signals[0] || !signals[1] || !signals[2])
|
||||
goto out_signals;
|
||||
|
||||
/* Xwayland uses SIGUSR1 for communicating with weston. Since some
|
||||
@@ -3522,13 +3783,10 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
|
||||
if (wet_load_shell(wet.compositor, shell, &argc, argv) < 0)
|
||||
goto out;
|
||||
|
||||
weston_config_section_get_string(section, "modules", &modules, "");
|
||||
if (load_modules(wet.compositor, modules, &argc, argv, &xwayland) < 0)
|
||||
goto out;
|
||||
|
||||
if (load_modules(wet.compositor, option_modules, &argc, argv, &xwayland) < 0)
|
||||
goto out;
|
||||
|
||||
/* Load xwayland before other modules - this way if we're using
|
||||
* the systemd-notify module it will notify after we're ready
|
||||
* to receive xwayland connections.
|
||||
*/
|
||||
if (!xwayland) {
|
||||
weston_config_section_get_bool(section, "xwayland", &xwayland,
|
||||
false);
|
||||
@@ -3538,6 +3796,13 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
|
||||
goto out;
|
||||
}
|
||||
|
||||
weston_config_section_get_string(section, "modules", &modules, "");
|
||||
if (load_modules(wet.compositor, modules, &argc, argv) < 0)
|
||||
goto out;
|
||||
|
||||
if (load_modules(wet.compositor, option_modules, &argc, argv) < 0)
|
||||
goto out;
|
||||
|
||||
section = weston_config_get_section(config, "keyboard", NULL, NULL);
|
||||
weston_config_section_get_bool(section, "numlock-on", &numlock_on, false);
|
||||
if (numlock_on) {
|
||||
@@ -3573,8 +3838,6 @@ wet_main(int argc, char *argv[], const struct weston_testsuite_data *test_data)
|
||||
ret = wet.compositor->exit_code;
|
||||
|
||||
out:
|
||||
wet_compositor_destroy_layout(&wet);
|
||||
|
||||
/* free(NULL) is valid, and it won't be NULL if it's used */
|
||||
free(wet.parsed_options);
|
||||
|
||||
@@ -3582,6 +3845,7 @@ out:
|
||||
wl_protocol_logger_destroy(protologger);
|
||||
|
||||
weston_compositor_destroy(wet.compositor);
|
||||
wet_compositor_destroy_layout(&wet);
|
||||
weston_log_scope_destroy(protocol_scope);
|
||||
protocol_scope = NULL;
|
||||
|
||||
|
||||
+14
-10
@@ -78,6 +78,7 @@ if get_option('screenshare')
|
||||
deps_screenshare = [
|
||||
dep_libexec_weston,
|
||||
dep_libshared,
|
||||
dep_shell_utils,
|
||||
dep_libweston_public,
|
||||
dep_libweston_private_h, # XXX: https://gitlab.freedesktop.org/wayland/weston/issues/292
|
||||
dep_wayland_client,
|
||||
@@ -95,19 +96,18 @@ if get_option('screenshare')
|
||||
env_modmap += 'screen-share.so=@0@;'.format(plugin_screenshare.full_path())
|
||||
endif
|
||||
|
||||
if get_option('color-management-lcms')
|
||||
config_h.set('HAVE_LCMS', '1')
|
||||
|
||||
if get_option('deprecated-color-management-static')
|
||||
srcs_lcms = [
|
||||
'cms-static.c',
|
||||
'cms-helper.c',
|
||||
]
|
||||
|
||||
dep_lcms2 = dependency('lcms2', required: false)
|
||||
if not dep_lcms2.found()
|
||||
error('cms-static requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.')
|
||||
error('cms-static requires lcms2 which was not found. Or, you can use \'-Ddeprecated-color-management-static=false\'.')
|
||||
endif
|
||||
|
||||
config_h.set('HAVE_LCMS', '1')
|
||||
|
||||
plugin_lcms = shared_library(
|
||||
'cms-static',
|
||||
srcs_lcms,
|
||||
@@ -119,11 +119,13 @@ if get_option('color-management-lcms')
|
||||
install_rpath: '$ORIGIN'
|
||||
)
|
||||
env_modmap += 'cms-static.so=@0@;'.format(plugin_lcms.full_path())
|
||||
|
||||
warning('deprecated-color-management-static is enabled. This will go away, see https://gitlab.freedesktop.org/wayland/weston/-/issues/634')
|
||||
endif
|
||||
|
||||
if get_option('color-management-colord')
|
||||
if not get_option('color-management-lcms')
|
||||
error('LCMS must be enabled to support colord. Or, you can use \'-Dcolor-management-colord=false\'.')
|
||||
if get_option('deprecated-color-management-colord')
|
||||
if not get_option('deprecated-color-management-static')
|
||||
error('deprecated-color-management-static must be enabled to support colord. Or, you can use \'-Ddeprecated-color-management-colord=false\'.')
|
||||
endif
|
||||
|
||||
srcs_colord = [
|
||||
@@ -133,7 +135,7 @@ if get_option('color-management-colord')
|
||||
|
||||
dep_colord = dependency('colord', version: '>= 0.1.27', required: false)
|
||||
if not dep_colord.found()
|
||||
error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Dcolor-management-colord=false\'.')
|
||||
error('cms-colord requires colord >= 0.1.27 which was not found. Or, you can use \'-Ddeprecated-color-management-colord=false\'.')
|
||||
endif
|
||||
|
||||
plugin_colord_deps = [ dep_libweston_public, dep_colord, dep_lcms2 ]
|
||||
@@ -141,7 +143,7 @@ if get_option('color-management-colord')
|
||||
foreach depname : [ 'glib-2.0', 'gobject-2.0' ]
|
||||
dep = dependency(depname, required: false)
|
||||
if not dep.found()
|
||||
error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Dcolor-management-colord=false\'.'.format(depname))
|
||||
error('cms-colord requires \'@0@\' which was not found. If you rather not build this, set \'-Ddeprecated-color-management-colord=false\'.'.format(depname))
|
||||
endif
|
||||
plugin_colord_deps += dep
|
||||
endforeach
|
||||
@@ -156,6 +158,8 @@ if get_option('color-management-colord')
|
||||
install_dir: dir_module_weston
|
||||
)
|
||||
env_modmap += 'cms-colord.so=@0@;'.format(plugin_colord.full_path())
|
||||
|
||||
warning('deprecated-color-management-colord is enabled. This will go away, see https://gitlab.freedesktop.org/wayland/weston/-/issues/634')
|
||||
endif
|
||||
|
||||
if get_option('systemd')
|
||||
|
||||
+42
-18
@@ -43,10 +43,12 @@
|
||||
#include <libweston/libweston.h>
|
||||
#include "backend.h"
|
||||
#include "libweston-internal.h"
|
||||
#include "pixel-formats.h"
|
||||
#include "weston.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/os-compatibility.h"
|
||||
#include "shared/timespec-util.h"
|
||||
#include "shell-utils/shell-utils.h"
|
||||
#include "fullscreen-shell-unstable-v1-client-protocol.h"
|
||||
|
||||
struct shared_output {
|
||||
@@ -59,7 +61,7 @@ struct shared_output {
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shm *shm;
|
||||
uint32_t shm_formats;
|
||||
bool shm_formats_has_xrgb;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
struct wl_output *output;
|
||||
struct wl_surface *surface;
|
||||
@@ -114,9 +116,7 @@ struct ss_shm_buffer {
|
||||
|
||||
struct screen_share {
|
||||
struct weston_compositor *compositor;
|
||||
/* XXX: missing compositor destroy listener
|
||||
* https://gitlab.freedesktop.org/wayland/weston/issues/298
|
||||
*/
|
||||
struct wl_listener compositor_destroy_listener;
|
||||
char *command;
|
||||
};
|
||||
|
||||
@@ -368,7 +368,7 @@ ss_seat_create(struct shared_output *so, uint32_t id)
|
||||
if (seat == NULL)
|
||||
return NULL;
|
||||
|
||||
weston_seat_init(&seat->base, so->output->compositor, "default");
|
||||
weston_seat_init(&seat->base, so->output->compositor, "screen-share");
|
||||
seat->output = so;
|
||||
seat->id = id;
|
||||
seat->parent.seat = wl_registry_bind(so->parent.registry, id,
|
||||
@@ -702,7 +702,8 @@ shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format)
|
||||
{
|
||||
struct shared_output *so = data;
|
||||
|
||||
so->parent.shm_formats |= (1 << format);
|
||||
if (format == WL_SHM_FORMAT_XRGB8888)
|
||||
so->parent.shm_formats_has_xrgb = true;
|
||||
}
|
||||
|
||||
struct wl_shm_listener shm_listener = {
|
||||
@@ -828,6 +829,9 @@ shared_output_repainted(struct wl_listener *listener, void *data)
|
||||
pixman_box32_t *r;
|
||||
pixman_image_t *damaged_image;
|
||||
pixman_transform_t transform;
|
||||
const struct pixel_format_info *read_format =
|
||||
so->output->compositor->read_format;
|
||||
const pixman_format_code_t pixman_format = read_format->pixman_format;
|
||||
|
||||
width = so->output->current_mode->width;
|
||||
height = so->output->current_mode->height;
|
||||
@@ -882,13 +886,13 @@ shared_output_repainted(struct wl_listener *listener, void *data)
|
||||
y_orig = y;
|
||||
|
||||
so->output->compositor->renderer->read_pixels(
|
||||
so->output, PIXMAN_a8r8g8b8, so->tmp_data,
|
||||
x, y_orig, width, height);
|
||||
so->output, read_format,
|
||||
so->tmp_data, x, y_orig, width, height);
|
||||
|
||||
damaged_image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
damaged_image = pixman_image_create_bits(pixman_format,
|
||||
width, height,
|
||||
so->tmp_data,
|
||||
(PIXMAN_FORMAT_BPP(PIXMAN_a8r8g8b8) / 8) * width);
|
||||
(PIXMAN_FORMAT_BPP(pixman_format) / 8) * width);
|
||||
if (!damaged_image)
|
||||
goto err_pixman_init;
|
||||
|
||||
@@ -968,7 +972,7 @@ shared_output_create(struct weston_output *output, int parent_fd)
|
||||
|
||||
/* Get SHM formats */
|
||||
wl_display_roundtrip(so->parent.display);
|
||||
if (!(so->parent.shm_formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
|
||||
if (!so->parent.shm_formats_has_xrgb) {
|
||||
weston_log("Screen share failed: "
|
||||
"WL_SHM_FORMAT_XRGB8888 not available\n");
|
||||
goto err_display;
|
||||
@@ -1144,22 +1148,37 @@ share_output_binding(struct weston_keyboard *keyboard,
|
||||
struct screen_share *ss = data;
|
||||
|
||||
pointer = weston_seat_get_pointer(keyboard->seat);
|
||||
if (!pointer) {
|
||||
weston_log("Cannot pick output: Seat does not have pointer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (pointer) {
|
||||
output = weston_output_find(pointer->seat->compositor,
|
||||
wl_fixed_to_int(pointer->x),
|
||||
wl_fixed_to_int(pointer->y));
|
||||
} else {
|
||||
output = get_focused_output(keyboard->seat->compositor);
|
||||
if (!output)
|
||||
output = get_default_output(keyboard->seat->compositor);
|
||||
}
|
||||
|
||||
if (!output) {
|
||||
weston_log("Cannot pick output: Pointer not on any output\n");
|
||||
weston_log("Cannot pick output: Pointer not on any output, "
|
||||
"or no focused/default output found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
weston_output_share(output, ss->command);
|
||||
}
|
||||
|
||||
static void
|
||||
compositor_destroy_listener(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct screen_share *ss =
|
||||
wl_container_of(listener, ss, compositor_destroy_listener);
|
||||
|
||||
wl_list_remove(&ss->compositor_destroy_listener.link);
|
||||
|
||||
free(ss->command);
|
||||
free(ss);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
wet_module_init(struct weston_compositor *compositor,
|
||||
int *argc, char *argv[])
|
||||
@@ -1175,11 +1194,16 @@ wet_module_init(struct weston_compositor *compositor,
|
||||
return -1;
|
||||
ss->compositor = compositor;
|
||||
|
||||
wl_list_init(&ss->compositor_destroy_listener.link);
|
||||
|
||||
ss->compositor_destroy_listener.notify = compositor_destroy_listener;
|
||||
wl_signal_add(&compositor->destroy_signal, &ss->compositor_destroy_listener);
|
||||
|
||||
config = wet_get_config(compositor);
|
||||
|
||||
section = weston_config_get_section(config, "screen-share", NULL, NULL);
|
||||
|
||||
weston_config_section_get_string(section, "command", &ss->command, "");
|
||||
weston_config_section_get_string(section, "command", &ss->command, NULL);
|
||||
|
||||
weston_compositor_add_key_binding(compositor, KEY_S,
|
||||
MODIFIER_CTRL | MODIFIER_ALT,
|
||||
|
||||
@@ -141,6 +141,12 @@ deactivate_input_method(struct input_method *input_method)
|
||||
input_method->input = NULL;
|
||||
input_method->context = NULL;
|
||||
|
||||
/* text_input_manager::destroy_listener by compositor shutdown */
|
||||
if (!text_input->manager) {
|
||||
zwp_text_input_v1_send_leave(text_input->resource);
|
||||
return;
|
||||
}
|
||||
|
||||
if (wl_list_empty(&text_input->input_methods) &&
|
||||
text_input->input_panel_visible &&
|
||||
text_input->manager->current_text_input == text_input) {
|
||||
@@ -456,6 +462,8 @@ text_input_manager_notifier_destroy(struct wl_listener *listener, void *data)
|
||||
wl_list_remove(&text_input_manager->destroy_listener.link);
|
||||
wl_global_destroy(text_input_manager->text_input_manager_global);
|
||||
|
||||
if (text_input_manager->current_text_input)
|
||||
text_input_manager->current_text_input->manager = NULL;
|
||||
free(text_input_manager);
|
||||
}
|
||||
|
||||
@@ -949,7 +957,7 @@ input_method_init_seat(struct weston_seat *seat)
|
||||
seat->input_method->focus_listener_initialized = true;
|
||||
}
|
||||
|
||||
static void launch_input_method(struct text_backend *text_backend);
|
||||
static void launch_input_method(void *data);
|
||||
|
||||
static void
|
||||
respawn_input_method_process(struct text_backend *text_backend)
|
||||
@@ -989,8 +997,10 @@ input_method_client_notifier(struct wl_listener *listener, void *data)
|
||||
}
|
||||
|
||||
static void
|
||||
launch_input_method(struct text_backend *text_backend)
|
||||
launch_input_method(void *data)
|
||||
{
|
||||
struct text_backend *text_backend = data;
|
||||
|
||||
if (!text_backend->input_method.path)
|
||||
return;
|
||||
|
||||
@@ -1093,6 +1103,7 @@ text_backend_init(struct weston_compositor *ec)
|
||||
{
|
||||
struct text_backend *text_backend;
|
||||
struct weston_seat *seat;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
text_backend = zalloc(sizeof(*text_backend));
|
||||
if (text_backend == NULL)
|
||||
@@ -1110,7 +1121,8 @@ text_backend_init(struct weston_compositor *ec)
|
||||
|
||||
text_input_manager_create(ec);
|
||||
|
||||
launch_input_method(text_backend);
|
||||
loop = wl_display_get_event_loop(ec->wl_display);
|
||||
wl_event_loop_add_idle(loop, launch_input_method, text_backend);
|
||||
|
||||
return text_backend;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
* Copyright 2022 Collabora, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -23,28 +23,12 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#pragma once
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/config-parser.h>
|
||||
|
||||
#include "xalloc.h"
|
||||
|
||||
void *
|
||||
fail_on_null(void *p, size_t size, char *file, int32_t line)
|
||||
{
|
||||
if (p == NULL) {
|
||||
fprintf(stderr, "[%s] ", program_invocation_short_name);
|
||||
if (file)
|
||||
fprintf(stderr, "%s:%d: ", file, line);
|
||||
fprintf(stderr, "out of memory");
|
||||
if (size)
|
||||
fprintf(stderr, " (%zd)", size);
|
||||
fprintf(stderr, "\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
int
|
||||
wet_output_set_color_characteristics(struct weston_output *output,
|
||||
struct weston_config *wc,
|
||||
struct weston_config_section *section);
|
||||
@@ -68,8 +68,9 @@ screenshooter_take_shot(struct wl_client *client,
|
||||
{
|
||||
struct weston_output *output =
|
||||
weston_head_from_resource(output_resource)->output;
|
||||
struct weston_compositor *ec = output->compositor;
|
||||
struct weston_buffer *buffer =
|
||||
weston_buffer_from_resource(buffer_resource);
|
||||
weston_buffer_from_resource(ec, buffer_resource);
|
||||
|
||||
if (buffer == NULL) {
|
||||
wl_resource_post_no_memory(resource);
|
||||
|
||||
+144
-78
@@ -30,34 +30,65 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include "compositor/weston.h"
|
||||
#include <libweston/xwayland-api.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/os-compatibility.h"
|
||||
#include "shared/process-util.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
#ifdef HAVE_XWAYLAND_LISTENFD
|
||||
# define LISTEN_STR "-listenfd"
|
||||
#else
|
||||
# define LISTEN_STR "-listen"
|
||||
#endif
|
||||
|
||||
struct wet_xwayland {
|
||||
struct weston_compositor *compositor;
|
||||
struct wl_listener compositor_destroy_listener;
|
||||
const struct weston_xwayland_api *api;
|
||||
struct weston_xwayland *xwayland;
|
||||
struct wl_event_source *sigusr1_source;
|
||||
struct wl_event_source *display_fd_source;
|
||||
struct wl_client *client;
|
||||
int wm_fd;
|
||||
struct weston_process process;
|
||||
};
|
||||
|
||||
static int
|
||||
handle_sigusr1(int signal_number, void *data)
|
||||
handle_display_fd(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct wet_xwayland *wxw = data;
|
||||
char buf[64];
|
||||
ssize_t n;
|
||||
|
||||
/* We'd be safer if we actually had the struct
|
||||
* signalfd_siginfo from the signalfd data and could verify
|
||||
* this came from Xwayland.*/
|
||||
wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd);
|
||||
wl_event_source_remove(wxw->sigusr1_source);
|
||||
/* xwayland exited before being ready, don't finish initialization,
|
||||
* the process watcher will cleanup */
|
||||
if (!(mask & WL_EVENT_READABLE))
|
||||
goto out;
|
||||
|
||||
/* Xwayland writes to the pipe twice, so if we close it too early
|
||||
* it's possible the second write will fail and Xwayland shuts down.
|
||||
* Make sure we read until end of line marker to avoid this. */
|
||||
n = read(fd, buf, sizeof buf);
|
||||
if (n < 0 && errno != EAGAIN) {
|
||||
weston_log("read from Xwayland display_fd failed: %s\n",
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
/* Returning 1 here means recheck and call us again if required. */
|
||||
if (n <= 0 || (n > 0 && buf[n - 1] != '\n'))
|
||||
return 1;
|
||||
|
||||
wxw->api->xserver_loaded(wxw->xwayland, wxw->client, wxw->wm_fd);
|
||||
|
||||
out:
|
||||
wl_event_source_remove(wxw->display_fd_source);
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pid_t
|
||||
@@ -65,93 +96,109 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
||||
{
|
||||
struct wet_xwayland *wxw = user_data;
|
||||
pid_t pid;
|
||||
char s[12], abstract_fd_str[12], unix_fd_str[12], wm_fd_str[12];
|
||||
int sv[2], wm[2], fd;
|
||||
struct fdstr wayland_socket;
|
||||
struct fdstr x11_abstract_socket;
|
||||
struct fdstr x11_unix_socket;
|
||||
struct fdstr x11_wm_socket;
|
||||
struct fdstr display_pipe;
|
||||
char *xserver = NULL;
|
||||
struct weston_config *config = wet_get_config(wxw->compositor);
|
||||
struct weston_config_section *section;
|
||||
struct wl_event_loop *loop;
|
||||
char *exec_failure_msg;
|
||||
struct custom_env child_env;
|
||||
char *const *envp;
|
||||
char *const *argp;
|
||||
bool ret;
|
||||
size_t written __attribute__ ((unused));
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
|
||||
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, wayland_socket.fds) < 0) {
|
||||
weston_log("wl connection socketpair failed\n");
|
||||
return 1;
|
||||
}
|
||||
fdstr_update_str1(&wayland_socket);
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, wm) < 0) {
|
||||
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, x11_wm_socket.fds) < 0) {
|
||||
weston_log("X wm connection socketpair failed\n");
|
||||
return 1;
|
||||
}
|
||||
fdstr_update_str1(&x11_wm_socket);
|
||||
|
||||
if (pipe2(display_pipe.fds, O_CLOEXEC) < 0) {
|
||||
weston_log("pipe creation for displayfd failed\n");
|
||||
return 1;
|
||||
}
|
||||
fdstr_update_str1(&display_pipe);
|
||||
|
||||
fdstr_set_fd1(&x11_abstract_socket, abstract_fd);
|
||||
fdstr_set_fd1(&x11_unix_socket, unix_fd);
|
||||
|
||||
section = weston_config_get_section(config, "xwayland", NULL, NULL);
|
||||
weston_config_section_get_string(section, "path",
|
||||
&xserver, XSERVER_PATH);
|
||||
str_printf(&exec_failure_msg,
|
||||
"Error: executing Xwayland as '%s' failed.\n", xserver);
|
||||
custom_env_init_from_environ(&child_env);
|
||||
custom_env_set_env_var(&child_env, "WAYLAND_SOCKET", wayland_socket.str1);
|
||||
|
||||
custom_env_add_arg(&child_env, xserver);
|
||||
custom_env_add_arg(&child_env, display);
|
||||
custom_env_add_arg(&child_env, "-rootless");
|
||||
custom_env_add_arg(&child_env, LISTEN_STR);
|
||||
custom_env_add_arg(&child_env, x11_abstract_socket.str1);
|
||||
custom_env_add_arg(&child_env, LISTEN_STR);
|
||||
custom_env_add_arg(&child_env, x11_unix_socket.str1);
|
||||
custom_env_add_arg(&child_env, "-displayfd");
|
||||
custom_env_add_arg(&child_env, display_pipe.str1);
|
||||
custom_env_add_arg(&child_env, "-wm");
|
||||
custom_env_add_arg(&child_env, x11_wm_socket.str1);
|
||||
custom_env_add_arg(&child_env, "-terminate");
|
||||
|
||||
envp = custom_env_get_envp(&child_env);
|
||||
argp = custom_env_get_argp(&child_env);
|
||||
|
||||
pid = fork();
|
||||
switch (pid) {
|
||||
case 0:
|
||||
setsid();
|
||||
/* SOCK_CLOEXEC closes both ends, so we need to unset
|
||||
* the flag on the client fd. */
|
||||
fd = dup(sv[1]);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(s, sizeof s, "%d", fd);
|
||||
setenv("WAYLAND_SOCKET", s, 1);
|
||||
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
|
||||
ret &= fdstr_clear_cloexec_fd1(&x11_abstract_socket);
|
||||
ret &= fdstr_clear_cloexec_fd1(&x11_unix_socket);
|
||||
ret &= fdstr_clear_cloexec_fd1(&x11_wm_socket);
|
||||
ret &= fdstr_clear_cloexec_fd1(&display_pipe);
|
||||
if (!ret)
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
fd = dup(abstract_fd);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(abstract_fd_str, sizeof abstract_fd_str, "%d", fd);
|
||||
fd = dup(unix_fd);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(unix_fd_str, sizeof unix_fd_str, "%d", fd);
|
||||
fd = dup(wm[1]);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
snprintf(wm_fd_str, sizeof wm_fd_str, "%d", fd);
|
||||
execve(xserver, argp, envp);
|
||||
/* execve does not return on success, so it failed */
|
||||
|
||||
section = weston_config_get_section(config,
|
||||
"xwayland", NULL, NULL);
|
||||
weston_config_section_get_string(section, "path",
|
||||
&xserver, XSERVER_PATH);
|
||||
if (exec_failure_msg) {
|
||||
written = write(STDERR_FILENO, exec_failure_msg,
|
||||
strlen(exec_failure_msg));
|
||||
}
|
||||
|
||||
/* Ignore SIGUSR1 in the child, which will make the X
|
||||
* server send SIGUSR1 to the parent (weston) when
|
||||
* it's done with initialization. During
|
||||
* initialization the X server will round trip and
|
||||
* block on the wayland compositor, so avoid making
|
||||
* blocking requests (like xcb_connect_to_fd) until
|
||||
* it's done with that. */
|
||||
signal(SIGUSR1, SIG_IGN);
|
||||
|
||||
if (execl(xserver,
|
||||
xserver,
|
||||
display,
|
||||
"-rootless",
|
||||
#ifdef HAVE_XWAYLAND_LISTENFD
|
||||
"-listenfd", abstract_fd_str,
|
||||
"-listenfd", unix_fd_str,
|
||||
#else
|
||||
"-listen", abstract_fd_str,
|
||||
"-listen", unix_fd_str,
|
||||
#endif
|
||||
"-wm", wm_fd_str,
|
||||
"-terminate",
|
||||
NULL) < 0)
|
||||
weston_log("exec of '%s %s -rootless "
|
||||
#ifdef HAVE_XWAYLAND_LISTENFD
|
||||
"-listenfd %s -listenfd %s "
|
||||
#else
|
||||
"-listen %s -listen %s "
|
||||
#endif
|
||||
"-wm %s -terminate' failed: %s\n",
|
||||
xserver, display,
|
||||
abstract_fd_str, unix_fd_str, wm_fd_str,
|
||||
strerror(errno));
|
||||
fail:
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
default:
|
||||
close(sv[1]);
|
||||
wxw->client = wl_client_create(wxw->compositor->wl_display, sv[0]);
|
||||
close(wayland_socket.fds[1]);
|
||||
wxw->client = wl_client_create(wxw->compositor->wl_display,
|
||||
wayland_socket.fds[0]);
|
||||
|
||||
close(wm[1]);
|
||||
wxw->wm_fd = wm[0];
|
||||
close(x11_wm_socket.fds[1]);
|
||||
wxw->wm_fd = x11_wm_socket.fds[0];
|
||||
|
||||
/* During initialization the X server will round trip
|
||||
* and block on the wayland compositor, so avoid making
|
||||
* blocking requests (like xcb_connect_to_fd) until
|
||||
* it's done with that. */
|
||||
close(display_pipe.fds[1]);
|
||||
loop = wl_display_get_event_loop(wxw->compositor->wl_display);
|
||||
wxw->display_fd_source =
|
||||
wl_event_loop_add_fd(loop, display_pipe.fds[0],
|
||||
WL_EVENT_READABLE,
|
||||
handle_display_fd, wxw);
|
||||
|
||||
wxw->process.pid = pid;
|
||||
wet_watch_process(wxw->compositor, &wxw->process);
|
||||
@@ -159,9 +206,16 @@ spawn_xserver(void *user_data, const char *display, int abstract_fd, int unix_fd
|
||||
|
||||
case -1:
|
||||
weston_log("Failed to fork to spawn xserver process\n");
|
||||
fdstr_close_all(&wayland_socket);
|
||||
fdstr_close_all(&x11_wm_socket);
|
||||
fdstr_close_all(&display_pipe);
|
||||
break;
|
||||
}
|
||||
|
||||
custom_env_fini(&child_env);
|
||||
free(exec_failure_msg);
|
||||
free(xserver);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
@@ -170,22 +224,34 @@ xserver_cleanup(struct weston_process *process, int status)
|
||||
{
|
||||
struct wet_xwayland *wxw =
|
||||
container_of(process, struct wet_xwayland, process);
|
||||
struct wl_event_loop *loop =
|
||||
wl_display_get_event_loop(wxw->compositor->wl_display);
|
||||
|
||||
wxw->api->xserver_exited(wxw->xwayland, status);
|
||||
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
|
||||
handle_sigusr1, wxw);
|
||||
wxw->client = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wxw_compositor_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wet_xwayland *wxw =
|
||||
wl_container_of(listener, wxw, compositor_destroy_listener);
|
||||
|
||||
wl_list_remove(&wxw->compositor_destroy_listener.link);
|
||||
|
||||
/* Don't call xserver_exited because Xwayland's own destroy handler
|
||||
* already does this for us ... */
|
||||
if (wxw->client)
|
||||
kill(wxw->process.pid, SIGTERM);
|
||||
|
||||
wl_list_remove(&wxw->process.link);
|
||||
free(wxw);
|
||||
}
|
||||
|
||||
int
|
||||
wet_load_xwayland(struct weston_compositor *comp)
|
||||
{
|
||||
const struct weston_xwayland_api *api;
|
||||
struct weston_xwayland *xwayland;
|
||||
struct wet_xwayland *wxw;
|
||||
struct wl_event_loop *loop;
|
||||
|
||||
if (weston_compositor_load_xwayland(comp) < 0)
|
||||
return -1;
|
||||
@@ -209,13 +275,13 @@ wet_load_xwayland(struct weston_compositor *comp)
|
||||
wxw->compositor = comp;
|
||||
wxw->api = api;
|
||||
wxw->xwayland = xwayland;
|
||||
wl_list_init(&wxw->process.link);
|
||||
wxw->process.cleanup = xserver_cleanup;
|
||||
wxw->compositor_destroy_listener.notify = wxw_compositor_destroy;
|
||||
if (api->listen(xwayland, wxw, spawn_xserver) < 0)
|
||||
return -1;
|
||||
|
||||
loop = wl_display_get_event_loop(comp->wl_display);
|
||||
wxw->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1,
|
||||
handle_sigusr1, wxw);
|
||||
wl_signal_add(&comp->destroy_signal, &wxw->compositor_destroy_listener);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,737 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#include "shell.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
struct exposay_surface {
|
||||
struct desktop_shell *shell;
|
||||
struct exposay_output *eoutput;
|
||||
struct weston_surface *surface;
|
||||
struct weston_view *view;
|
||||
struct wl_listener view_destroy_listener;
|
||||
struct wl_list link;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
double scale;
|
||||
|
||||
int row;
|
||||
int column;
|
||||
|
||||
/* The animations only apply a transformation for their own lifetime,
|
||||
* and don't have an option to indefinitely maintain the
|
||||
* transformation in a steady state - so, we apply our own once the
|
||||
* animation has finished. */
|
||||
struct weston_transform transform;
|
||||
};
|
||||
|
||||
static void exposay_set_state(struct desktop_shell *shell,
|
||||
enum exposay_target_state state,
|
||||
struct weston_seat *seat);
|
||||
static void exposay_check_state(struct desktop_shell *shell);
|
||||
|
||||
static void
|
||||
exposay_surface_destroy(struct exposay_surface *esurface)
|
||||
{
|
||||
wl_list_remove(&esurface->link);
|
||||
wl_list_remove(&esurface->view_destroy_listener.link);
|
||||
|
||||
if (esurface->shell->exposay.focus_current == esurface->view)
|
||||
esurface->shell->exposay.focus_current = NULL;
|
||||
if (esurface->shell->exposay.focus_prev == esurface->view)
|
||||
esurface->shell->exposay.focus_prev = NULL;
|
||||
|
||||
free(esurface);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_in_flight_inc(struct desktop_shell *shell)
|
||||
{
|
||||
shell->exposay.in_flight++;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_in_flight_dec(struct desktop_shell *shell)
|
||||
{
|
||||
if (--shell->exposay.in_flight > 0)
|
||||
return;
|
||||
|
||||
exposay_check_state(shell);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_in_done(struct weston_view_animation *animation, void *data)
|
||||
{
|
||||
struct exposay_surface *esurface = data;
|
||||
|
||||
wl_list_insert(&esurface->view->geometry.transformation_list,
|
||||
&esurface->transform.link);
|
||||
weston_matrix_init(&esurface->transform.matrix);
|
||||
weston_matrix_scale(&esurface->transform.matrix,
|
||||
esurface->scale, esurface->scale, 1.0f);
|
||||
weston_matrix_translate(&esurface->transform.matrix,
|
||||
esurface->x - esurface->view->geometry.x,
|
||||
esurface->y - esurface->view->geometry.y,
|
||||
0);
|
||||
|
||||
weston_view_geometry_dirty(esurface->view);
|
||||
weston_compositor_schedule_repaint(esurface->view->surface->compositor);
|
||||
|
||||
exposay_in_flight_dec(esurface->shell);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_in(struct exposay_surface *esurface)
|
||||
{
|
||||
exposay_in_flight_inc(esurface->shell);
|
||||
|
||||
weston_move_scale_run(esurface->view,
|
||||
esurface->x - esurface->view->geometry.x,
|
||||
esurface->y - esurface->view->geometry.y,
|
||||
1.0, esurface->scale, 0,
|
||||
exposay_animate_in_done, esurface);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_out_done(struct weston_view_animation *animation, void *data)
|
||||
{
|
||||
struct exposay_surface *esurface = data;
|
||||
struct desktop_shell *shell = esurface->shell;
|
||||
|
||||
exposay_surface_destroy(esurface);
|
||||
|
||||
exposay_in_flight_dec(shell);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_animate_out(struct exposay_surface *esurface)
|
||||
{
|
||||
exposay_in_flight_inc(esurface->shell);
|
||||
|
||||
/* Remove the static transformation set up by
|
||||
* exposay_transform_in_done(). */
|
||||
wl_list_remove(&esurface->transform.link);
|
||||
weston_view_geometry_dirty(esurface->view);
|
||||
|
||||
weston_move_scale_run(esurface->view,
|
||||
esurface->x - esurface->view->geometry.x,
|
||||
esurface->y - esurface->view->geometry.y,
|
||||
1.0, esurface->scale, 1,
|
||||
exposay_animate_out_done, esurface);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_highlight_surface(struct desktop_shell *shell,
|
||||
struct exposay_surface *esurface)
|
||||
{
|
||||
struct weston_view *view = esurface->view;
|
||||
|
||||
if (shell->exposay.focus_current == view)
|
||||
return;
|
||||
|
||||
shell->exposay.row_current = esurface->row;
|
||||
shell->exposay.column_current = esurface->column;
|
||||
shell->exposay.cur_output = esurface->eoutput;
|
||||
|
||||
activate(shell, view, shell->exposay.seat,
|
||||
WESTON_ACTIVATE_FLAG_NONE);
|
||||
shell->exposay.focus_current = view;
|
||||
}
|
||||
|
||||
static int
|
||||
exposay_is_animating(struct desktop_shell *shell)
|
||||
{
|
||||
if (shell->exposay.state_cur == EXPOSAY_LAYOUT_INACTIVE ||
|
||||
shell->exposay.state_cur == EXPOSAY_LAYOUT_OVERVIEW)
|
||||
return 0;
|
||||
|
||||
return (shell->exposay.in_flight > 0);
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_pick(struct desktop_shell *shell, int x, int y)
|
||||
{
|
||||
struct exposay_surface *esurface;
|
||||
|
||||
if (exposay_is_animating(shell))
|
||||
return;
|
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
|
||||
if (x < esurface->x || x > esurface->x + esurface->width)
|
||||
continue;
|
||||
if (y < esurface->y || y > esurface->y + esurface->height)
|
||||
continue;
|
||||
|
||||
exposay_highlight_surface(shell, esurface);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_view_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct exposay_surface *esurface = container_of(listener,
|
||||
struct exposay_surface,
|
||||
view_destroy_listener);
|
||||
|
||||
exposay_surface_destroy(esurface);
|
||||
}
|
||||
|
||||
/* Compute each surface size and then inner pad (10% of surface size).
|
||||
* After that, it's necessary to recompute surface size (90% of its
|
||||
* original size). Also, each surface can't be bigger than half the
|
||||
* exposay area width and height.
|
||||
*/
|
||||
static void
|
||||
exposay_surface_and_inner_pad_size(pixman_rectangle32_t exposay_area, struct exposay_output *eoutput)
|
||||
{
|
||||
if (exposay_area.height < exposay_area.width)
|
||||
eoutput->surface_size = exposay_area.height / eoutput->grid_size;
|
||||
else
|
||||
eoutput->surface_size = exposay_area.width / eoutput->grid_size;
|
||||
|
||||
eoutput->padding_inner = eoutput->surface_size / 10;
|
||||
eoutput->surface_size -= eoutput->padding_inner;
|
||||
|
||||
if ((uint32_t)eoutput->surface_size > (exposay_area.width / 2))
|
||||
eoutput->surface_size = exposay_area.width / 2;
|
||||
if ((uint32_t)eoutput->surface_size > (exposay_area.height / 2))
|
||||
eoutput->surface_size = exposay_area.height / 2;
|
||||
}
|
||||
|
||||
/* Compute the exposay top/left margin in order to centralize it */
|
||||
static void
|
||||
exposay_margin_size(struct desktop_shell *shell, pixman_rectangle32_t exposay_area,
|
||||
int row_size, int column_size, int *left_margin, int *top_margin)
|
||||
{
|
||||
(*left_margin) = exposay_area.x + (exposay_area.width - row_size) / 2;
|
||||
(*top_margin) = exposay_area.y + (exposay_area.height - column_size) / 2;
|
||||
}
|
||||
|
||||
/* Pretty lame layout for now; just tries to make a square. Should take
|
||||
* aspect ratio into account really. Also needs to be notified of surface
|
||||
* addition and removal and adjust layout/animate accordingly.
|
||||
*
|
||||
* Lay the grid out as square as possible, losing surfaces from the
|
||||
* bottom row if required. Start with fixed padding of a 10% margin
|
||||
* around the outside, and maximise the area made available to surfaces
|
||||
* after this. Also, add an inner padding between surfaces that varies
|
||||
* with the surface size (10% of its size).
|
||||
*
|
||||
* If we can't make a square grid, add one extra row at the bottom which
|
||||
* will have a smaller number of columns.
|
||||
*/
|
||||
static enum exposay_layout_state
|
||||
exposay_layout(struct desktop_shell *shell, struct shell_output *shell_output)
|
||||
{
|
||||
struct workspace *workspace = shell->exposay.workspace;
|
||||
struct weston_output *output = shell_output->output;
|
||||
struct exposay_output *eoutput = &shell_output->eoutput;
|
||||
struct weston_view *view;
|
||||
struct exposay_surface *esurface, *highlight = NULL;
|
||||
pixman_rectangle32_t exposay_area;
|
||||
int pad, row_size, column_size, left_margin, top_margin;
|
||||
int last_row_size, last_row_margin_increase;
|
||||
int populated_rows;
|
||||
int i;
|
||||
|
||||
eoutput->num_surfaces = 0;
|
||||
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
|
||||
if (!get_shell_surface(view->surface))
|
||||
continue;
|
||||
if (view->output != output)
|
||||
continue;
|
||||
eoutput->num_surfaces++;
|
||||
}
|
||||
|
||||
if (eoutput->num_surfaces == 0) {
|
||||
eoutput->grid_size = 0;
|
||||
eoutput->padding_inner = 0;
|
||||
eoutput->surface_size = 0;
|
||||
return EXPOSAY_LAYOUT_OVERVIEW;
|
||||
}
|
||||
|
||||
/* Get exposay area and position, taking into account
|
||||
* the shell panel position and size */
|
||||
get_output_work_area(shell, output, &exposay_area);
|
||||
|
||||
/* Compute grid size */
|
||||
eoutput->grid_size = floor(sqrtf(eoutput->num_surfaces));
|
||||
if (pow(eoutput->grid_size, 2) != eoutput->num_surfaces)
|
||||
eoutput->grid_size++;
|
||||
|
||||
/* Compute each surface size and the inner padding between them */
|
||||
exposay_surface_and_inner_pad_size(exposay_area, eoutput);
|
||||
|
||||
/* Compute each row/column size */
|
||||
pad = eoutput->surface_size + eoutput->padding_inner;
|
||||
row_size = (pad * eoutput->grid_size) - eoutput->padding_inner;
|
||||
/* We may have empty rows that should be desconsidered to compute
|
||||
* column size */
|
||||
populated_rows = ceil(eoutput->num_surfaces / (float) eoutput->grid_size);
|
||||
column_size = (pad * populated_rows) - eoutput->padding_inner;
|
||||
|
||||
/* The last row size can be different, since it may have less surfaces
|
||||
* than the grid size. Also, its margin may be increased to centralize
|
||||
* its surfaces, in the case where we don't have a perfect grid. */
|
||||
last_row_size = ((eoutput->num_surfaces % eoutput->grid_size) * pad) - eoutput->padding_inner;
|
||||
if (eoutput->num_surfaces % eoutput->grid_size)
|
||||
last_row_margin_increase = (row_size - last_row_size) / 2;
|
||||
else
|
||||
last_row_margin_increase = 0;
|
||||
|
||||
/* Compute a top/left margin to centralize the exposay */
|
||||
exposay_margin_size(shell, exposay_area, row_size, column_size, &left_margin, &top_margin);
|
||||
|
||||
i = 0;
|
||||
wl_list_for_each(view, &workspace->layer.view_list.link, layer_link.link) {
|
||||
|
||||
if (!get_shell_surface(view->surface))
|
||||
continue;
|
||||
if (view->output != output)
|
||||
continue;
|
||||
|
||||
esurface = malloc(sizeof(*esurface));
|
||||
if (!esurface) {
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL,
|
||||
shell->exposay.seat);
|
||||
break;
|
||||
}
|
||||
|
||||
wl_list_insert(&shell->exposay.surface_list, &esurface->link);
|
||||
esurface->shell = shell;
|
||||
esurface->eoutput = eoutput;
|
||||
esurface->view = view;
|
||||
|
||||
esurface->row = i / eoutput->grid_size;
|
||||
esurface->column = i % eoutput->grid_size;
|
||||
|
||||
esurface->x = left_margin + (pad * esurface->column);
|
||||
esurface->y = top_margin + (pad * esurface->row);
|
||||
|
||||
/* If this is the last row, increase left margin (it sums 0 if
|
||||
* we have a perfect square) to centralize the surfaces */
|
||||
if (eoutput->num_surfaces / eoutput->grid_size == esurface->row)
|
||||
esurface->x += last_row_margin_increase;
|
||||
|
||||
if (view->surface->width > view->surface->height)
|
||||
esurface->scale = eoutput->surface_size / (float) view->surface->width;
|
||||
else
|
||||
esurface->scale = eoutput->surface_size / (float) view->surface->height;
|
||||
esurface->width = view->surface->width * esurface->scale;
|
||||
esurface->height = view->surface->height * esurface->scale;
|
||||
|
||||
/* Surfaces are usually rectangular, but their exposay surfaces
|
||||
* are square. centralize them in their own square */
|
||||
if (esurface->width > esurface->height)
|
||||
esurface->y += (esurface->width - esurface->height) / 2;
|
||||
else
|
||||
esurface->x += (esurface->height - esurface->width) / 2;
|
||||
|
||||
if (shell->exposay.focus_current == esurface->view)
|
||||
highlight = esurface;
|
||||
|
||||
exposay_animate_in(esurface);
|
||||
|
||||
/* We want our destroy handler to be after the animation
|
||||
* destroy handler in the list, this way when the view is
|
||||
* destroyed, the animation can safely call the animation
|
||||
* completion callback before we free the esurface in our
|
||||
* destroy handler.
|
||||
*/
|
||||
esurface->view_destroy_listener.notify = handle_view_destroy;
|
||||
wl_signal_add(&view->destroy_signal, &esurface->view_destroy_listener);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
shell->exposay.focus_current = NULL;
|
||||
exposay_highlight_surface(shell, highlight);
|
||||
}
|
||||
|
||||
weston_compositor_schedule_repaint(shell->compositor);
|
||||
|
||||
return EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_focus(struct weston_pointer_grab *grab)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_motion(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
struct weston_pointer_motion_event *event)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr);
|
||||
|
||||
weston_pointer_move(grab->pointer, event);
|
||||
|
||||
exposay_pick(shell,
|
||||
wl_fixed_to_int(grab->pointer->x),
|
||||
wl_fixed_to_int(grab->pointer->y));
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_button(struct weston_pointer_grab *grab, const struct timespec *time,
|
||||
uint32_t button, uint32_t state_w)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr);
|
||||
struct weston_seat *seat = grab->pointer->seat;
|
||||
enum wl_pointer_button_state state = state_w;
|
||||
|
||||
if (button != BTN_LEFT)
|
||||
return;
|
||||
|
||||
/* Store the surface we clicked on, and don't do anything if we end up
|
||||
* releasing on a different surface. */
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
shell->exposay.clicked = shell->exposay.focus_current;
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell->exposay.focus_current == shell->exposay.clicked)
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
|
||||
else
|
||||
shell->exposay.clicked = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_axis(struct weston_pointer_grab *grab,
|
||||
const struct timespec *time,
|
||||
struct weston_pointer_axis_event *event)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_axis_source(struct weston_pointer_grab *grab, uint32_t source)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_frame(struct weston_pointer_grab *grab)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_pointer_grab_cancel(struct weston_pointer_grab *grab)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_ptr);
|
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
|
||||
}
|
||||
|
||||
static const struct weston_pointer_grab_interface exposay_ptr_grab = {
|
||||
exposay_focus,
|
||||
exposay_motion,
|
||||
exposay_button,
|
||||
exposay_axis,
|
||||
exposay_axis_source,
|
||||
exposay_frame,
|
||||
exposay_pointer_grab_cancel,
|
||||
};
|
||||
|
||||
static int
|
||||
exposay_maybe_move(struct desktop_shell *shell, int row, int column)
|
||||
{
|
||||
struct exposay_surface *esurface;
|
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link) {
|
||||
if (esurface->eoutput != shell->exposay.cur_output ||
|
||||
esurface->row != row || esurface->column != column)
|
||||
continue;
|
||||
|
||||
exposay_highlight_surface(shell, esurface);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_key(struct weston_keyboard_grab *grab, const struct timespec *time,
|
||||
uint32_t key, uint32_t state_w)
|
||||
{
|
||||
struct weston_seat *seat = grab->keyboard->seat;
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd);
|
||||
enum wl_keyboard_key_state state = state_w;
|
||||
|
||||
if (state != WL_KEYBOARD_KEY_STATE_RELEASED)
|
||||
return;
|
||||
|
||||
switch (key) {
|
||||
case KEY_ESC:
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
|
||||
break;
|
||||
case KEY_ENTER:
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_SWITCH, seat);
|
||||
break;
|
||||
case KEY_UP:
|
||||
exposay_maybe_move(shell, shell->exposay.row_current - 1,
|
||||
shell->exposay.column_current);
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
/* Special case for trying to move to the bottom row when it
|
||||
* has fewer items than all the others. */
|
||||
if (!exposay_maybe_move(shell, shell->exposay.row_current + 1,
|
||||
shell->exposay.column_current) &&
|
||||
shell->exposay.row_current < (shell->exposay.cur_output->grid_size - 1)) {
|
||||
exposay_maybe_move(shell, shell->exposay.row_current + 1,
|
||||
(shell->exposay.cur_output->num_surfaces %
|
||||
shell->exposay.cur_output->grid_size) - 1);
|
||||
}
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
exposay_maybe_move(shell, shell->exposay.row_current,
|
||||
shell->exposay.column_current - 1);
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
exposay_maybe_move(shell, shell->exposay.row_current,
|
||||
shell->exposay.column_current + 1);
|
||||
break;
|
||||
case KEY_TAB:
|
||||
/* Try to move right, then down (and to the leftmost column),
|
||||
* then if all else fails, to the top left. */
|
||||
if (!exposay_maybe_move(shell, shell->exposay.row_current,
|
||||
shell->exposay.column_current + 1) &&
|
||||
!exposay_maybe_move(shell, shell->exposay.row_current + 1, 0))
|
||||
exposay_maybe_move(shell, 0, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_modifier(struct weston_keyboard_grab *grab, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd);
|
||||
struct weston_seat *seat = (struct weston_seat *) grab->keyboard->seat;
|
||||
|
||||
/* We want to know when mod has been pressed and released.
|
||||
* FIXME: There is a problem here: if mod is pressed, then a key
|
||||
* is pressed and released, then mod is released, we will treat that
|
||||
* as if only mod had been pressed and released. */
|
||||
if (seat->modifier_state) {
|
||||
if (seat->modifier_state == shell->binding_modifier) {
|
||||
shell->exposay.mod_pressed = true;
|
||||
} else {
|
||||
shell->exposay.mod_invalid = true;
|
||||
}
|
||||
} else {
|
||||
if (shell->exposay.mod_pressed && !shell->exposay.mod_invalid)
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, seat);
|
||||
|
||||
shell->exposay.mod_invalid = false;
|
||||
shell->exposay.mod_pressed = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_cancel(struct weston_keyboard_grab *grab)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(grab, struct desktop_shell, exposay.grab_kbd);
|
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_CANCEL, shell->exposay.seat);
|
||||
}
|
||||
|
||||
static const struct weston_keyboard_grab_interface exposay_kbd_grab = {
|
||||
exposay_key,
|
||||
exposay_modifier,
|
||||
exposay_cancel,
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the transition from overview -> inactive has completed.
|
||||
*/
|
||||
static enum exposay_layout_state
|
||||
exposay_set_inactive(struct desktop_shell *shell)
|
||||
{
|
||||
struct weston_seat *seat = shell->exposay.seat;
|
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
||||
|
||||
if (pointer)
|
||||
weston_pointer_end_grab(pointer);
|
||||
|
||||
if (keyboard) {
|
||||
weston_keyboard_end_grab(keyboard);
|
||||
if (keyboard->input_method_resource)
|
||||
keyboard->grab = &keyboard->input_method_grab;
|
||||
}
|
||||
|
||||
return EXPOSAY_LAYOUT_INACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begins the transition from overview to inactive. */
|
||||
static enum exposay_layout_state
|
||||
exposay_transition_inactive(struct desktop_shell *shell, int switch_focus)
|
||||
{
|
||||
struct exposay_surface *esurface;
|
||||
|
||||
/* Call activate() before we start the animations to avoid
|
||||
* animating back the old state and then immediately transitioning
|
||||
* to the new. */
|
||||
if (switch_focus && shell->exposay.focus_current)
|
||||
activate(shell, shell->exposay.focus_current,
|
||||
shell->exposay.seat,
|
||||
WESTON_ACTIVATE_FLAG_CONFIGURE);
|
||||
else if (shell->exposay.focus_prev)
|
||||
activate(shell, shell->exposay.focus_prev,
|
||||
shell->exposay.seat,
|
||||
WESTON_ACTIVATE_FLAG_CONFIGURE);
|
||||
|
||||
wl_list_for_each(esurface, &shell->exposay.surface_list, link)
|
||||
exposay_animate_out(esurface);
|
||||
weston_compositor_schedule_repaint(shell->compositor);
|
||||
|
||||
return EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE;
|
||||
}
|
||||
|
||||
static enum exposay_layout_state
|
||||
exposay_transition_active(struct desktop_shell *shell)
|
||||
{
|
||||
struct weston_seat *seat = shell->exposay.seat;
|
||||
struct weston_pointer *pointer = weston_seat_get_pointer(seat);
|
||||
struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
|
||||
struct shell_output *shell_output;
|
||||
bool animate = false;
|
||||
|
||||
shell->exposay.workspace = get_current_workspace(shell);
|
||||
shell->exposay.focus_prev = get_default_view(keyboard->focus);
|
||||
shell->exposay.focus_current = get_default_view(keyboard->focus);
|
||||
shell->exposay.clicked = NULL;
|
||||
wl_list_init(&shell->exposay.surface_list);
|
||||
|
||||
lower_fullscreen_layer(shell, NULL);
|
||||
shell->exposay.grab_kbd.interface = &exposay_kbd_grab;
|
||||
weston_keyboard_start_grab(keyboard,
|
||||
&shell->exposay.grab_kbd);
|
||||
weston_keyboard_set_focus(keyboard, NULL);
|
||||
|
||||
shell->exposay.grab_ptr.interface = &exposay_ptr_grab;
|
||||
if (pointer) {
|
||||
weston_pointer_start_grab(pointer,
|
||||
&shell->exposay.grab_ptr);
|
||||
weston_pointer_clear_focus(pointer);
|
||||
}
|
||||
wl_list_for_each(shell_output, &shell->output_list, link) {
|
||||
enum exposay_layout_state state;
|
||||
|
||||
state = exposay_layout(shell, shell_output);
|
||||
|
||||
if (state == EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW)
|
||||
animate = true;
|
||||
}
|
||||
|
||||
return animate ? EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW
|
||||
: EXPOSAY_LAYOUT_OVERVIEW;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_check_state(struct desktop_shell *shell)
|
||||
{
|
||||
enum exposay_layout_state state_new = shell->exposay.state_cur;
|
||||
int do_switch = 0;
|
||||
|
||||
/* Don't do anything whilst animations are running, just store up
|
||||
* target state changes and only act on them when the animations have
|
||||
* completed. */
|
||||
if (exposay_is_animating(shell))
|
||||
return;
|
||||
|
||||
switch (shell->exposay.state_target) {
|
||||
case EXPOSAY_TARGET_OVERVIEW:
|
||||
switch (shell->exposay.state_cur) {
|
||||
case EXPOSAY_LAYOUT_OVERVIEW:
|
||||
goto out;
|
||||
case EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW:
|
||||
state_new = EXPOSAY_LAYOUT_OVERVIEW;
|
||||
break;
|
||||
default:
|
||||
state_new = exposay_transition_active(shell);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EXPOSAY_TARGET_SWITCH:
|
||||
do_switch = 1; /* fallthrough */
|
||||
case EXPOSAY_TARGET_CANCEL:
|
||||
switch (shell->exposay.state_cur) {
|
||||
case EXPOSAY_LAYOUT_INACTIVE:
|
||||
goto out;
|
||||
case EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE:
|
||||
state_new = exposay_set_inactive(shell);
|
||||
break;
|
||||
default:
|
||||
state_new = exposay_transition_inactive(shell, do_switch);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
shell->exposay.state_cur = state_new;
|
||||
}
|
||||
|
||||
static void
|
||||
exposay_set_state(struct desktop_shell *shell, enum exposay_target_state state,
|
||||
struct weston_seat *seat)
|
||||
{
|
||||
shell->exposay.state_target = state;
|
||||
shell->exposay.seat = seat;
|
||||
exposay_check_state(shell);
|
||||
}
|
||||
|
||||
void
|
||||
exposay_binding(struct weston_keyboard *keyboard, enum weston_keyboard_modifier modifier,
|
||||
void *data)
|
||||
{
|
||||
struct desktop_shell *shell = data;
|
||||
|
||||
exposay_set_state(shell, EXPOSAY_TARGET_OVERVIEW, keyboard->seat);
|
||||
}
|
||||
@@ -103,8 +103,8 @@ show_input_panel_surface(struct input_panel_surface *ipsurf)
|
||||
&ipsurf->view->layer_link);
|
||||
weston_view_geometry_dirty(ipsurf->view);
|
||||
weston_view_update_transform(ipsurf->view);
|
||||
ipsurf->surface->is_mapped = true;
|
||||
ipsurf->view->is_mapped = true;
|
||||
weston_surface_map(ipsurf->surface);
|
||||
weston_surface_damage(ipsurf->surface);
|
||||
|
||||
if (ipsurf->anim)
|
||||
|
||||
@@ -3,9 +3,7 @@ if get_option('shell-desktop')
|
||||
|
||||
srcs_shell_desktop = [
|
||||
'shell.c',
|
||||
'exposay.c',
|
||||
'input-panel.c',
|
||||
'../shared/shell-utils.c',
|
||||
weston_desktop_shell_server_protocol_h,
|
||||
weston_desktop_shell_protocol_c,
|
||||
input_method_unstable_v1_server_protocol_h,
|
||||
@@ -15,8 +13,8 @@ if get_option('shell-desktop')
|
||||
dep_libm,
|
||||
dep_libexec_weston,
|
||||
dep_libshared,
|
||||
dep_lib_desktop,
|
||||
dep_libweston_public,
|
||||
dep_shell_utils,
|
||||
]
|
||||
plugin_shell_desktop = shared_library(
|
||||
'desktop-shell',
|
||||
|
||||
+337
-858
File diff suppressed because it is too large
Load Diff
+3
-72
@@ -45,55 +45,8 @@ enum fade_type {
|
||||
FADE_OUT
|
||||
};
|
||||
|
||||
enum exposay_target_state {
|
||||
EXPOSAY_TARGET_OVERVIEW, /* show all windows */
|
||||
EXPOSAY_TARGET_CANCEL, /* return to normal, same focus */
|
||||
EXPOSAY_TARGET_SWITCH, /* return to normal, switch focus */
|
||||
};
|
||||
|
||||
enum exposay_layout_state {
|
||||
EXPOSAY_LAYOUT_INACTIVE = 0, /* normal desktop */
|
||||
EXPOSAY_LAYOUT_ANIMATE_TO_INACTIVE, /* in transition to normal */
|
||||
EXPOSAY_LAYOUT_OVERVIEW, /* show all windows */
|
||||
EXPOSAY_LAYOUT_ANIMATE_TO_OVERVIEW, /* in transition to all windows */
|
||||
};
|
||||
|
||||
struct exposay_output {
|
||||
int num_surfaces;
|
||||
int grid_size;
|
||||
int surface_size;
|
||||
int padding_inner;
|
||||
};
|
||||
|
||||
struct exposay {
|
||||
/* XXX: Make these exposay_surfaces. */
|
||||
struct weston_view *focus_prev;
|
||||
struct weston_view *focus_current;
|
||||
struct weston_view *clicked;
|
||||
struct workspace *workspace;
|
||||
struct weston_seat *seat;
|
||||
|
||||
struct wl_list surface_list;
|
||||
|
||||
struct weston_keyboard_grab grab_kbd;
|
||||
struct weston_pointer_grab grab_ptr;
|
||||
|
||||
enum exposay_target_state state_target;
|
||||
enum exposay_layout_state state_cur;
|
||||
int in_flight; /* number of animations still running */
|
||||
|
||||
int row_current;
|
||||
int column_current;
|
||||
struct exposay_output *cur_output;
|
||||
|
||||
bool mod_pressed;
|
||||
bool mod_invalid;
|
||||
};
|
||||
|
||||
struct focus_surface {
|
||||
struct weston_surface *surface;
|
||||
struct weston_view *view;
|
||||
struct weston_transform workspace_transform;
|
||||
struct weston_curtain *curtain;
|
||||
};
|
||||
|
||||
struct workspace {
|
||||
@@ -110,7 +63,6 @@ struct workspace {
|
||||
struct shell_output {
|
||||
struct desktop_shell *shell;
|
||||
struct weston_output *output;
|
||||
struct exposay_output eoutput;
|
||||
struct wl_listener destroy_listener;
|
||||
struct wl_list link;
|
||||
|
||||
@@ -121,7 +73,7 @@ struct shell_output {
|
||||
struct wl_listener background_surface_listener;
|
||||
|
||||
struct {
|
||||
struct weston_view *view;
|
||||
struct weston_curtain *curtain;
|
||||
struct weston_view_animation *animation;
|
||||
enum fade_type type;
|
||||
struct wl_event_source *startup_timer;
|
||||
@@ -175,32 +127,15 @@ struct desktop_shell {
|
||||
struct weston_surface *lock_surface;
|
||||
struct wl_listener lock_surface_listener;
|
||||
|
||||
struct {
|
||||
struct wl_array array;
|
||||
unsigned int current;
|
||||
unsigned int num;
|
||||
|
||||
struct wl_list client_list;
|
||||
|
||||
struct weston_animation animation;
|
||||
struct wl_list anim_sticky_list;
|
||||
int anim_dir;
|
||||
struct timespec anim_timestamp;
|
||||
double anim_current;
|
||||
struct workspace *anim_from;
|
||||
struct workspace *anim_to;
|
||||
} workspaces;
|
||||
struct workspace workspace;
|
||||
|
||||
struct {
|
||||
struct wl_resource *binding;
|
||||
struct wl_list surfaces;
|
||||
} input_panel;
|
||||
|
||||
struct exposay exposay;
|
||||
|
||||
bool allow_zap;
|
||||
uint32_t binding_modifier;
|
||||
uint32_t exposay_modifier;
|
||||
enum animation_type win_animation_type;
|
||||
enum animation_type win_close_animation_type;
|
||||
enum animation_type startup_animation_type;
|
||||
@@ -246,10 +181,6 @@ void
|
||||
activate(struct desktop_shell *shell, struct weston_view *view,
|
||||
struct weston_seat *seat, uint32_t flags);
|
||||
|
||||
void
|
||||
exposay_binding(struct weston_keyboard *keyboard,
|
||||
enum weston_keyboard_modifier modifier,
|
||||
void *data);
|
||||
int
|
||||
input_panel_setup(struct desktop_shell *shell);
|
||||
void
|
||||
|
||||
+3
-296
@@ -759,7 +759,7 @@ WARN_NO_PARAMDOC = NO
|
||||
# a warning is encountered.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = YES
|
||||
WARN_AS_ERROR = @MESON_WERROR@
|
||||
|
||||
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
|
||||
# can produce. The string should contain the $file, $line, and $text tags, which
|
||||
@@ -1486,17 +1486,6 @@ EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
# Note that when changing this option you need to delete any form_*.png files in
|
||||
# the HTML output directory before the changes have effect.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# http://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
@@ -1632,241 +1621,6 @@ EXTERNAL_SEARCH_ID =
|
||||
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_LATEX = NO
|
||||
|
||||
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
# it.
|
||||
# The default directory is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
#
|
||||
# Note that when enabling USE_PDFLATEX this option is only used for generating
|
||||
# bitmaps for formulas in the HTML output, but not in the Makefile that is
|
||||
# written to the output directory.
|
||||
# The default file is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_CMD_NAME = latex
|
||||
|
||||
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
|
||||
# index for LaTeX.
|
||||
# The default file is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
|
||||
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
|
||||
# documents. This may be useful for small projects and may help to save some
|
||||
# trees in general.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
COMPACT_LATEX = NO
|
||||
|
||||
# The PAPER_TYPE tag can be used to set the paper type that is used by the
|
||||
# printer.
|
||||
# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
|
||||
# 14 inches) and executive (7.25 x 10.5 inches).
|
||||
# The default value is: a4.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PAPER_TYPE = a4
|
||||
|
||||
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
|
||||
# that should be included in the LaTeX output. The package can be specified just
|
||||
# by its name or with the correct syntax as to be used with the LaTeX
|
||||
# \usepackage command. To get the times font for instance you can specify :
|
||||
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
|
||||
# To use the option intlimits with the amsmath package you can specify:
|
||||
# EXTRA_PACKAGES=[intlimits]{amsmath}
|
||||
# If left blank no extra packages will be included.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
# chapter. If it is left blank doxygen will generate a standard header. See
|
||||
# section "Doxygen usage" for information on how to let doxygen write the
|
||||
# default header to a separate file.
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
|
||||
# string, for the replacement values of the other commands the user is referred
|
||||
# to HTML_HEADER.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
|
||||
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# LaTeX style sheets that are included after the standard style sheets created
|
||||
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
|
||||
# will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||
# style sheet in the list overrules the setting of the previous ones in the
|
||||
# list).
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_STYLESHEET =
|
||||
|
||||
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the LATEX_OUTPUT output
|
||||
# directory. Note that the files will be copied as-is; there are no commands or
|
||||
# markers available.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_FILES =
|
||||
|
||||
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
|
||||
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
|
||||
# contain links (just like the HTML output) instead of page references. This
|
||||
# makes the output suitable for online browsing using a PDF viewer.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES, to get a
|
||||
# higher quality PDF documentation.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help. This option is also used
|
||||
# when generating formulas in HTML.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_BATCHMODE = NO
|
||||
|
||||
# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
|
||||
# index chapters (such as File Index, Compound Index, etc.) in the output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HIDE_INDICES = NO
|
||||
|
||||
# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
|
||||
# code with syntax highlighting in the LaTeX output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_SOURCE_CODE = NO
|
||||
|
||||
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
|
||||
# bibliography, e.g. plainnat, or ieeetr. See
|
||||
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
|
||||
# The default value is: plain.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
|
||||
# RTF output is optimized for Word 97 and may not look too pretty with other RTF
|
||||
# readers/editors.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_RTF = NO
|
||||
|
||||
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
# it.
|
||||
# The default directory is: rtf.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_OUTPUT = rtf
|
||||
|
||||
# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
|
||||
# documents. This may be useful for small projects and may help to save some
|
||||
# trees in general.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
COMPACT_RTF = NO
|
||||
|
||||
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
|
||||
# contain hyperlink fields. The RTF file will contain links (just like the HTML
|
||||
# output) instead of page references. This makes the output suitable for online
|
||||
# browsing using Word or some other Word compatible readers that support those
|
||||
# fields.
|
||||
#
|
||||
# Note: WordPad (write) and others do not support links.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_HYPERLINKS = NO
|
||||
|
||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
|
||||
# file, i.e. a series of assignments. You only have to provide replacements,
|
||||
# missing definitions are set to their default value.
|
||||
#
|
||||
# See also section "Doxygen usage" for information on how to generate the
|
||||
# default style sheet that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
|
||||
# with syntax highlighting in the RTF output.
|
||||
#
|
||||
# Note that which sources are shown also depends on other settings such as
|
||||
# SOURCE_BROWSER.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_SOURCE_CODE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1956,15 +1710,6 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -2143,15 +1888,6 @@ EXTERNAL_PAGES = YES
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
|
||||
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
|
||||
# NO turns the diagrams off. Note that this option also works with HAVE_DOT
|
||||
# disabled, but it is recommended to install and use dot, since it yields more
|
||||
# powerful graphs.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
@@ -2172,7 +1908,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
# set to NO
|
||||
# The default value is: YES.
|
||||
|
||||
HAVE_DOT = NO
|
||||
HAVE_DOT = YES
|
||||
|
||||
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
|
||||
# to run in parallel. When set to 0 doxygen will base this on the number of
|
||||
@@ -2184,23 +1920,6 @@ HAVE_DOT = NO
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
# setting DOT_FONTPATH to the directory containing the font.
|
||||
# The default value is: Helvetica.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTNAME = Helvetica
|
||||
|
||||
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||
# dot graphs.
|
||||
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTSIZE = 10
|
||||
|
||||
# By default doxygen will tell dot to use the default font as specified with
|
||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||
# the path where dot can find it using this tag.
|
||||
@@ -2401,7 +2120,7 @@ PLANTUML_INCLUDE_PATH =
|
||||
# Minimum value: 0, maximum value: 10000, default value: 50.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_GRAPH_MAX_NODES = 50
|
||||
DOT_GRAPH_MAX_NODES = 250
|
||||
|
||||
# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
|
||||
# generated by dot. A depth value of 3 means that only nodes reachable from the
|
||||
@@ -2415,18 +2134,6 @@ DOT_GRAPH_MAX_NODES = 50
|
||||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, because dot on Windows does not seem
|
||||
# to support this out of the box.
|
||||
#
|
||||
# Warning: Depending on the platform used, enabling this option may lead to
|
||||
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||
# read).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_TRANSPARENT = YES
|
||||
|
||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||
|
||||
@@ -9,6 +9,7 @@ Welcome to Weston documentation!
|
||||
toc/libweston.rst
|
||||
toc/test-suite.rst
|
||||
toc/kiosk-shell.rst
|
||||
toc/ivi-shell.rst
|
||||
|
||||
Weston
|
||||
------
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
sphinx = find_program('sphinx-build', required: true)
|
||||
doxygen = find_program('doxygen', required: true)
|
||||
dot = find_program('dot', required: true)
|
||||
breathe = find_program('breathe-apidoc', required: true)
|
||||
|
||||
sphinx_c = run_command(sphinx, '--version')
|
||||
@@ -38,6 +39,7 @@ sphinx_conf = configure_file(
|
||||
doxy_conf_data = configuration_data()
|
||||
doxy_conf_data.set('SRC_ROOT', meson.source_root())
|
||||
doxy_conf_data.set('OUTPUT_DIR', doxygen_database)
|
||||
doxy_conf_data.set('MESON_WERROR', get_option('werror') == true ? 'YES' : 'NO')
|
||||
doxygen_conf_weston = configure_file(
|
||||
input: 'doxygen.ini.in',
|
||||
output: 'doxygen.ini',
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
@@ -0,0 +1,9 @@
|
||||
# Sphinx does not know look for these files in the source directory, so
|
||||
# they must be copied to the build directory.
|
||||
files = [
|
||||
'ivi-shell.png',
|
||||
]
|
||||
|
||||
foreach file : files
|
||||
configure_file(input: file, output: file, copy: true)
|
||||
endforeach
|
||||
@@ -0,0 +1,111 @@
|
||||
Weston IVI-shell
|
||||
================
|
||||
|
||||
Weston's IVI-shell is a highly customizable shell targeted at use cases which
|
||||
need custom control over the shell's window layout with one or more applications
|
||||
without interactive configuration of the layout by the user.
|
||||
|
||||
Example use cases for the IVI-shell are IVI applications or industrial human
|
||||
machine interfaces. In general, whenever the user interface requires the exact
|
||||
positioning of multiple application surfaces on one or more screens.
|
||||
|
||||
The IVI-shell also provides a means for applications to identify themselves to
|
||||
the shell by application IDs via the ivi_application Wayland protocol.
|
||||
|
||||
IVI-shell client protocol
|
||||
-------------------------
|
||||
|
||||
Wayland clients can implement the ``ivi_application`` Wayland protocol, which
|
||||
allows them to specify an ``ivi_id`` to allow the IVI controller to identify the
|
||||
application. This allows the controller to implement special behavior for
|
||||
well-known applications.
|
||||
|
||||
The IVI-shell is able to also handle clients that use the ``xdg-shell``
|
||||
protocol, but in these cases the IVI-shell needs other means to identify client
|
||||
applications.
|
||||
|
||||
See ``ivi-application.xml`` for the protocol specification.
|
||||
|
||||
IVI-shell Weston modules
|
||||
------------------------
|
||||
|
||||
The IVI-shell consists of two main components: The ``ivi-shell.so`` and custom
|
||||
IVI controller (with the ``hmi-controller.so`` example implementation).
|
||||
|
||||
The ``ivi-shell.so`` is responsible for handling the application IDs and for
|
||||
providing abstractions to configure the window layout via the
|
||||
``ivi_layout_interface``. This interface is discussed in `IVI-shell compositor
|
||||
implementation`.
|
||||
|
||||
The IVI controller uses the ``ivi_layout_interface`` to implement a window
|
||||
manager and is responsible for configuring the window layout, i.e. the position
|
||||
of the applications on the screens.
|
||||
|
||||
Due to this separation, both modules must be loaded in your ``weston.ini`` to
|
||||
use the IVI-shell.
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[core]
|
||||
shell=ivi-shell.so
|
||||
modules=hmi-controller.so
|
||||
|
||||
If you are using your custom controller, replace ``hmi-controller.so`` with the
|
||||
name of your own controller module.
|
||||
|
||||
.. figure:: images/ivi-shell.png
|
||||
:alt: IVI-shell architecture overview
|
||||
|
||||
Controlling the IVI-shell
|
||||
-------------------------
|
||||
|
||||
The IVI-shell provides the ``ivi_layout_interface`` API that a controller must
|
||||
use to control the window layout of the IVI-shell. See
|
||||
``ivi-shell/ivi-layout-export.h`` for the definition of this API.
|
||||
|
||||
For the initial configuration, the controller has to create at least one
|
||||
``ivi_layout_layer`` and add the ``ivi_layout_layer`` to a ``weston_output``.
|
||||
The layers allow to group multiple applications surfaces and control them
|
||||
together and are the main mechanism to group and organize surfaces. These are
|
||||
always necessary to show something using the IVI-shell. The IVI-shell will
|
||||
internally create an ``ivi_layout_screen``, but a controller always uses the
|
||||
``weston_output`` directly.
|
||||
|
||||
To get control over the client surfaces, the controller must use notifiers that
|
||||
trigger whenever there are changes to the client surfaces. The client surfaces
|
||||
then show up as ``ivi_layout_surface``. These have an ID, which allows the
|
||||
controller to identify the surface and reconfigure the window layout
|
||||
accordingly.
|
||||
|
||||
The controller must add the ``ivi_layout_surface`` to an ``ivi_layout_layer``
|
||||
and configure it's position and z-order wrt. the other surfaces in the layer.
|
||||
Otherwise, the newly added surface will not show up on the screen.
|
||||
|
||||
The IVI-shell will internally create an ``ivi_layout_view`` for each layer that
|
||||
the surface was added to. However, the views are not provided to the IVI
|
||||
controller.
|
||||
|
||||
After configuring all expected changes, the controller must call the
|
||||
``commit_changes`` to atomically update the display layout.
|
||||
|
||||
IVI-shell example implementation
|
||||
--------------------------------
|
||||
|
||||
The IVI-shell comes with an example implementation of an IVI controller -- the
|
||||
`hmi-controller`. The hmi-controller will usually replaced by a custom
|
||||
implementation that implements the use-case-specific behavior.
|
||||
|
||||
The hmi-controller is split into two parts:
|
||||
|
||||
The ``hmi-controller.so`` is a Weston Plugin that uses the
|
||||
``ivi_layout_interface`` to perform the window manager tasks. It allows some
|
||||
reconfiguration of the window layout via the ``ivi_hmi_controller`` protocol.
|
||||
Other implementations may keep all window management inside the module or may
|
||||
expose even more window management via a custom protocol to an external process.
|
||||
|
||||
The ``weston-ivi-shell-user-interface`` is an example hmi-controller helper
|
||||
client that serves as a user interface for controlling the hmi-controller.
|
||||
|
||||
The hmi-controller can be customized using the ``[ivi-shell]`` section in the
|
||||
``weston.ini``. An example configuration will be generated in
|
||||
``<build_dir>/ivi-shell/weston.ini``.
|
||||
@@ -11,7 +11,7 @@ msc {
|
||||
--- [label = "Compositor creates an output for a head"];
|
||||
|
||||
c box c [label = "Have an existing head to process."];
|
||||
c => w [label = "weston_compositor_create_output_with_head()"];
|
||||
c => w [label = "weston_compositor_create_output()"];
|
||||
w => b [label = "weston_backend::create_output()"];
|
||||
w << b [label = "an empty output, no hw resources"];
|
||||
w => b [label = "weston_output::attach_head()"];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# you need to add here any files you add to the toc directory as well
|
||||
files = [
|
||||
'kiosk-shell.rst',
|
||||
'ivi-shell.rst',
|
||||
'running-weston.rst',
|
||||
'libweston.rst',
|
||||
'test-suite.rst',
|
||||
@@ -11,4 +12,5 @@ foreach file : files
|
||||
configure_file(input: file, output: file, copy: true)
|
||||
endforeach
|
||||
|
||||
subdir('images')
|
||||
subdir('libweston')
|
||||
|
||||
@@ -6,7 +6,7 @@ underlying environment where it runs on. Ultimately, the back-end is
|
||||
responsible for handling the input and generate an output. Weston, as a
|
||||
libweston user, can be run on different back-ends, including nested, by using
|
||||
the wayland backend, but also on X11 or on a stand-alone back-end like
|
||||
DRM/KMS and now deprecated fbdev.
|
||||
DRM/KMS.
|
||||
|
||||
In most cases, people should allow Weston to choose the backend automatically
|
||||
as it will produce the best results. That happens for instance when running
|
||||
@@ -28,7 +28,6 @@ Available back-ends:
|
||||
* **x11** -- run as a x11 application, nested in a X11 display server instance
|
||||
* **rdp** -- run as an RDP server without local input or output
|
||||
* **headless** -- run without input or output, useful for test suite
|
||||
* **fbdev** -- run stand-alone on fbdev/evdev (deprecated)
|
||||
|
||||
The job of gathering all the surfaces (windows) being displayed on an output and
|
||||
stitching them together is performed by a *renderer*. By doing so, it is
|
||||
@@ -91,17 +90,20 @@ You can start Weston from a VT assuming that there's a seat manager supported by
|
||||
backend to be used by ``libseat`` can optionally be selected with
|
||||
``$LIBSEAT_BACKEND``. If ``libseat`` and ``seatd`` are both installed, but
|
||||
``seatd`` is not already running, it can be started with ``sudo -- seatd -g
|
||||
video``. If no seat manager supported by ``libseat`` is available, you can use
|
||||
the ``weston-launch`` application that can handle VT switching.
|
||||
video``.
|
||||
|
||||
Another way of launching Weston is via ssh or a serial terminal. The simplest
|
||||
option here is to use the ``libseat`` launcher with ``seatd``. The process for
|
||||
Launching Weston via ssh or a serial terminal is best with the ``libseat``
|
||||
launcher and ``seatd``. Logind will refuse to give access to local seats from
|
||||
remote connections directly. The process for
|
||||
setting that up is identical to the one described above, where one just need to
|
||||
ensure that ``seatd`` is running with the appropriate arguments, after which one
|
||||
can just run ``weston``. Another option, is to rely on logind and start weston
|
||||
as systemd user service: :ref:`weston-user-service`. Alternatively and as a last
|
||||
resort, one can run Weston as root, specifying the tty to use on the command
|
||||
line: If TTY 2 is active, one would run ``weston --tty 2`` as root.
|
||||
can just run ``weston``. ``seatd`` will lend out the current VT, and if you want
|
||||
to run on a different VT you need to ``chvt`` first. Make sure nothing will try
|
||||
to take over the seat or VT via logind at the same time in case logind is
|
||||
running.
|
||||
|
||||
If you want to rely on logind, you can start weston as a systemd user service:
|
||||
:ref:`weston-user-service`.
|
||||
|
||||
Running Weston on a different seat on a stand-alone back-end
|
||||
------------------------------------------------------------
|
||||
@@ -171,7 +173,14 @@ Then, weston can be run by selecting the DRM-backend and the seat ``seat-insecur
|
||||
|
||||
::
|
||||
|
||||
./weston -Bdrm-backend.so --seat=seat-insecure
|
||||
SEATD_VTBOUND=0 ./weston -Bdrm-backend.so --seat=seat-insecure
|
||||
|
||||
This assumes you are using the libseat launcher of Weston with the "builtin"
|
||||
backend of libseat. Libseat automatically falls back to the builtin backend if
|
||||
``seatd`` is not running and a ``logind`` service is not running or refuses.
|
||||
You can also force it with ``LIBSEAT_BACKEND=builtin`` if needed.
|
||||
``SEATD_VTBOUND=0`` tells libseat that there is no VT associated with the
|
||||
chosen seat.
|
||||
|
||||
If everything went well you should see weston be up-and-running on an output
|
||||
connected to that DRM device.
|
||||
|
||||
@@ -218,10 +218,19 @@ DRM-backend tests
|
||||
|
||||
DRM-backend tests require a DRM device, so they are a special case. To select a
|
||||
device the test suite will simply look at the environment variable
|
||||
``WESTON_TEST_SUITE_DRM_DEVICE``. So the first thing the user has to do in order
|
||||
to run DRM-backend tests is to set this environment variable with the card that
|
||||
should run the tests. For instance, in order to run DRM-backend tests with
|
||||
``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``.
|
||||
``WESTON_TEST_SUITE_DRM_DEVICE``. In Weston's CI, we set this variable to the
|
||||
DRM node that VKMS takes (``cardX`` - X can change across each bot, as the order
|
||||
in which devices are loaded is not predictable).
|
||||
|
||||
**IMPORTANT**: our DRM-backend tests are written specifically to run on top of
|
||||
VKMS (KMS driver created to be used by headless machines in test suites, so it
|
||||
aims to be more configurable and predictable than real hardware). We don't
|
||||
guarantee that these tests will work on real hardware.
|
||||
|
||||
But if users want to run DRM-backend tests using real hardware anyway, the first
|
||||
thing they need to do is to set this environment variable with the DRM node of
|
||||
the card that should run the tests. For instance, in order to run DRM-backend
|
||||
tests with ``card0`` we need to run ``export WESTON_TEST_SUITE_DRM_DEVICE=card0``.
|
||||
|
||||
Note that the card should not be in use by a desktop environment (or any other
|
||||
program that requires master status), as there can only be one user at a time
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "compositor/weston.h"
|
||||
#include "fullscreen-shell-unstable-v1-server-protocol.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shell-utils.h"
|
||||
|
||||
struct fullscreen_shell {
|
||||
struct wl_client *client;
|
||||
@@ -80,7 +81,7 @@ struct fs_output {
|
||||
struct weston_surface *surface;
|
||||
struct wl_listener surface_destroyed;
|
||||
struct weston_view *view;
|
||||
struct weston_view *black_view;
|
||||
struct weston_curtain *curtain;
|
||||
struct weston_transform transform; /* matrix from x, y */
|
||||
|
||||
int presented_for_mode;
|
||||
@@ -226,37 +227,27 @@ black_surface_committed(struct weston_surface *es, int32_t sx, int32_t sy)
|
||||
{
|
||||
}
|
||||
|
||||
static struct weston_view *
|
||||
create_black_surface(struct weston_compositor *ec, struct fs_output *fsout,
|
||||
static struct weston_curtain *
|
||||
create_curtain(struct weston_compositor *ec, struct fs_output *fsout,
|
||||
float x, float y, int w, int h)
|
||||
{
|
||||
struct weston_surface *surface = NULL;
|
||||
struct weston_view *view;
|
||||
struct weston_curtain_params curtain_params = {
|
||||
.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0,
|
||||
.x = x, .y = y, .width = w, .height = h,
|
||||
.surface_committed = black_surface_committed,
|
||||
.get_label = NULL,
|
||||
.surface_private = fsout,
|
||||
.capture_input = true,
|
||||
};
|
||||
struct weston_curtain *curtain;
|
||||
|
||||
surface = weston_surface_create(ec);
|
||||
if (surface == NULL) {
|
||||
weston_log("no memory\n");
|
||||
return NULL;
|
||||
}
|
||||
view = weston_view_create(surface);
|
||||
if (!view) {
|
||||
weston_surface_destroy(surface);
|
||||
curtain = weston_curtain_create(ec, &curtain_params);
|
||||
if (!curtain) {
|
||||
weston_log("no memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
surface->committed = black_surface_committed;
|
||||
surface->committed_private = fsout;
|
||||
weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
|
||||
pixman_region32_fini(&surface->opaque);
|
||||
pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
|
||||
pixman_region32_fini(&surface->input);
|
||||
pixman_region32_init_rect(&surface->input, 0, 0, w, h);
|
||||
|
||||
weston_surface_set_size(surface, w, h);
|
||||
weston_view_set_position(view, x, y);
|
||||
|
||||
return view;
|
||||
return curtain;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -333,13 +324,12 @@ fs_output_create(struct fullscreen_shell *shell, struct weston_output *output)
|
||||
|
||||
fsout->surface_destroyed.notify = surface_destroyed;
|
||||
fsout->pending.surface_destroyed.notify = pending_surface_destroyed;
|
||||
fsout->black_view = create_black_surface(shell->compositor, fsout,
|
||||
fsout->curtain = create_curtain(shell->compositor, fsout,
|
||||
output->x, output->y,
|
||||
output->width, output->height);
|
||||
fsout->black_view->surface->is_mapped = true;
|
||||
fsout->black_view->is_mapped = true;
|
||||
fsout->curtain->view->is_mapped = true;
|
||||
weston_layer_entry_insert(&shell->layer.view_list,
|
||||
&fsout->black_view->layer_link);
|
||||
&fsout->curtain->view->layer_link);
|
||||
wl_list_init(&fsout->transform.link);
|
||||
|
||||
if (!wl_list_empty(&shell->default_surface_list)) {
|
||||
@@ -373,41 +363,6 @@ restore_output_mode(struct weston_output *output)
|
||||
weston_output_mode_switch_to_native(output);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the bounding box of a surface and all its sub-surfaces,
|
||||
* in surface-local coordinates. */
|
||||
static void
|
||||
surface_subsurfaces_boundingbox(struct weston_surface *surface, int32_t *x,
|
||||
int32_t *y, int32_t *w, int32_t *h) {
|
||||
pixman_region32_t region;
|
||||
pixman_box32_t *box;
|
||||
struct weston_subsurface *subsurface;
|
||||
|
||||
pixman_region32_init_rect(®ion, 0, 0,
|
||||
surface->width,
|
||||
surface->height);
|
||||
|
||||
wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
|
||||
pixman_region32_union_rect(®ion, ®ion,
|
||||
subsurface->position.x,
|
||||
subsurface->position.y,
|
||||
subsurface->surface->width,
|
||||
subsurface->surface->height);
|
||||
}
|
||||
|
||||
box = pixman_region32_extents(®ion);
|
||||
if (x)
|
||||
*x = box->x1;
|
||||
if (y)
|
||||
*y = box->y1;
|
||||
if (w)
|
||||
*w = box->x2 - box->x1;
|
||||
if (h)
|
||||
*h = box->y2 - box->y1;
|
||||
|
||||
pixman_region32_fini(®ion);
|
||||
}
|
||||
|
||||
static void
|
||||
fs_output_center_view(struct fs_output *fsout)
|
||||
{
|
||||
@@ -520,10 +475,10 @@ fs_output_configure_simple(struct fs_output *fsout,
|
||||
break;
|
||||
}
|
||||
|
||||
weston_view_set_position(fsout->black_view,
|
||||
weston_view_set_position(fsout->curtain->view,
|
||||
fsout->output->x - surf_x,
|
||||
fsout->output->y - surf_y);
|
||||
weston_surface_set_size(fsout->black_view->surface,
|
||||
weston_surface_set_size(fsout->curtain->view->surface,
|
||||
fsout->output->width,
|
||||
fsout->output->height);
|
||||
}
|
||||
@@ -670,6 +625,7 @@ fs_output_apply_pending(struct fs_output *fsout)
|
||||
return;
|
||||
}
|
||||
fsout->view->is_mapped = true;
|
||||
fsout->surface->is_mapped = true;
|
||||
|
||||
wl_signal_add(&fsout->surface->destroy_signal,
|
||||
&fsout->surface_destroyed);
|
||||
|
||||
@@ -4,9 +4,10 @@ if get_option('shell-fullscreen')
|
||||
fullscreen_shell_unstable_v1_server_protocol_h,
|
||||
fullscreen_shell_unstable_v1_protocol_c,
|
||||
]
|
||||
deps_shell_fullscreen=[
|
||||
deps_shell_fullscreen = [
|
||||
dep_libweston_public,
|
||||
dep_libexec_weston,
|
||||
dep_shell_utils,
|
||||
]
|
||||
shared_library(
|
||||
'fullscreen-shell',
|
||||
|
||||
@@ -44,6 +44,14 @@ enum weston_desktop_surface_edge {
|
||||
WESTON_DESKTOP_SURFACE_EDGE_BOTTOM_RIGHT = 10,
|
||||
};
|
||||
|
||||
enum weston_top_level_tiled_orientation {
|
||||
WESTON_TOP_LEVEL_TILED_ORIENTATION_NONE = 0 << 0,
|
||||
WESTON_TOP_LEVEL_TILED_ORIENTATION_LEFT = 1 << 1,
|
||||
WESTON_TOP_LEVEL_TILED_ORIENTATION_RIGHT = 1 << 2,
|
||||
WESTON_TOP_LEVEL_TILED_ORIENTATION_TOP = 1 << 3,
|
||||
WESTON_TOP_LEVEL_TILED_ORIENTATION_BOTTOM = 1 << 4,
|
||||
};
|
||||
|
||||
struct weston_desktop;
|
||||
struct weston_desktop_client;
|
||||
struct weston_desktop_surface;
|
||||
@@ -113,6 +121,9 @@ struct weston_desktop_api {
|
||||
*/
|
||||
void (*set_xwayland_position)(struct weston_desktop_surface *surface,
|
||||
int32_t x, int32_t y, void *user_data);
|
||||
void (*get_position)(struct weston_desktop_surface *surface,
|
||||
int32_t *x, int32_t *y,
|
||||
void *user_data);
|
||||
};
|
||||
|
||||
void
|
||||
@@ -163,6 +174,9 @@ void
|
||||
weston_desktop_surface_set_size(struct weston_desktop_surface *surface,
|
||||
int32_t width, int32_t height);
|
||||
void
|
||||
weston_desktop_surface_set_orientation(struct weston_desktop_surface *surface,
|
||||
enum weston_top_level_tiled_orientation tile_orientation);
|
||||
void
|
||||
weston_desktop_surface_close(struct weston_desktop_surface *surface);
|
||||
void
|
||||
weston_desktop_surface_add_metadata_listener(struct weston_desktop_surface *surface,
|
||||
@@ -195,6 +209,19 @@ weston_desktop_surface_get_max_size(struct weston_desktop_surface *surface);
|
||||
struct weston_size
|
||||
weston_desktop_surface_get_min_size(struct weston_desktop_surface *surface);
|
||||
|
||||
bool
|
||||
weston_desktop_window_menu_supported(struct weston_desktop *desktop);
|
||||
bool
|
||||
weston_desktop_move_supported(struct weston_desktop *desktop);
|
||||
bool
|
||||
weston_desktop_resize_supported(struct weston_desktop *desktop);
|
||||
bool
|
||||
weston_desktop_fullscreen_supported(struct weston_desktop *desktop);
|
||||
bool
|
||||
weston_desktop_minimize_supported(struct weston_desktop *desktop);
|
||||
bool
|
||||
weston_desktop_maximize_supported(struct weston_desktop *desktop);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WESTON_DRM_BACKEND_CONFIG_VERSION 4
|
||||
#define WESTON_DRM_BACKEND_CONFIG_VERSION 5
|
||||
|
||||
struct libinput_device;
|
||||
|
||||
@@ -78,6 +78,21 @@ struct weston_drm_output_api {
|
||||
*/
|
||||
void (*set_seat)(struct weston_output *output,
|
||||
const char *seat);
|
||||
|
||||
/** Set the "max bpc" KMS connector property
|
||||
*
|
||||
* The property is used for working around faulty sink hardware like
|
||||
* monitors or media converters that mishandle the kernel driver
|
||||
* chosen bits-per-channel on the physical link. When having trouble,
|
||||
* try a lower value like 8. A value of 0 means that the current max
|
||||
* bpc will be reprogrammed.
|
||||
*
|
||||
* The value actually used in KMS is silently clamped to the range the
|
||||
* KMS driver claims to support. The default value is 16.
|
||||
*
|
||||
* This can be set only while the output is disabled.
|
||||
*/
|
||||
void (*set_max_bpc)(struct weston_output *output, unsigned max_bpc);
|
||||
};
|
||||
|
||||
static inline const struct weston_drm_output_api *
|
||||
@@ -90,7 +105,7 @@ weston_drm_output_get_api(struct weston_compositor *compositor)
|
||||
return (const struct weston_drm_output_api *)api;
|
||||
}
|
||||
|
||||
#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v1"
|
||||
#define WESTON_DRM_VIRTUAL_OUTPUT_API_NAME "weston_drm_virtual_output_api_v2"
|
||||
|
||||
struct drm_fb;
|
||||
typedef int (*submit_frame_cb)(struct weston_output *output, int fd,
|
||||
@@ -101,12 +116,16 @@ struct weston_drm_virtual_output_api {
|
||||
* This is a low-level function, where the caller is expected to wrap
|
||||
* the weston_output function pointers as necessary to make the virtual
|
||||
* output useful. The caller must set up output make, model, serial,
|
||||
* physical size, the mode list and current mode.
|
||||
* physical size, the mode list and current mode. The destroy function
|
||||
* pointer must not be overwritten, as it is used by the DRM backend to
|
||||
* recognize its outputs. Instead, an auxiliary destroy callback has to
|
||||
* be provided as a parameter.
|
||||
*
|
||||
* Returns output on success, NULL on failure.
|
||||
*/
|
||||
struct weston_output* (*create_output)(struct weston_compositor *c,
|
||||
char *name);
|
||||
char *name,
|
||||
void (*destroy_func)(struct weston_output *base));
|
||||
|
||||
/** Set pixel format same as drm_output set_gbm_format().
|
||||
*
|
||||
@@ -171,9 +190,6 @@ weston_drm_virtual_output_get_api(struct weston_compositor *compositor)
|
||||
struct weston_drm_backend_config {
|
||||
struct weston_backend_config base;
|
||||
|
||||
/** The tty to be used. Set to 0 to use the current tty. */
|
||||
int tty;
|
||||
|
||||
/** Whether to use the pixman renderer instead of the OpenGL ES renderer. */
|
||||
bool use_pixman;
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2016 Benoit Gschwind
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef WESTON_COMPOSITOR_FBDEV_H
|
||||
#define WESTON_COMPOSITOR_FBDEV_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
|
||||
#define WESTON_FBDEV_BACKEND_CONFIG_VERSION 2
|
||||
|
||||
struct libinput_device;
|
||||
|
||||
struct weston_fbdev_backend_config {
|
||||
struct weston_backend_config base;
|
||||
|
||||
int tty;
|
||||
char *device;
|
||||
|
||||
/** Callback used to configure input devices.
|
||||
*
|
||||
* This function will be called by the backend when a new input device
|
||||
* needs to be configured.
|
||||
* If NULL the device will use the default configuration.
|
||||
*/
|
||||
void (*configure_device)(struct weston_compositor *compositor,
|
||||
struct libinput_device *device);
|
||||
|
||||
/** The seat to be used for input and output.
|
||||
*
|
||||
* If seat_id is NULL, the seat is taken from XDG_SEAT environment
|
||||
* variable. If neither is set, "seat0" is used. The backend will
|
||||
* take ownership of the seat_id pointer and will free it on
|
||||
* backend destruction.
|
||||
*/
|
||||
char *seat_id;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WESTON_COMPOSITOR_FBDEV_H */
|
||||
@@ -34,6 +34,7 @@ extern "C" {
|
||||
#include <libweston/plugin-registry.h>
|
||||
|
||||
#define WESTON_RDP_OUTPUT_API_NAME "weston_rdp_output_api_v1"
|
||||
#define RDP_DEFAULT_FREQ 60
|
||||
|
||||
struct weston_rdp_output_api {
|
||||
/** Initialize a RDP output with specified width and height.
|
||||
@@ -56,6 +57,11 @@ weston_rdp_output_get_api(struct weston_compositor *compositor)
|
||||
|
||||
#define WESTON_RDP_BACKEND_CONFIG_VERSION 2
|
||||
|
||||
typedef void *(*rdp_audio_in_setup)(struct weston_compositor *c, void *vcm);
|
||||
typedef void (*rdp_audio_in_teardown)(void *audio_private);
|
||||
typedef void *(*rdp_audio_out_setup)(struct weston_compositor *c, void *vcm);
|
||||
typedef void (*rdp_audio_out_teardown)(void *audio_private);
|
||||
|
||||
struct weston_rdp_backend_config {
|
||||
struct weston_backend_config base;
|
||||
char *bind_address;
|
||||
@@ -66,6 +72,13 @@ struct weston_rdp_backend_config {
|
||||
int env_socket;
|
||||
int no_clients_resize;
|
||||
int force_no_compression;
|
||||
bool remotefx_codec;
|
||||
int external_listener_fd;
|
||||
int refresh_rate;
|
||||
rdp_audio_in_setup audio_in_setup;
|
||||
rdp_audio_in_teardown audio_in_teardown;
|
||||
rdp_audio_out_setup audio_out_setup;
|
||||
rdp_audio_out_teardown audio_out_teardown;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -35,26 +35,6 @@ extern "C" {
|
||||
|
||||
#define WESTON_CONFIG_FILE_ENV_VAR "WESTON_CONFIG_FILE"
|
||||
|
||||
enum config_key_type {
|
||||
CONFIG_KEY_INTEGER, /* typeof data = int */
|
||||
CONFIG_KEY_UNSIGNED_INTEGER, /* typeof data = unsigned int */
|
||||
CONFIG_KEY_STRING, /* typeof data = char* */
|
||||
CONFIG_KEY_BOOLEAN /* typeof data = int */
|
||||
};
|
||||
|
||||
struct config_key {
|
||||
const char *name;
|
||||
enum config_key_type type;
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct config_section {
|
||||
const char *name;
|
||||
const struct config_key *keys;
|
||||
int num_keys;
|
||||
void (*done)(void *data);
|
||||
};
|
||||
|
||||
enum weston_option_type {
|
||||
WESTON_OPTION_INTEGER,
|
||||
WESTON_OPTION_UNSIGNED_INTEGER,
|
||||
@@ -108,6 +88,9 @@ weston_config_section_get_bool(struct weston_config_section *section,
|
||||
const char *
|
||||
weston_config_get_name_from_env(void);
|
||||
|
||||
struct weston_config *
|
||||
weston_config_parse_fp(FILE *file);
|
||||
|
||||
struct weston_config *
|
||||
weston_config_parse(const char *name);
|
||||
|
||||
@@ -121,10 +104,8 @@ int weston_config_next_section(struct weston_config *config,
|
||||
struct weston_config_section **section,
|
||||
const char **name);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIGPARSER_H */
|
||||
|
||||
|
||||
+264
-75
@@ -81,6 +81,7 @@ struct weston_pointer_constraint;
|
||||
struct ro_anonymous_file;
|
||||
struct weston_color_profile;
|
||||
struct weston_color_transform;
|
||||
struct pixel_format_info;
|
||||
|
||||
enum weston_keyboard_modifier {
|
||||
MODIFIER_CTRL = (1 << 0),
|
||||
@@ -150,21 +151,6 @@ struct weston_spring {
|
||||
uint32_t clip;
|
||||
};
|
||||
|
||||
struct weston_output_zoom {
|
||||
bool active;
|
||||
float increment;
|
||||
float level;
|
||||
float max_level;
|
||||
float trans_x, trans_y;
|
||||
struct {
|
||||
double x, y;
|
||||
} current;
|
||||
struct weston_seat *seat;
|
||||
struct weston_animation animation_z;
|
||||
struct weston_spring spring_z;
|
||||
struct wl_listener motion_listener;
|
||||
};
|
||||
|
||||
/* bit compatible with drm definitions. */
|
||||
enum dpms_enum {
|
||||
WESTON_DPMS_ON,
|
||||
@@ -222,6 +208,154 @@ struct weston_testsuite_data {
|
||||
void *test_private_data;
|
||||
};
|
||||
|
||||
/** EOTF mode for outputs and heads
|
||||
*
|
||||
* A list of EOTF modes for driving displays, defined by CTA-861-G for
|
||||
* Dynamic Range and Mastering InfoFrame.
|
||||
*
|
||||
* On heads, a bitmask of one or more entries shows which modes are claimed
|
||||
* supported.
|
||||
*
|
||||
* On outputs, the mode to be used for driving the video sink.
|
||||
*
|
||||
* For traditional non-HDR sRGB, use WESTON_EOTF_MODE_SDR.
|
||||
*/
|
||||
enum weston_eotf_mode {
|
||||
/** Invalid EOTF mode, or none supported. */
|
||||
WESTON_EOTF_MODE_NONE = 0,
|
||||
|
||||
/** Traditional gamma, SDR luminance range */
|
||||
WESTON_EOTF_MODE_SDR = 0x01,
|
||||
|
||||
/** Traditional gamma, HDR luminance range */
|
||||
WESTON_EOTF_MODE_TRADITIONAL_HDR = 0x02,
|
||||
|
||||
/** Preceptual quantizer, SMPTE ST 2084 */
|
||||
WESTON_EOTF_MODE_ST2084 = 0x04,
|
||||
|
||||
/** Hybrid log-gamma, ITU-R BT.2100 */
|
||||
WESTON_EOTF_MODE_HLG = 0x08,
|
||||
};
|
||||
|
||||
/** Bitmask of all defined EOTF modes */
|
||||
#define WESTON_EOTF_MODE_ALL_MASK \
|
||||
((uint32_t)(WESTON_EOTF_MODE_SDR | WESTON_EOTF_MODE_TRADITIONAL_HDR | \
|
||||
WESTON_EOTF_MODE_ST2084 | WESTON_EOTF_MODE_HLG))
|
||||
|
||||
/** CIE 1931 xy chromaticity coordinates */
|
||||
struct weston_CIExy {
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
||||
enum weston_hdr_metadata_type1_groups {
|
||||
/** weston_hdr_metadata_type1::primary is set */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES = 0x01,
|
||||
|
||||
/** weston_hdr_metadata_type1::white is set */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_WHITE = 0x02,
|
||||
|
||||
/** weston_hdr_metadata_type1::maxDML is set */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML = 0x04,
|
||||
|
||||
/** weston_hdr_metadata_type1::minDML is set */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_MINDML = 0x08,
|
||||
|
||||
/** weston_hdr_metadata_type1::maxCLL is set */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL = 0x10,
|
||||
|
||||
/** weston_hdr_metadata_type1::maxFALL is set */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL = 0x20,
|
||||
|
||||
/** all valid bits */
|
||||
WESTON_HDR_METADATA_TYPE1_GROUP_ALL_MASK = 0x3f
|
||||
};
|
||||
|
||||
/** HDR static metadata type 1
|
||||
*
|
||||
* The fields are defined by CTA-861-G except here they use float encoding.
|
||||
*
|
||||
* In Weston used only with HDR display modes.
|
||||
*/
|
||||
struct weston_hdr_metadata_type1 {
|
||||
/** Which fields are valid
|
||||
*
|
||||
* A bitmask of values from enum weston_hdr_metadata_type1_groups.
|
||||
*/
|
||||
uint32_t group_mask;
|
||||
|
||||
/* EOTF is tracked externally with enum weston_eotf_mode */
|
||||
|
||||
/** Chromaticities of the primaries, in any order */
|
||||
struct weston_CIExy primary[3];
|
||||
|
||||
/** White point chromaticity */
|
||||
struct weston_CIExy white;
|
||||
|
||||
/** Maximum display mastering luminance, 1 - 65535 cd/m² */
|
||||
float maxDML;
|
||||
|
||||
/** Minimum display mastering luminance, 0.0001 - 6.5535 cd/m² */
|
||||
float minDML;
|
||||
|
||||
/** Maximum content light level, 1 - 65535 cd/m² */
|
||||
float maxCLL;
|
||||
|
||||
/** Maximum frame-average light level, 1 - 65535 cd/m² */
|
||||
float maxFALL;
|
||||
};
|
||||
|
||||
enum weston_color_characteristics_groups {
|
||||
/** weston_color_characteristics::primary is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES = 0x01,
|
||||
|
||||
/** weston_color_characteristics::white is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE = 0x02,
|
||||
|
||||
/** weston_color_characteristics::max_luminance is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL = 0x04,
|
||||
|
||||
/** weston_color_characteristics::min_luminance is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_MINL = 0x08,
|
||||
|
||||
/** weston_color_characteristics::maxFALL is set */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL = 0x10,
|
||||
|
||||
/** all valid bits */
|
||||
WESTON_COLOR_CHARACTERISTICS_GROUP_ALL_MASK = 0x1f
|
||||
};
|
||||
|
||||
/** Basic display color characteristics
|
||||
*
|
||||
* This is a simple description of a display or output (monitor) color
|
||||
* characteristics. The parameters can be found in EDID, with caveats. They
|
||||
* are particularly useful with HDR monitors.
|
||||
*/
|
||||
struct weston_color_characteristics {
|
||||
/** Which fields are valid
|
||||
*
|
||||
* A bitmask of values from enum weston_color_characteristics_groups.
|
||||
*/
|
||||
uint32_t group_mask;
|
||||
|
||||
/* EOTF is tracked externally with enum weston_eotf_mode */
|
||||
|
||||
/** Chromaticities of the primaries */
|
||||
struct weston_CIExy primary[3];
|
||||
|
||||
/** White point chromaticity */
|
||||
struct weston_CIExy white;
|
||||
|
||||
/** Display's desired maximum content peak luminance, cd/m² */
|
||||
float max_luminance;
|
||||
|
||||
/** Display's desired minimum content luminance, cd/m² */
|
||||
float min_luminance;
|
||||
|
||||
/** Display's desired maximum frame-average light level, cd/m² */
|
||||
float maxFALL;
|
||||
};
|
||||
|
||||
/** Represents a head, usually a display connector
|
||||
*
|
||||
* \rst
|
||||
@@ -258,9 +392,36 @@ struct weston_head {
|
||||
char *name; /**< head name, e.g. connector name */
|
||||
bool connected; /**< is physically connected */
|
||||
bool non_desktop; /**< non-desktop display, e.g. HMD */
|
||||
uint32_t supported_eotf_mask; /**< supported weston_eotf_mode bits */
|
||||
|
||||
/** Current content protection status */
|
||||
enum weston_hdcp_protection current_protection;
|
||||
|
||||
/** Opaque pointer used by backends to identify heads as theirs */
|
||||
const void *backend_id;
|
||||
};
|
||||
|
||||
/** Output properties derived from its color characteristics and profile
|
||||
*
|
||||
* These are constructed by a color manager.
|
||||
*
|
||||
* A weston_output_color_outcome owns (a reference to) everything it contains.
|
||||
*
|
||||
* \ingroup output
|
||||
* \internal
|
||||
*/
|
||||
struct weston_output_color_outcome {
|
||||
/** sRGB to output color space transformation */
|
||||
struct weston_color_transform *from_sRGB_to_output;
|
||||
|
||||
/** sRGB to blending color space transformation */
|
||||
struct weston_color_transform *from_sRGB_to_blend;
|
||||
|
||||
/** Blending to output color space transformation */
|
||||
struct weston_color_transform *from_blend_to_output;
|
||||
|
||||
/** HDR Static Metadata Type 1 for WESTON_EOTF_MODE_ST2084 */
|
||||
struct weston_hdr_metadata_type1 hdr_meta;
|
||||
};
|
||||
|
||||
/** Content producer for heads
|
||||
@@ -324,7 +485,6 @@ struct weston_output {
|
||||
/** For cancelling the idle_repaint callback on output destruction. */
|
||||
struct wl_event_source *idle_repaint_source;
|
||||
|
||||
struct weston_output_zoom zoom;
|
||||
int dirty;
|
||||
struct wl_signal frame_signal;
|
||||
struct wl_signal destroy_signal; /**< sent when disabled */
|
||||
@@ -352,11 +512,9 @@ struct weston_output {
|
||||
bool allow_protection;
|
||||
|
||||
int (*start_repaint_loop)(struct weston_output *output);
|
||||
int (*repaint)(struct weston_output *output,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data);
|
||||
int (*repaint)(struct weston_output *output, pixman_region32_t *damage);
|
||||
void (*destroy)(struct weston_output *output);
|
||||
void (*assign_planes)(struct weston_output *output, void *repaint_data);
|
||||
void (*assign_planes)(struct weston_output *output);
|
||||
int (*switch_mode)(struct weston_output *output, struct weston_mode *mode);
|
||||
|
||||
/* backlight values are on 0-255 range, where higher is brighter */
|
||||
@@ -375,10 +533,12 @@ struct weston_output {
|
||||
int scale;
|
||||
|
||||
struct weston_color_profile *color_profile;
|
||||
struct weston_color_transform *from_sRGB_to_output;
|
||||
struct weston_color_transform *from_sRGB_to_blend;
|
||||
struct weston_color_transform *from_blend_to_output;
|
||||
bool from_blend_to_output_by_backend;
|
||||
enum weston_eotf_mode eotf_mode;
|
||||
struct weston_color_characteristics color_characteristics;
|
||||
|
||||
struct weston_output_color_outcome *color_outcome;
|
||||
uint64_t color_outcome_serial;
|
||||
|
||||
int (*enable)(struct weston_output *output);
|
||||
int (*disable)(struct weston_output *output);
|
||||
@@ -937,39 +1097,6 @@ struct weston_plane {
|
||||
|
||||
struct weston_drm_format_array;
|
||||
|
||||
struct weston_renderer {
|
||||
int (*read_pixels)(struct weston_output *output,
|
||||
pixman_format_code_t format, void *pixels,
|
||||
uint32_t x, uint32_t y,
|
||||
uint32_t width, uint32_t height);
|
||||
void (*repaint_output)(struct weston_output *output,
|
||||
pixman_region32_t *output_damage);
|
||||
void (*flush_damage)(struct weston_surface *surface);
|
||||
void (*attach)(struct weston_surface *es, struct weston_buffer *buffer);
|
||||
void (*surface_set_color)(struct weston_surface *surface,
|
||||
float red, float green,
|
||||
float blue, float alpha);
|
||||
void (*destroy)(struct weston_compositor *ec);
|
||||
|
||||
|
||||
/** See weston_surface_get_content_size() */
|
||||
void (*surface_get_content_size)(struct weston_surface *surface,
|
||||
int *width, int *height);
|
||||
|
||||
/** See weston_surface_copy_content() */
|
||||
int (*surface_copy_content)(struct weston_surface *surface,
|
||||
void *target, size_t size,
|
||||
int src_x, int src_y,
|
||||
int width, int height);
|
||||
|
||||
/** See weston_compositor_import_dmabuf() */
|
||||
bool (*import_dmabuf)(struct weston_compositor *ec,
|
||||
struct linux_dmabuf_buffer *buffer);
|
||||
|
||||
const struct weston_drm_format_array *
|
||||
(*get_supported_formats)(struct weston_compositor *ec);
|
||||
};
|
||||
|
||||
enum weston_capability {
|
||||
/* backend/renderer supports arbitrary rotation */
|
||||
WESTON_CAP_ROTATION_ANY = 0x0001,
|
||||
@@ -1126,7 +1253,7 @@ struct weston_compositor {
|
||||
|
||||
struct weston_color_manager *color_manager;
|
||||
struct weston_renderer *renderer;
|
||||
pixman_format_code_t read_format;
|
||||
const struct pixel_format_info *read_format;
|
||||
|
||||
struct weston_backend *backend;
|
||||
struct weston_launcher *launcher;
|
||||
@@ -1137,6 +1264,7 @@ struct weston_compositor {
|
||||
struct wl_list plugin_api_list; /* struct weston_plugin_api::link */
|
||||
|
||||
uint32_t output_id_pool;
|
||||
bool output_flow_dirty;
|
||||
|
||||
struct xkb_rule_names xkb_names;
|
||||
struct xkb_context *xkb_context;
|
||||
@@ -1182,8 +1310,17 @@ struct weston_compositor {
|
||||
struct weston_log_context *weston_log_ctx;
|
||||
struct weston_log_scope *debug_scene;
|
||||
struct weston_log_scope *timeline;
|
||||
struct weston_log_scope *libseat_debug;
|
||||
|
||||
struct content_protection *content_protection;
|
||||
|
||||
/* One-time warning about a view appearing in the layer list when it
|
||||
* or its surface are not mapped. */
|
||||
bool warned_about_unmapped_surface_or_view;
|
||||
};
|
||||
|
||||
struct weston_solid_buffer_values {
|
||||
float r, g, b, a;
|
||||
};
|
||||
|
||||
struct weston_buffer {
|
||||
@@ -1191,19 +1328,45 @@ struct weston_buffer {
|
||||
struct wl_signal destroy_signal;
|
||||
struct wl_listener destroy_listener;
|
||||
|
||||
enum {
|
||||
WESTON_BUFFER_SHM,
|
||||
WESTON_BUFFER_DMABUF,
|
||||
WESTON_BUFFER_RENDERER_OPAQUE,
|
||||
WESTON_BUFFER_SOLID,
|
||||
} type;
|
||||
|
||||
union {
|
||||
struct wl_shm_buffer *shm_buffer;
|
||||
void *dmabuf;
|
||||
void *legacy_buffer;
|
||||
struct weston_solid_buffer_values solid;
|
||||
};
|
||||
|
||||
int32_t width, height;
|
||||
uint32_t busy_count;
|
||||
int y_inverted;
|
||||
uint32_t passive_count;
|
||||
enum {
|
||||
ORIGIN_TOP_LEFT, /* buffer content starts at (0,0) */
|
||||
ORIGIN_BOTTOM_LEFT, /* buffer content starts at (0, height) */
|
||||
} buffer_origin;
|
||||
bool direct_display;
|
||||
|
||||
void *renderer_private;
|
||||
void *backend_private;
|
||||
|
||||
const struct pixel_format_info *pixel_format;
|
||||
uint64_t format_modifier;
|
||||
};
|
||||
|
||||
enum weston_buffer_reference_type {
|
||||
BUFFER_REF_NONE,
|
||||
BUFFER_MAY_BE_ACCESSED,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED,
|
||||
};
|
||||
|
||||
struct weston_buffer_reference {
|
||||
struct weston_buffer *buffer;
|
||||
struct wl_listener destroy_listener;
|
||||
enum weston_buffer_reference_type type;
|
||||
};
|
||||
|
||||
struct weston_buffer_viewport {
|
||||
@@ -1377,6 +1540,7 @@ struct weston_surface_state {
|
||||
int newly_attached;
|
||||
struct weston_buffer *buffer;
|
||||
struct wl_listener buffer_destroy_listener;
|
||||
|
||||
int32_t sx;
|
||||
int32_t sy;
|
||||
|
||||
@@ -1762,6 +1926,21 @@ weston_surface_create(struct weston_compositor *compositor);
|
||||
struct weston_view *
|
||||
weston_view_create(struct weston_surface *surface);
|
||||
|
||||
struct weston_buffer_reference *
|
||||
weston_buffer_create_solid_rgba(struct weston_compositor *compositor,
|
||||
float r, float g, float b, float a);
|
||||
|
||||
void
|
||||
weston_surface_attach_solid(struct weston_surface *surface,
|
||||
struct weston_buffer_reference *buffer_ref,
|
||||
int w, int h);
|
||||
|
||||
void
|
||||
weston_buffer_destroy_solid(struct weston_buffer_reference *buffer_ref);
|
||||
|
||||
bool
|
||||
weston_surface_has_content(struct weston_surface *surface);
|
||||
|
||||
void
|
||||
weston_view_destroy(struct weston_view *view);
|
||||
|
||||
@@ -1802,6 +1981,9 @@ weston_view_damage_below(struct weston_view *view);
|
||||
void
|
||||
weston_view_unmap(struct weston_view *view);
|
||||
|
||||
void
|
||||
weston_surface_map(struct weston_surface *surface);
|
||||
|
||||
void
|
||||
weston_surface_unmap(struct weston_surface *surface);
|
||||
|
||||
@@ -1835,7 +2017,8 @@ weston_surface_copy_content(struct weston_surface *surface,
|
||||
int width, int height);
|
||||
|
||||
struct weston_buffer *
|
||||
weston_buffer_from_resource(struct wl_resource *resource);
|
||||
weston_buffer_from_resource(struct weston_compositor *ec,
|
||||
struct wl_resource *resource);
|
||||
|
||||
void
|
||||
weston_compositor_get_time(struct timespec *time);
|
||||
@@ -1858,7 +2041,6 @@ weston_compositor_add_destroy_listener_once(struct weston_compositor *compositor
|
||||
|
||||
enum weston_compositor_backend {
|
||||
WESTON_BACKEND_DRM,
|
||||
WESTON_BACKEND_FBDEV,
|
||||
WESTON_BACKEND_HEADLESS,
|
||||
WESTON_BACKEND_RDP,
|
||||
WESTON_BACKEND_WAYLAND,
|
||||
@@ -1877,11 +2059,6 @@ void
|
||||
weston_compositor_exit_with_code(struct weston_compositor *compositor,
|
||||
int exit_code);
|
||||
void
|
||||
weston_output_update_zoom(struct weston_output *output);
|
||||
void
|
||||
weston_output_activate_zoom(struct weston_output *output,
|
||||
struct weston_seat *seat);
|
||||
void
|
||||
weston_output_add_destroy_listener(struct weston_output *output,
|
||||
struct wl_listener *listener);
|
||||
struct wl_listener *
|
||||
@@ -1960,12 +2137,11 @@ struct weston_view_animation *
|
||||
weston_slide_run(struct weston_view *view, float start, float stop,
|
||||
weston_view_animation_done_func_t done, void *data);
|
||||
|
||||
void
|
||||
weston_surface_set_color(struct weston_surface *surface,
|
||||
float red, float green, float blue, float alpha);
|
||||
struct weston_surface *
|
||||
weston_surface_ref(struct weston_surface *surface);
|
||||
|
||||
void
|
||||
weston_surface_destroy(struct weston_surface *surface);
|
||||
weston_surface_unref(struct weston_surface *surface);
|
||||
|
||||
int
|
||||
weston_output_mode_switch_to_temporary(struct weston_output *output,
|
||||
@@ -2068,11 +2244,7 @@ weston_compositor_find_output_by_name(struct weston_compositor *compositor,
|
||||
|
||||
struct weston_output *
|
||||
weston_compositor_create_output(struct weston_compositor *compositor,
|
||||
const char *name);
|
||||
|
||||
struct weston_output *
|
||||
weston_compositor_create_output_with_head(struct weston_compositor *compositor,
|
||||
struct weston_head *head);
|
||||
struct weston_head *head, const char *name);
|
||||
|
||||
void
|
||||
weston_output_destroy(struct weston_output *output);
|
||||
@@ -2097,6 +2269,20 @@ bool
|
||||
weston_output_set_color_profile(struct weston_output *output,
|
||||
struct weston_color_profile *cprof);
|
||||
|
||||
void
|
||||
weston_output_set_eotf_mode(struct weston_output *output,
|
||||
enum weston_eotf_mode eotf_mode);
|
||||
|
||||
enum weston_eotf_mode
|
||||
weston_output_get_eotf_mode(const struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_set_color_characteristics(struct weston_output *output,
|
||||
const struct weston_color_characteristics *cc);
|
||||
|
||||
const struct weston_color_characteristics *
|
||||
weston_output_get_color_characteristics(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_init(struct weston_output *output,
|
||||
struct weston_compositor *compositor,
|
||||
@@ -2111,6 +2297,9 @@ weston_output_enable(struct weston_output *output);
|
||||
void
|
||||
weston_output_disable(struct weston_output *output);
|
||||
|
||||
uint32_t
|
||||
weston_output_get_supported_eotf_modes(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_compositor_flush_heads_changed(struct weston_compositor *compositor);
|
||||
|
||||
|
||||
@@ -65,19 +65,6 @@ int
|
||||
weston_matrix_invert(struct weston_matrix *inverse,
|
||||
const struct weston_matrix *matrix);
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
# define MATRIX_TEST_EXPORT WL_EXPORT
|
||||
|
||||
int
|
||||
matrix_invert(double *A, unsigned *p, const struct weston_matrix *matrix);
|
||||
|
||||
void
|
||||
inverse_transform(const double *LU, const unsigned *p, float *v);
|
||||
|
||||
#else
|
||||
# define MATRIX_TEST_EXPORT static
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,7 +12,6 @@ install_headers(
|
||||
)
|
||||
|
||||
backend_drm_h = files('backend-drm.h')
|
||||
backend_fbdev_h = files('backend-fbdev.h')
|
||||
backend_headless_h = files('backend-headless.h')
|
||||
backend_rdp_h = files('backend-rdp.h')
|
||||
backend_wayland_h = files('backend-wayland.h')
|
||||
|
||||
@@ -109,6 +109,8 @@ weston_log_subscription_complete(struct weston_log_subscription *sub);
|
||||
char *
|
||||
weston_log_scope_timestamp(struct weston_log_scope *scope,
|
||||
char *buf, size_t len);
|
||||
char *
|
||||
weston_log_timestamp(char *buf, size_t len, int *cached_tm_mday);
|
||||
|
||||
void
|
||||
weston_log_subscriber_destroy(struct weston_log_subscriber *subscriber);
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
In-vehicle infotainment (information and entertainment)
|
||||
graphical environment support modules for Weston
|
||||
|
||||
|
||||
IVI-shell is an alternative shell for Weston, a Wayland display server.
|
||||
Window management and application interaction with the display server
|
||||
are very different to that of a normal desktop, which is why this is
|
||||
a separate shell and not an extension to the desktop-shell suite with
|
||||
xdg_shell. As such, applications need to be specifically written to use
|
||||
IVI-shell.
|
||||
|
||||
IVI-shell contains two main features:
|
||||
- Common layout library for surface, which allow ivi-shell developer
|
||||
to develop own shell, linking Common layout library.
|
||||
For the time being, the library refers Genivi ilm interface.
|
||||
|
||||
https://at.projects.genivi.org/wiki/display/WIE/Wayland+IVI+Extension+Home
|
||||
|
||||
- Extension protocol; ivi-application to tie wl_surface and a given ID.
|
||||
With this ID, shell can identify which wl_surface is drawn by which
|
||||
application. In in-vehicle infortainment system, a shell has to update
|
||||
a property of a wl_surface. E.g. there may be a use case when vehicle
|
||||
starts to move, the wl_surface drawn by Car navigation is expected to
|
||||
move top of surfaces.
|
||||
|
||||
The actual software components delivered with Weston are:
|
||||
|
||||
- ivi-application.xml:
|
||||
Wayland protocol extension for IVI-applications; the public
|
||||
shell protocol (the same concept as xdg_shell).
|
||||
Implemented by ivi-shell.so.
|
||||
|
||||
- ivi-shell.so:
|
||||
A Weston shell module that implements ivi-application.xml interfaces.
|
||||
Loads ivi-layout.so.
|
||||
|
||||
- ivi-layout.so:
|
||||
Implements the IVI window management concepts: Screen, Layer,
|
||||
Surface, groups of Layers, groups of Surfaces, see:
|
||||
https://at.projects.genivi.org/wiki/display/WIE/Summary+of+Layer+manager+APIs
|
||||
Offers a stable API for writing IVI-controller modules like
|
||||
hmi-controller.so against the IVI concepts. In other words,
|
||||
it offers an API to write IVI window manager modules.
|
||||
|
||||
- hmi-controller.so:
|
||||
A sample implementation of an IVI-controller module, usually
|
||||
replaced by IVI system vendors.
|
||||
Uses ivi-layout.so to perform essentially window manager tasks.
|
||||
This implementation keeps all window management inside the module,
|
||||
while IVI-systems may use another module that exposes all window
|
||||
management via Wayland or other protocol for an external process
|
||||
to control.
|
||||
|
||||
- ivi-hmi-controller.xml:
|
||||
Wayland protocol extension for IVI display control; the private
|
||||
shell protocol for weston-ivi-shell-user-interface client
|
||||
(the same concept as desktop-shell.xml).
|
||||
Implemented by hmi-controller.so, and usually replaced by IVI
|
||||
system vendors.
|
||||
|
||||
- weston-ivi-shell-user-interface:
|
||||
A sample implementation of an IVI shell helper client, usually
|
||||
replaced by IVI system vendors.
|
||||
A helper client for basic display content, similar to
|
||||
weston-desktop-shell.
|
||||
|
||||
|
||||
How to compile:
|
||||
same as weston. To disable, use option: --disable-ivi-shell for configure.
|
||||
|
||||
How to configure weston.ini:
|
||||
reference ini file will be generated in <build_dir>/ivi-shell.
|
||||
|
||||
How to run:
|
||||
same as weston. exec weston.
|
||||
|
||||
How to use UI:
|
||||
http://lists.freedesktop.org/archives/wayland-devel/attachments/20140625/abbfc064/attachment-0001.png
|
||||
+16
-21
@@ -153,13 +153,6 @@ struct launcher_info {
|
||||
/*****************************************************************************
|
||||
* local functions
|
||||
****************************************************************************/
|
||||
static void *
|
||||
mem_alloc(size_t size, char *file, int32_t line)
|
||||
{
|
||||
return fail_on_null(calloc(1, size), size, file, line);
|
||||
}
|
||||
|
||||
#define MEM_ALLOC(s) mem_alloc((s),__FILE__,__LINE__)
|
||||
|
||||
static int32_t
|
||||
is_surf_in_ui_widget(struct hmi_controller *hmi_ctrl,
|
||||
@@ -222,8 +215,8 @@ mode_divided_into_tiling(struct hmi_controller *hmi_ctrl,
|
||||
int32_t surf_num = 0;
|
||||
int32_t idx = 0;
|
||||
|
||||
surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length);
|
||||
new_order = MEM_ALLOC(sizeof(*surfaces) * surface_length);
|
||||
surfaces = xcalloc(surface_length, sizeof(*surfaces));
|
||||
new_order = xcalloc(surface_length, sizeof(*surfaces));
|
||||
|
||||
for (i = 0; i < surface_length; i++) {
|
||||
ivisurf = pp_surface[i];
|
||||
@@ -297,8 +290,8 @@ mode_divided_into_sidebyside(struct hmi_controller *hmi_ctrl,
|
||||
int32_t surf_num = 0;
|
||||
int32_t idx = 0;
|
||||
|
||||
surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length);
|
||||
new_order = MEM_ALLOC(sizeof(*surfaces) * surface_length);
|
||||
surfaces = xcalloc(surface_length, sizeof(*surfaces));
|
||||
new_order = xcalloc(surface_length, sizeof(*surfaces));
|
||||
|
||||
for (i = 0; i < surface_length; i++) {
|
||||
ivisurf = pp_surface[i];
|
||||
@@ -362,7 +355,7 @@ mode_fullscreen_someone(struct hmi_controller *hmi_ctrl,
|
||||
int32_t surf_num = 0;
|
||||
struct ivi_layout_surface **surfaces;
|
||||
|
||||
surfaces = MEM_ALLOC(sizeof(*surfaces) * surface_length);
|
||||
surfaces = xcalloc(surface_length, sizeof(*surfaces));
|
||||
|
||||
for (i = 0; i < surface_length; i++) {
|
||||
ivisurf = pp_surface[i];
|
||||
@@ -412,7 +405,7 @@ mode_random_replace(struct hmi_controller *hmi_ctrl,
|
||||
int32_t i = 0;
|
||||
int32_t layer_idx = 0;
|
||||
|
||||
layers = MEM_ALLOC(sizeof(*layers) * hmi_ctrl->screen_num);
|
||||
layers = xcalloc(hmi_ctrl->screen_num, sizeof(*layers));
|
||||
|
||||
wl_list_for_each(application_layer, layer_list, link) {
|
||||
layers[layer_idx] = application_layer;
|
||||
@@ -689,7 +682,7 @@ set_notification_configure_desktop_surface(struct wl_listener *listener, void *d
|
||||
static struct hmi_server_setting *
|
||||
hmi_server_setting_create(struct weston_compositor *ec)
|
||||
{
|
||||
struct hmi_server_setting *setting = MEM_ALLOC(sizeof(*setting));
|
||||
struct hmi_server_setting *setting = xzalloc(sizeof(*setting));
|
||||
struct weston_config *config = wet_get_config(ec);
|
||||
struct weston_config_section *shell_section = NULL;
|
||||
char *ivi_ui_config;
|
||||
@@ -745,6 +738,8 @@ hmi_controller_destroy(struct wl_listener *listener, void *data)
|
||||
struct hmi_controller *hmi_ctrl =
|
||||
container_of(listener, struct hmi_controller, destroy_listener);
|
||||
|
||||
wl_list_remove(&hmi_ctrl->destroy_listener.link);
|
||||
|
||||
wl_list_for_each_safe(link, next,
|
||||
&hmi_ctrl->workspace_fade.layer_list, link) {
|
||||
wl_list_remove(&link->link);
|
||||
@@ -804,7 +799,7 @@ hmi_controller_create(struct weston_compositor *ec)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hmi_ctrl = MEM_ALLOC(sizeof(*hmi_ctrl));
|
||||
hmi_ctrl = xzalloc(sizeof(*hmi_ctrl));
|
||||
i = 0;
|
||||
|
||||
wl_array_init(&hmi_ctrl->ui_widgets);
|
||||
@@ -817,7 +812,7 @@ hmi_controller_create(struct weston_compositor *ec)
|
||||
/* init base ivi_layer*/
|
||||
wl_list_init(&hmi_ctrl->base_layer_list);
|
||||
wl_list_for_each(output, &ec->output_list, link) {
|
||||
base_layer = MEM_ALLOC(1 * sizeof(struct hmi_controller_layer));
|
||||
base_layer = xzalloc(sizeof(struct hmi_controller_layer));
|
||||
base_layer->x = 0;
|
||||
base_layer->y = 0;
|
||||
base_layer->width = output->current_mode->width;
|
||||
@@ -837,7 +832,7 @@ hmi_controller_create(struct weston_compositor *ec)
|
||||
/* init application ivi_layer */
|
||||
wl_list_init(&hmi_ctrl->application_layer_list);
|
||||
wl_list_for_each(output, &ec->output_list, link) {
|
||||
application_layer = MEM_ALLOC(1 * sizeof(struct hmi_controller_layer));
|
||||
application_layer = xzalloc(sizeof(struct hmi_controller_layer));
|
||||
application_layer->x = 0;
|
||||
application_layer->y = 0;
|
||||
application_layer->width = output->current_mode->width;
|
||||
@@ -872,7 +867,7 @@ hmi_controller_create(struct weston_compositor *ec)
|
||||
|
||||
|
||||
wl_list_init(&hmi_ctrl->workspace_fade.layer_list);
|
||||
tmp_link_layer = MEM_ALLOC(sizeof(*tmp_link_layer));
|
||||
tmp_link_layer = xzalloc(sizeof(*tmp_link_layer));
|
||||
tmp_link_layer->layout_layer =
|
||||
hmi_ctrl->workspace_background_layer.ivilayer;
|
||||
wl_list_insert(&hmi_ctrl->workspace_fade.layer_list,
|
||||
@@ -1267,7 +1262,7 @@ ivi_hmi_controller_add_launchers(struct hmi_controller *hmi_ctrl,
|
||||
hmi_ctrl->interface->layer_set_visibility(hmi_ctrl->workspace_layer.ivilayer,
|
||||
false);
|
||||
|
||||
tmp_link_layer = MEM_ALLOC(sizeof(*tmp_link_layer));
|
||||
tmp_link_layer = xzalloc(sizeof(*tmp_link_layer));
|
||||
tmp_link_layer->layout_layer = hmi_ctrl->workspace_layer.ivilayer;
|
||||
wl_list_insert(&hmi_ctrl->workspace_fade.layer_list,
|
||||
&tmp_link_layer->link);
|
||||
@@ -1756,7 +1751,7 @@ create_workspace_pointer_move(struct weston_pointer *pointer,
|
||||
struct wl_resource* resource)
|
||||
{
|
||||
struct pointer_move_grab *pnt_move_grab =
|
||||
MEM_ALLOC(sizeof(*pnt_move_grab));
|
||||
xzalloc(sizeof(*pnt_move_grab));
|
||||
|
||||
pnt_move_grab->base.resource = resource;
|
||||
move_grab_init_workspace(&pnt_move_grab->move, pointer->grab_x,
|
||||
@@ -1770,7 +1765,7 @@ create_workspace_touch_move(struct weston_touch *touch,
|
||||
struct wl_resource* resource)
|
||||
{
|
||||
struct touch_move_grab *tch_move_grab =
|
||||
MEM_ALLOC(sizeof(*tch_move_grab));
|
||||
xzalloc(sizeof(*tch_move_grab));
|
||||
|
||||
tch_move_grab->base.resource = resource;
|
||||
tch_move_grab->is_active = 1;
|
||||
|
||||
@@ -43,9 +43,6 @@
|
||||
* way in In-Vehicle Infotainment system, which integrate several domains
|
||||
* in one system. A layer is allocated to a domain in order to control
|
||||
* application surfaces grouped to the layer all together.
|
||||
*
|
||||
* This API and ABI follow following specifications.
|
||||
* https://at.projects.genivi.org/wiki/display/PROJ/Wayland+IVI+Extension+Design
|
||||
*/
|
||||
|
||||
#ifndef _IVI_LAYOUT_EXPORT_H_
|
||||
@@ -67,7 +64,6 @@ extern "C" {
|
||||
#define IVI_INVALID_ID UINT_MAX
|
||||
|
||||
struct ivi_layout_layer;
|
||||
struct ivi_layout_screen;
|
||||
struct ivi_layout_surface;
|
||||
|
||||
struct ivi_layout_surface_properties
|
||||
|
||||
@@ -44,7 +44,8 @@ ivi_layout_desktop_surface_configure(struct ivi_layout_surface *ivisurf,
|
||||
int32_t width, int32_t height);
|
||||
|
||||
struct ivi_layout_surface*
|
||||
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface);
|
||||
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface,
|
||||
struct weston_desktop_surface *surface);
|
||||
|
||||
void
|
||||
ivi_layout_surface_configure(struct ivi_layout_surface *ivisurf,
|
||||
|
||||
+18
-9
@@ -835,7 +835,7 @@ build_view_list(struct ivi_layout *layout)
|
||||
weston_layer_entry_insert(&layout->layout_layer.view_list,
|
||||
&ivi_view->view->layer_link);
|
||||
|
||||
ivi_view->ivisurf->surface->is_mapped = true;
|
||||
weston_surface_map(ivi_view->ivisurf->surface);
|
||||
ivi_view->view->is_mapped = true;
|
||||
}
|
||||
}
|
||||
@@ -1192,14 +1192,13 @@ ivi_layout_get_layers_under_surface(struct ivi_layout_surface *ivisurf,
|
||||
else
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
*pLength = length;
|
||||
|
||||
if (!length) {
|
||||
if (length == 0) {
|
||||
free(*ppArray);
|
||||
*ppArray = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
*pLength = length;
|
||||
|
||||
return IVI_SUCCEEDED;
|
||||
}
|
||||
@@ -1882,7 +1881,6 @@ ivi_layout_surface_set_id(struct ivi_layout_surface *ivisurf,
|
||||
|
||||
ivisurf->id_surface = id_surface;
|
||||
|
||||
wl_signal_emit(&layout->surface_notification.created, ivisurf);
|
||||
wl_signal_emit(&layout->surface_notification.configure_changed,
|
||||
ivisurf);
|
||||
|
||||
@@ -1979,9 +1977,20 @@ ivi_layout_desktop_surface_configure(struct ivi_layout_surface *ivisurf,
|
||||
}
|
||||
|
||||
struct ivi_layout_surface*
|
||||
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface)
|
||||
ivi_layout_desktop_surface_create(struct weston_surface *wl_surface,
|
||||
struct weston_desktop_surface *surface)
|
||||
{
|
||||
return surface_create(wl_surface, IVI_INVALID_ID);
|
||||
struct ivi_layout *layout = get_instance();
|
||||
struct ivi_layout_surface *ivisurf;
|
||||
|
||||
ivisurf = surface_create(wl_surface, IVI_INVALID_ID);
|
||||
|
||||
if (ivisurf) {
|
||||
ivisurf->weston_desktop_surface = surface;
|
||||
wl_signal_emit(&layout->surface_notification.created, ivisurf);
|
||||
}
|
||||
|
||||
return ivisurf;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+10
-4
@@ -346,7 +346,6 @@ shell_destroy(struct wl_listener *listener, void *data)
|
||||
wl_list_remove(&shell->destroy_listener.link);
|
||||
wl_list_remove(&shell->wake_listener.link);
|
||||
|
||||
weston_desktop_destroy(shell->desktop);
|
||||
|
||||
wl_list_for_each_safe(ivisurf, next, &shell->ivi_surface_list, link) {
|
||||
if (ivisurf->layout_surface != NULL)
|
||||
@@ -357,6 +356,7 @@ shell_destroy(struct wl_listener *listener, void *data)
|
||||
|
||||
ivi_layout_fini();
|
||||
|
||||
weston_desktop_destroy(shell->desktop);
|
||||
free(shell);
|
||||
}
|
||||
|
||||
@@ -489,13 +489,11 @@ desktop_surface_added(struct weston_desktop_surface *surface,
|
||||
struct weston_surface *weston_surf =
|
||||
weston_desktop_surface_get_surface(surface);
|
||||
|
||||
layout_surface = ivi_layout_desktop_surface_create(weston_surf);
|
||||
layout_surface = ivi_layout_desktop_surface_create(weston_surf, surface);
|
||||
if (!layout_surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
layout_surface->weston_desktop_surface = surface;
|
||||
|
||||
ivisurf = zalloc(sizeof *ivisurf);
|
||||
if (!ivisurf) {
|
||||
return;
|
||||
@@ -509,6 +507,8 @@ desktop_surface_added(struct weston_desktop_surface *surface,
|
||||
ivisurf->layout_surface = layout_surface;
|
||||
ivisurf->surface = weston_surf;
|
||||
|
||||
wl_list_insert(&shell->ivi_surface_list, &ivisurf->link);
|
||||
|
||||
weston_desktop_surface_set_user_data(surface, ivisurf);
|
||||
}
|
||||
|
||||
@@ -521,8 +521,14 @@ desktop_surface_removed(struct weston_desktop_surface *surface,
|
||||
|
||||
assert(ivisurf != NULL);
|
||||
|
||||
weston_desktop_surface_set_user_data(surface, NULL);
|
||||
|
||||
if (ivisurf->layout_surface)
|
||||
layout_surface_cleanup(ivisurf);
|
||||
|
||||
wl_list_remove(&ivisurf->link);
|
||||
|
||||
free(ivisurf);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -15,7 +15,6 @@ if get_option('shell-ivi')
|
||||
dependencies: [
|
||||
dep_libm,
|
||||
dep_libexec_weston,
|
||||
dep_lib_desktop,
|
||||
dep_libweston_public
|
||||
],
|
||||
name_prefix: '',
|
||||
|
||||
+47
-25
@@ -33,7 +33,7 @@
|
||||
#include "kiosk-shell-grab.h"
|
||||
#include "compositor/weston.h"
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/shell-utils.h"
|
||||
#include "shell-utils.h"
|
||||
|
||||
#include <libweston/xwayland-api.h>
|
||||
|
||||
@@ -413,6 +413,7 @@ kiosk_shell_surface_activate(struct kiosk_shell_surface *shsurf,
|
||||
weston_layer_entry_insert(&shsurf->shell->normal_layer.view_list,
|
||||
&shsurf->view->layer_link);
|
||||
weston_view_geometry_dirty(shsurf->view);
|
||||
weston_view_update_transform(shsurf->view);
|
||||
weston_surface_damage(shsurf->view->surface);
|
||||
}
|
||||
|
||||
@@ -480,13 +481,14 @@ static void
|
||||
kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput)
|
||||
{
|
||||
struct kiosk_shell *shell = shoutput->shell;
|
||||
struct weston_compositor *ec = shell->compositor;
|
||||
struct weston_output *output = shoutput->output;
|
||||
struct weston_config_section *shell_section = NULL;
|
||||
uint32_t bg_color = 0x0;
|
||||
struct weston_solid_color_surface solid_surface = {};
|
||||
struct weston_curtain_params curtain_params = {};
|
||||
|
||||
if (shoutput->background_view)
|
||||
weston_surface_destroy(shoutput->background_view->surface);
|
||||
if (shoutput->curtain)
|
||||
weston_curtain_destroy(shoutput->curtain);
|
||||
|
||||
if (!output)
|
||||
return;
|
||||
@@ -497,31 +499,33 @@ kiosk_shell_output_recreate_background(struct kiosk_shell_output *shoutput)
|
||||
weston_config_section_get_color(shell_section, "background-color",
|
||||
&bg_color, 0x00000000);
|
||||
|
||||
solid_surface.r = ((bg_color >> 16) & 0xff) / 255.0;
|
||||
solid_surface.g = ((bg_color >> 8) & 0xff) / 255.0;
|
||||
solid_surface.b = ((bg_color >> 0) & 0xff) / 255.0;
|
||||
curtain_params.r = ((bg_color >> 16) & 0xff) / 255.0;
|
||||
curtain_params.g = ((bg_color >> 8) & 0xff) / 255.0;
|
||||
curtain_params.b = ((bg_color >> 0) & 0xff) / 255.0;
|
||||
curtain_params.a = 1.0;
|
||||
|
||||
solid_surface.get_label = kiosk_shell_background_surface_get_label;
|
||||
solid_surface.surface_committed = NULL;
|
||||
solid_surface.surface_private = NULL;
|
||||
curtain_params.x = output->x;
|
||||
curtain_params.y = output->y;
|
||||
curtain_params.width = output->width;
|
||||
curtain_params.height = output->height;
|
||||
|
||||
shoutput->background_view =
|
||||
create_solid_color_surface(shoutput->shell->compositor,
|
||||
&solid_surface,
|
||||
output->x, output->y,
|
||||
output->width,
|
||||
output->height);
|
||||
curtain_params.capture_input = true;
|
||||
|
||||
weston_surface_set_role(shoutput->background_view->surface,
|
||||
curtain_params.get_label = kiosk_shell_background_surface_get_label;
|
||||
curtain_params.surface_committed = NULL;
|
||||
curtain_params.surface_private = NULL;
|
||||
|
||||
shoutput->curtain = weston_curtain_create(ec, &curtain_params);
|
||||
|
||||
weston_surface_set_role(shoutput->curtain->view->surface,
|
||||
"kiosk-shell-background", NULL, 0);
|
||||
|
||||
weston_layer_entry_insert(&shell->background_layer.view_list,
|
||||
&shoutput->background_view->layer_link);
|
||||
&shoutput->curtain->view->layer_link);
|
||||
|
||||
shoutput->background_view->is_mapped = true;
|
||||
shoutput->background_view->surface->is_mapped = true;
|
||||
shoutput->background_view->surface->output = output;
|
||||
weston_view_set_output(shoutput->background_view, output);
|
||||
shoutput->curtain->view->is_mapped = true;
|
||||
shoutput->curtain->view->surface->output = output;
|
||||
weston_view_set_output(shoutput->curtain->view, output);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -530,8 +534,8 @@ kiosk_shell_output_destroy(struct kiosk_shell_output *shoutput)
|
||||
shoutput->output = NULL;
|
||||
shoutput->output_destroy_listener.notify = NULL;
|
||||
|
||||
if (shoutput->background_view)
|
||||
weston_surface_destroy(shoutput->background_view->surface);
|
||||
if (shoutput->curtain)
|
||||
weston_curtain_destroy(shoutput->curtain);
|
||||
|
||||
wl_list_remove(&shoutput->output_destroy_listener.link);
|
||||
wl_list_remove(&shoutput->link);
|
||||
@@ -795,7 +799,7 @@ desktop_surface_committed(struct weston_desktop_surface *desktop_surface,
|
||||
struct kiosk_shell_seat *kiosk_seat;
|
||||
|
||||
shsurf->view->is_mapped = true;
|
||||
surface->is_mapped = true;
|
||||
weston_surface_map(surface);
|
||||
|
||||
kiosk_seat = get_kiosk_shell_seat(seat);
|
||||
if (seat && kiosk_seat)
|
||||
@@ -948,6 +952,17 @@ desktop_surface_set_xwayland_position(struct weston_desktop_surface *desktop_sur
|
||||
shsurf->xwayland.is_set = true;
|
||||
}
|
||||
|
||||
static void
|
||||
desktop_surface_get_position(struct weston_desktop_surface *desktop_surface,
|
||||
int32_t *x, int32_t *y, void *shell)
|
||||
{
|
||||
struct kiosk_shell_surface *shsurf =
|
||||
weston_desktop_surface_get_user_data(desktop_surface);
|
||||
|
||||
*x = shsurf->view->geometry.x;
|
||||
*y = shsurf->view->geometry.y;
|
||||
}
|
||||
|
||||
static const struct weston_desktop_api kiosk_shell_desktop_api = {
|
||||
.struct_size = sizeof(struct weston_desktop_api),
|
||||
.surface_added = desktop_surface_added,
|
||||
@@ -962,6 +977,7 @@ static const struct weston_desktop_api kiosk_shell_desktop_api = {
|
||||
.ping_timeout = desktop_surface_ping_timeout,
|
||||
.pong = desktop_surface_pong,
|
||||
.set_xwayland_position = desktop_surface_set_xwayland_position,
|
||||
.get_position = desktop_surface_get_position,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1049,6 +1065,10 @@ kiosk_shell_touch_to_activate_binding(struct weston_touch *touch,
|
||||
static void
|
||||
kiosk_shell_add_bindings(struct kiosk_shell *shell)
|
||||
{
|
||||
uint32_t mod = 0;
|
||||
|
||||
mod = weston_shell_get_binding_modifier(shell->config, MODIFIER_SUPER);
|
||||
|
||||
weston_compositor_add_button_binding(shell->compositor, BTN_LEFT, 0,
|
||||
kiosk_shell_click_to_activate_binding,
|
||||
shell);
|
||||
@@ -1058,6 +1078,8 @@ kiosk_shell_add_bindings(struct kiosk_shell *shell)
|
||||
weston_compositor_add_touch_binding(shell->compositor, 0,
|
||||
kiosk_shell_touch_to_activate_binding,
|
||||
shell);
|
||||
|
||||
weston_install_debug_key_binding(shell->compositor, mod);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -88,7 +88,7 @@ struct kiosk_shell_seat {
|
||||
struct kiosk_shell_output {
|
||||
struct weston_output *output;
|
||||
struct wl_listener output_destroy_listener;
|
||||
struct weston_view *background_view;
|
||||
struct weston_curtain *curtain;
|
||||
|
||||
struct kiosk_shell *shell;
|
||||
struct wl_list link;
|
||||
|
||||
@@ -2,9 +2,6 @@ if get_option('shell-kiosk')
|
||||
srcs_shell_kiosk = [
|
||||
'kiosk-shell.c',
|
||||
'kiosk-shell-grab.c',
|
||||
'../shared/shell-utils.c',
|
||||
weston_desktop_shell_server_protocol_h,
|
||||
weston_desktop_shell_protocol_c,
|
||||
input_method_unstable_v1_server_protocol_h,
|
||||
input_method_unstable_v1_protocol_c,
|
||||
]
|
||||
@@ -12,8 +9,8 @@ if get_option('shell-kiosk')
|
||||
dep_libm,
|
||||
dep_libexec_weston,
|
||||
dep_libshared,
|
||||
dep_lib_desktop,
|
||||
dep_libweston_public,
|
||||
dep_shell_utils,
|
||||
]
|
||||
plugin_shell_kiosk = shared_library(
|
||||
'kiosk-shell',
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
srcs_libdesktop = [
|
||||
'libweston-desktop.c',
|
||||
'client.c',
|
||||
'seat.c',
|
||||
'surface.c',
|
||||
'xwayland.c',
|
||||
'wl-shell.c',
|
||||
'xdg-shell.c',
|
||||
'xdg-shell-v6.c',
|
||||
xdg_shell_unstable_v6_server_protocol_h,
|
||||
xdg_shell_unstable_v6_protocol_c,
|
||||
xdg_shell_server_protocol_h,
|
||||
xdg_shell_protocol_c,
|
||||
]
|
||||
lib_desktop = shared_library(
|
||||
'weston-desktop-@0@'.format(libweston_major),
|
||||
srcs_libdesktop,
|
||||
include_directories: common_inc,
|
||||
install: true,
|
||||
version: '0.0.@0@'.format(libweston_revision),
|
||||
dependencies: dep_libweston_public
|
||||
)
|
||||
dep_lib_desktop = declare_dependency(
|
||||
link_with: lib_desktop,
|
||||
dependencies: dep_libweston_public
|
||||
)
|
||||
|
||||
pkgconfig.generate(
|
||||
lib_desktop,
|
||||
filebase: 'libweston-desktop-@0@'.format(libweston_major),
|
||||
name: 'libweston-desktop',
|
||||
version: version_weston,
|
||||
description: 'Desktop shells abstraction library for libweston compositors',
|
||||
requires_private: [ lib_weston, dep_wayland_server ],
|
||||
subdirs: dir_include_libweston
|
||||
)
|
||||
@@ -1,497 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2010-2012 Intel Corporation
|
||||
* Copyright © 2011-2012 Collabora, Ltd.
|
||||
* Copyright © 2013 Raspberry Pi Foundation
|
||||
* Copyright © 2016 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/zalloc.h>
|
||||
|
||||
#include <libweston-desktop/libweston-desktop.h>
|
||||
#include "internal.h"
|
||||
|
||||
#define WD_WL_SHELL_PROTOCOL_VERSION 1
|
||||
|
||||
enum weston_desktop_wl_shell_surface_state {
|
||||
NONE,
|
||||
TOPLEVEL,
|
||||
MAXIMIZED,
|
||||
FULLSCREEN,
|
||||
TRANSIENT,
|
||||
POPUP,
|
||||
};
|
||||
|
||||
struct weston_desktop_wl_shell_surface {
|
||||
struct wl_resource *resource;
|
||||
struct weston_desktop *desktop;
|
||||
struct wl_display *display;
|
||||
struct weston_desktop_surface *surface;
|
||||
struct weston_desktop_surface *parent;
|
||||
bool added;
|
||||
struct weston_desktop_seat *popup_seat;
|
||||
enum weston_desktop_wl_shell_surface_state state;
|
||||
struct wl_listener wl_surface_resource_destroy_listener;
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_set_size(struct weston_desktop_surface *dsurface,
|
||||
void *user_data,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
struct weston_surface *wsurface =
|
||||
weston_desktop_surface_get_surface(surface->surface);
|
||||
|
||||
if ((wsurface->width == width && wsurface->height == height) ||
|
||||
(width == 0 && height == 0))
|
||||
return;
|
||||
|
||||
wl_shell_surface_send_configure(surface->resource,
|
||||
WL_SHELL_SURFACE_RESIZE_NONE,
|
||||
width, height);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_maybe_ungrab(struct weston_desktop_wl_shell_surface *surface)
|
||||
{
|
||||
if (surface->state != POPUP ||
|
||||
!weston_desktop_surface_get_grab(surface->surface))
|
||||
return;
|
||||
|
||||
weston_desktop_surface_popup_ungrab(surface->surface,
|
||||
surface->popup_seat);
|
||||
surface->popup_seat = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_committed(struct weston_desktop_surface *dsurface,
|
||||
void *user_data,
|
||||
int32_t sx, int32_t sy)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
struct weston_surface *wsurface =
|
||||
weston_desktop_surface_get_surface(dsurface);
|
||||
|
||||
if (wsurface->buffer_ref.buffer == NULL)
|
||||
weston_desktop_wl_shell_surface_maybe_ungrab(surface);
|
||||
|
||||
if (surface->added)
|
||||
weston_desktop_api_committed(surface->desktop, surface->surface,
|
||||
sx, sy);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_ping(struct weston_desktop_surface *dsurface,
|
||||
uint32_t serial, void *user_data)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
|
||||
wl_shell_surface_send_ping(surface->resource, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_close(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
|
||||
if (surface->state == POPUP)
|
||||
wl_shell_surface_send_popup_done(surface->resource);
|
||||
}
|
||||
|
||||
static bool
|
||||
weston_desktop_wl_shell_surface_get_maximized(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
|
||||
return surface->state == MAXIMIZED;
|
||||
}
|
||||
|
||||
static bool
|
||||
weston_desktop_wl_shell_surface_get_fullscreen(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
|
||||
return surface->state == FULLSCREEN;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_change_state(struct weston_desktop_wl_shell_surface *surface,
|
||||
enum weston_desktop_wl_shell_surface_state state,
|
||||
struct weston_desktop_surface *parent,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
bool to_add = (parent == NULL);
|
||||
|
||||
assert(state != NONE);
|
||||
|
||||
if (to_add && surface->added) {
|
||||
surface->state = state;
|
||||
return;
|
||||
}
|
||||
|
||||
if (surface->state != state) {
|
||||
if (surface->state == POPUP)
|
||||
weston_desktop_wl_shell_surface_maybe_ungrab(surface);
|
||||
|
||||
if (to_add) {
|
||||
weston_desktop_surface_unset_relative_to(surface->surface);
|
||||
weston_desktop_api_surface_added(surface->desktop,
|
||||
surface->surface);
|
||||
} else if (surface->added) {
|
||||
weston_desktop_api_surface_removed(surface->desktop,
|
||||
surface->surface);
|
||||
}
|
||||
|
||||
surface->state = state;
|
||||
surface->added = to_add;
|
||||
}
|
||||
|
||||
if (parent != NULL)
|
||||
weston_desktop_surface_set_relative_to(surface->surface, parent,
|
||||
x, y, false);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_destroy(struct weston_desktop_surface *dsurface,
|
||||
void *user_data)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface = user_data;
|
||||
|
||||
wl_list_remove(&surface->wl_surface_resource_destroy_listener.link);
|
||||
|
||||
weston_desktop_wl_shell_surface_maybe_ungrab(surface);
|
||||
weston_desktop_surface_unset_relative_to(surface->surface);
|
||||
if (surface->added)
|
||||
weston_desktop_api_surface_removed(surface->desktop,
|
||||
surface->surface);
|
||||
|
||||
free(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_pong(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct weston_desktop_surface *surface = wl_resource_get_user_data(resource);
|
||||
|
||||
weston_desktop_client_pong(weston_desktop_surface_get_client(surface), serial);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_move(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *seat_resource,
|
||||
uint32_t serial)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_seat *seat =
|
||||
wl_resource_get_user_data(seat_resource);
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
|
||||
if (seat == NULL)
|
||||
return;
|
||||
|
||||
weston_desktop_api_move(surface->desktop, dsurface, seat, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_resize(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *seat_resource,
|
||||
uint32_t serial,
|
||||
enum wl_shell_surface_resize edges)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
enum weston_desktop_surface_edge surf_edges =
|
||||
(enum weston_desktop_surface_edge) edges;
|
||||
|
||||
if (seat == NULL)
|
||||
return;
|
||||
|
||||
weston_desktop_api_resize(surface->desktop, dsurface, seat, serial, surf_edges);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_toplevel(struct wl_client *wl_client,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
|
||||
weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL, 0, 0);
|
||||
if (surface->parent == NULL)
|
||||
return;
|
||||
surface->parent = NULL;
|
||||
weston_desktop_api_set_parent(surface->desktop, surface->surface, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_transient(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *parent_resource,
|
||||
int32_t x, int32_t y,
|
||||
enum wl_shell_surface_transient flags)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_surface *wparent =
|
||||
wl_resource_get_user_data(parent_resource);
|
||||
struct weston_desktop_surface *parent;
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
|
||||
if (!weston_surface_is_desktop_surface(wparent))
|
||||
return;
|
||||
|
||||
parent = weston_surface_get_desktop_surface(wparent);
|
||||
if (flags & WL_SHELL_SURFACE_TRANSIENT_INACTIVE) {
|
||||
weston_desktop_wl_shell_change_state(surface, TRANSIENT, parent,
|
||||
x, y);
|
||||
} else {
|
||||
weston_desktop_wl_shell_change_state(surface, TOPLEVEL, NULL,
|
||||
0, 0);
|
||||
surface->parent = parent;
|
||||
weston_desktop_api_set_parent(surface->desktop,
|
||||
surface->surface, parent);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_fullscreen(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
enum wl_shell_surface_fullscreen_method method,
|
||||
uint32_t framerate,
|
||||
struct wl_resource *output_resource)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
struct weston_output *output = NULL;
|
||||
|
||||
if (output_resource != NULL)
|
||||
output = weston_head_from_resource(output_resource)->output;
|
||||
|
||||
weston_desktop_wl_shell_change_state(surface, FULLSCREEN, NULL, 0, 0);
|
||||
weston_desktop_api_fullscreen_requested(surface->desktop, dsurface,
|
||||
true, output);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_popup(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *seat_resource,
|
||||
uint32_t serial,
|
||||
struct wl_resource *parent_resource,
|
||||
int32_t x, int32_t y,
|
||||
enum wl_shell_surface_transient flags)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_seat *wseat = wl_resource_get_user_data(seat_resource);
|
||||
struct weston_desktop_seat *seat = weston_desktop_seat_from_seat(wseat);
|
||||
struct weston_surface *parent =
|
||||
wl_resource_get_user_data(parent_resource);
|
||||
struct weston_desktop_surface *parent_surface;
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
|
||||
/* Check that if we have a valid wseat we also got a valid desktop seat */
|
||||
if (wseat != NULL && seat == NULL) {
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!weston_surface_is_desktop_surface(parent))
|
||||
return;
|
||||
|
||||
parent_surface = weston_surface_get_desktop_surface(parent);
|
||||
|
||||
weston_desktop_wl_shell_change_state(surface, POPUP,
|
||||
parent_surface, x, y);
|
||||
weston_desktop_surface_popup_grab(surface->surface, seat, serial);
|
||||
surface->popup_seat = seat;
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_maximized(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *output_resource)
|
||||
{
|
||||
struct weston_desktop_surface *dsurface =
|
||||
wl_resource_get_user_data(resource);
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
weston_desktop_surface_get_implementation_data(dsurface);
|
||||
|
||||
weston_desktop_wl_shell_change_state(surface, MAXIMIZED, NULL, 0, 0);
|
||||
weston_desktop_api_maximized_requested(surface->desktop, dsurface, true);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_title(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
const char *title)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_resource_get_user_data(resource);
|
||||
|
||||
weston_desktop_surface_set_title(surface, title);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_surface_protocol_set_class(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
const char *class_)
|
||||
{
|
||||
struct weston_desktop_surface *surface =
|
||||
wl_resource_get_user_data(resource);
|
||||
|
||||
weston_desktop_surface_set_app_id(surface, class_);
|
||||
}
|
||||
|
||||
|
||||
static const struct wl_shell_surface_interface weston_desktop_wl_shell_surface_implementation = {
|
||||
.pong = weston_desktop_wl_shell_surface_protocol_pong,
|
||||
.move = weston_desktop_wl_shell_surface_protocol_move,
|
||||
.resize = weston_desktop_wl_shell_surface_protocol_resize,
|
||||
.set_toplevel = weston_desktop_wl_shell_surface_protocol_set_toplevel,
|
||||
.set_transient = weston_desktop_wl_shell_surface_protocol_set_transient,
|
||||
.set_fullscreen = weston_desktop_wl_shell_surface_protocol_set_fullscreen,
|
||||
.set_popup = weston_desktop_wl_shell_surface_protocol_set_popup,
|
||||
.set_maximized = weston_desktop_wl_shell_surface_protocol_set_maximized,
|
||||
.set_title = weston_desktop_wl_shell_surface_protocol_set_title,
|
||||
.set_class = weston_desktop_wl_shell_surface_protocol_set_class,
|
||||
};
|
||||
|
||||
static const struct weston_desktop_surface_implementation weston_desktop_wl_shell_surface_internal_implementation = {
|
||||
.set_size = weston_desktop_wl_shell_surface_set_size,
|
||||
.committed = weston_desktop_wl_shell_surface_committed,
|
||||
.ping = weston_desktop_wl_shell_surface_ping,
|
||||
.close = weston_desktop_wl_shell_surface_close,
|
||||
|
||||
.get_maximized = weston_desktop_wl_shell_surface_get_maximized,
|
||||
.get_fullscreen = weston_desktop_wl_shell_surface_get_fullscreen,
|
||||
|
||||
.destroy = weston_desktop_wl_shell_surface_destroy,
|
||||
};
|
||||
|
||||
static void
|
||||
wl_surface_resource_destroyed(struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
struct weston_desktop_wl_shell_surface *surface =
|
||||
wl_container_of(listener, surface,
|
||||
wl_surface_resource_destroy_listener);
|
||||
|
||||
/* the wl_shell_surface spec says that wl_shell_surfaces are to be
|
||||
* destroyed automatically when the wl_surface is destroyed. */
|
||||
weston_desktop_surface_destroy(surface->surface);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_protocol_get_shell_surface(struct wl_client *wl_client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t id,
|
||||
struct wl_resource *surface_resource)
|
||||
{
|
||||
struct weston_desktop_client *client = wl_resource_get_user_data(resource);
|
||||
struct weston_surface *wsurface = wl_resource_get_user_data(surface_resource);
|
||||
struct weston_desktop_wl_shell_surface *surface;
|
||||
|
||||
|
||||
if (weston_surface_set_role(wsurface, "wl_shell_surface", resource, WL_SHELL_ERROR_ROLE) < 0)
|
||||
return;
|
||||
|
||||
surface = zalloc(sizeof(struct weston_desktop_wl_shell_surface));
|
||||
if (surface == NULL) {
|
||||
wl_client_post_no_memory(wl_client);
|
||||
return;
|
||||
}
|
||||
|
||||
surface->desktop = weston_desktop_client_get_desktop(client);
|
||||
surface->display = weston_desktop_get_display(surface->desktop);
|
||||
|
||||
surface->surface =
|
||||
weston_desktop_surface_create(surface->desktop, client, wsurface,
|
||||
&weston_desktop_wl_shell_surface_internal_implementation,
|
||||
surface);
|
||||
if (surface->surface == NULL) {
|
||||
free(surface);
|
||||
return;
|
||||
}
|
||||
|
||||
surface->wl_surface_resource_destroy_listener.notify =
|
||||
wl_surface_resource_destroyed;
|
||||
wl_resource_add_destroy_listener(wsurface->resource,
|
||||
&surface->wl_surface_resource_destroy_listener);
|
||||
|
||||
surface->resource =
|
||||
weston_desktop_surface_add_resource(surface->surface,
|
||||
&wl_shell_surface_interface,
|
||||
&weston_desktop_wl_shell_surface_implementation,
|
||||
id, NULL);
|
||||
}
|
||||
|
||||
|
||||
static const struct wl_shell_interface weston_desktop_wl_shell_implementation = {
|
||||
.get_shell_surface = weston_desktop_wl_shell_protocol_get_shell_surface,
|
||||
};
|
||||
|
||||
static void
|
||||
weston_desktop_wl_shell_bind(struct wl_client *client, void *data,
|
||||
uint32_t version, uint32_t id)
|
||||
{
|
||||
struct weston_desktop *desktop = data;
|
||||
|
||||
weston_desktop_client_create(desktop, client, NULL, &wl_shell_interface,
|
||||
&weston_desktop_wl_shell_implementation,
|
||||
version, id);
|
||||
}
|
||||
|
||||
struct wl_global *
|
||||
weston_desktop_wl_shell_create(struct weston_desktop *desktop,
|
||||
struct wl_display *display)
|
||||
{
|
||||
return wl_global_create(display,
|
||||
&wl_shell_interface,
|
||||
WD_WL_SHELL_PROTOCOL_VERSION, desktop,
|
||||
weston_desktop_wl_shell_bind);
|
||||
}
|
||||
@@ -118,8 +118,9 @@ drm_backend_create_gl_renderer(struct drm_backend *b)
|
||||
int
|
||||
init_egl(struct drm_backend *b)
|
||||
{
|
||||
b->gbm = create_gbm_device(b->drm.fd);
|
||||
struct drm_device *device = b->drm;
|
||||
|
||||
b->gbm = create_gbm_device(device->drm.fd);
|
||||
if (!b->gbm)
|
||||
return -1;
|
||||
|
||||
@@ -145,6 +146,7 @@ static void drm_output_fini_cursor_egl(struct drm_output *output)
|
||||
static int
|
||||
drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
|
||||
{
|
||||
struct drm_device *device = output->device;
|
||||
unsigned int i;
|
||||
|
||||
/* No point creating cursors if we don't have a plane for them. */
|
||||
@@ -154,14 +156,14 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
|
||||
for (i = 0; i < ARRAY_LENGTH(output->gbm_cursor_fb); i++) {
|
||||
struct gbm_bo *bo;
|
||||
|
||||
bo = gbm_bo_create(b->gbm, b->cursor_width, b->cursor_height,
|
||||
bo = gbm_bo_create(b->gbm, device->cursor_width, device->cursor_height,
|
||||
GBM_FORMAT_ARGB8888,
|
||||
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
|
||||
if (!bo)
|
||||
goto err;
|
||||
|
||||
output->gbm_cursor_fb[i] =
|
||||
drm_fb_get_from_bo(bo, b, false, BUFFER_CURSOR);
|
||||
drm_fb_get_from_bo(bo, device, false, BUFFER_CURSOR);
|
||||
if (!output->gbm_cursor_fb[i]) {
|
||||
gbm_bo_destroy(bo);
|
||||
goto err;
|
||||
@@ -173,7 +175,7 @@ drm_output_init_cursor_egl(struct drm_output *output, struct drm_backend *b)
|
||||
|
||||
err:
|
||||
weston_log("cursor buffers unavailable, using gl cursors\n");
|
||||
b->cursors_are_broken = true;
|
||||
device->cursors_are_broken = true;
|
||||
drm_output_fini_cursor_egl(output);
|
||||
return -1;
|
||||
}
|
||||
@@ -183,6 +185,7 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
|
||||
{
|
||||
struct weston_mode *mode = output->base.current_mode;
|
||||
struct drm_plane *plane = output->scanout_plane;
|
||||
const struct pixel_format_info *pixel_format;
|
||||
struct weston_drm_format *fmt;
|
||||
const uint64_t *modifiers;
|
||||
unsigned int num_modifiers;
|
||||
@@ -190,8 +193,15 @@ create_gbm_surface(struct gbm_device *gbm, struct drm_output *output)
|
||||
fmt = weston_drm_format_array_find_format(&plane->formats,
|
||||
output->gbm_format);
|
||||
if (!fmt) {
|
||||
pixel_format = pixel_format_get_info(output->gbm_format);
|
||||
if (pixel_format)
|
||||
weston_log("format %s not supported by output %s\n",
|
||||
pixel_format->drm_format_name,
|
||||
output->base.name);
|
||||
else
|
||||
weston_log("format 0x%x not supported by output %s\n",
|
||||
output->gbm_format, output->base.name);
|
||||
output->gbm_format,
|
||||
output->base.name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -280,7 +290,7 @@ struct drm_fb *
|
||||
drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct gbm_bo *bo;
|
||||
struct drm_fb *ret;
|
||||
|
||||
@@ -295,7 +305,7 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
}
|
||||
|
||||
/* The renderer always produces an opaque image. */
|
||||
ret = drm_fb_get_from_bo(bo, b, true, BUFFER_GBM_SURFACE);
|
||||
ret = drm_fb_get_from_bo(bo, device, true, BUFFER_GBM_SURFACE);
|
||||
if (!ret) {
|
||||
weston_log("failed to get drm_fb for bo\n");
|
||||
gbm_surface_release_buffer(output->gbm_surface, bo);
|
||||
@@ -305,68 +315,3 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
switch_to_gl_renderer(struct drm_backend *b)
|
||||
{
|
||||
struct drm_output *output;
|
||||
bool dmabuf_support_inited;
|
||||
bool linux_explicit_sync_inited;
|
||||
|
||||
if (!b->use_pixman)
|
||||
return;
|
||||
|
||||
dmabuf_support_inited = !!b->compositor->renderer->import_dmabuf;
|
||||
linux_explicit_sync_inited =
|
||||
b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC;
|
||||
|
||||
weston_log("Switching to GL renderer\n");
|
||||
|
||||
b->gbm = create_gbm_device(b->drm.fd);
|
||||
if (!b->gbm) {
|
||||
weston_log("Failed to create gbm device. "
|
||||
"Aborting renderer switch\n");
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_for_each(output, &b->compositor->output_list, base.link)
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
|
||||
b->compositor->renderer->destroy(b->compositor);
|
||||
|
||||
if (drm_backend_create_gl_renderer(b) < 0) {
|
||||
gbm_device_destroy(b->gbm);
|
||||
weston_log("Failed to create GL renderer. Quitting.\n");
|
||||
/* FIXME: we need a function to shutdown cleanly */
|
||||
assert(0);
|
||||
}
|
||||
|
||||
wl_list_for_each(output, &b->compositor->output_list, base.link)
|
||||
drm_output_init_egl(output, b);
|
||||
|
||||
b->use_pixman = 0;
|
||||
|
||||
if (!dmabuf_support_inited && b->compositor->renderer->import_dmabuf) {
|
||||
if (linux_dmabuf_setup(b->compositor) < 0)
|
||||
weston_log("Error: initializing dmabuf "
|
||||
"support failed.\n");
|
||||
}
|
||||
|
||||
if (!linux_explicit_sync_inited &&
|
||||
(b->compositor->capabilities & WESTON_CAP_EXPLICIT_SYNC)) {
|
||||
if (linux_explicit_synchronization_setup(b->compositor) < 0)
|
||||
weston_log("Error: initializing explicit "
|
||||
" synchronization support failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
renderer_switch_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data)
|
||||
{
|
||||
struct drm_backend *b =
|
||||
to_drm_backend(keyboard->seat->compositor);
|
||||
|
||||
switch_to_gl_renderer(b);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +186,8 @@ enum wdrm_connector_property {
|
||||
WDRM_CONNECTOR_CONTENT_PROTECTION,
|
||||
WDRM_CONNECTOR_HDCP_CONTENT_TYPE,
|
||||
WDRM_CONNECTOR_PANEL_ORIENTATION,
|
||||
WDRM_CONNECTOR_HDR_OUTPUT_METADATA,
|
||||
WDRM_CONNECTOR_MAX_BPC,
|
||||
WDRM_CONNECTOR__COUNT
|
||||
};
|
||||
|
||||
@@ -232,11 +234,20 @@ enum wdrm_crtc_property {
|
||||
*/
|
||||
enum try_view_on_plane_failure_reasons {
|
||||
FAILURE_REASONS_NONE = 0,
|
||||
FAILURE_REASONS_FORCE_RENDERER = (1 << 0),
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = (1 << 1),
|
||||
FAILURE_REASONS_DMABUF_MODIFIER_INVALID = (1 << 2),
|
||||
FAILURE_REASONS_ADD_FB_FAILED = (1 << 3),
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED = (1 << 4)
|
||||
FAILURE_REASONS_FORCE_RENDERER = 1 << 0,
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE = 1 << 1,
|
||||
FAILURE_REASONS_DMABUF_MODIFIER_INVALID = 1 << 2,
|
||||
FAILURE_REASONS_ADD_FB_FAILED = 1 << 3,
|
||||
FAILURE_REASONS_NO_PLANES_AVAILABLE = 1 << 4,
|
||||
FAILURE_REASONS_PLANES_REJECTED = 1 << 5,
|
||||
FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION = 1 << 6,
|
||||
FAILURE_REASONS_INCOMPATIBLE_TRANSFORM = 1 << 7,
|
||||
FAILURE_REASONS_NO_BUFFER = 1 << 8,
|
||||
FAILURE_REASONS_BUFFER_TYPE = 1 << 9,
|
||||
FAILURE_REASONS_GLOBAL_ALPHA = 1 << 10,
|
||||
FAILURE_REASONS_NO_GBM = 1 << 11,
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED = 1 << 12,
|
||||
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED = 1 << 13,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -249,6 +260,48 @@ enum actions_needed_dmabuf_feedback {
|
||||
ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1),
|
||||
};
|
||||
|
||||
struct drm_device {
|
||||
struct drm_backend *backend;
|
||||
|
||||
struct {
|
||||
int id;
|
||||
int fd;
|
||||
char *filename;
|
||||
dev_t devnum;
|
||||
} drm;
|
||||
|
||||
/* drm_crtc::link */
|
||||
struct wl_list crtc_list;
|
||||
|
||||
struct wl_list plane_list;
|
||||
|
||||
/* drm_writeback::link */
|
||||
struct wl_list writeback_connector_list;
|
||||
|
||||
bool state_invalid;
|
||||
|
||||
bool atomic_modeset;
|
||||
|
||||
bool aspect_ratio_supported;
|
||||
|
||||
int32_t cursor_width;
|
||||
int32_t cursor_height;
|
||||
|
||||
bool cursors_are_broken;
|
||||
bool sprites_are_broken;
|
||||
|
||||
void *repaint_data;
|
||||
|
||||
bool fb_modifiers;
|
||||
|
||||
/* we need these parameters in order to not fail drmModeAddFB2()
|
||||
* due to out of bounds dimensions, and then mistakenly set
|
||||
* sprites_are_broken:
|
||||
*/
|
||||
int min_width, max_width;
|
||||
int min_height, max_height;
|
||||
};
|
||||
|
||||
struct drm_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
@@ -259,57 +312,20 @@ struct drm_backend {
|
||||
struct udev_monitor *udev_monitor;
|
||||
struct wl_event_source *udev_drm_source;
|
||||
|
||||
struct {
|
||||
int id;
|
||||
int fd;
|
||||
char *filename;
|
||||
dev_t devnum;
|
||||
} drm;
|
||||
struct drm_device *drm;
|
||||
struct gbm_device *gbm;
|
||||
struct wl_listener session_listener;
|
||||
uint32_t gbm_format;
|
||||
|
||||
/* we need these parameters in order to not fail drmModeAddFB2()
|
||||
* due to out of bounds dimensions, and then mistakenly set
|
||||
* sprites_are_broken:
|
||||
*/
|
||||
int min_width, max_width;
|
||||
int min_height, max_height;
|
||||
|
||||
struct wl_list plane_list;
|
||||
uint32_t next_plane_idx;
|
||||
|
||||
void *repaint_data;
|
||||
|
||||
bool state_invalid;
|
||||
|
||||
/* drm_crtc::link */
|
||||
struct wl_list crtc_list;
|
||||
|
||||
/* drm_writeback::link */
|
||||
struct wl_list writeback_connector_list;
|
||||
|
||||
bool sprites_are_broken;
|
||||
bool cursors_are_broken;
|
||||
|
||||
bool atomic_modeset;
|
||||
|
||||
bool use_pixman;
|
||||
bool use_pixman_shadow;
|
||||
|
||||
struct udev_input input;
|
||||
|
||||
int32_t cursor_width;
|
||||
int32_t cursor_height;
|
||||
|
||||
uint32_t pageflip_timeout;
|
||||
|
||||
bool shutting_down;
|
||||
|
||||
bool aspect_ratio_supported;
|
||||
|
||||
bool fb_modifiers;
|
||||
|
||||
struct weston_log_scope *debug;
|
||||
};
|
||||
|
||||
@@ -373,7 +389,7 @@ struct drm_edid {
|
||||
* output state will complete and be retired separately.
|
||||
*/
|
||||
struct drm_pending_state {
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
struct wl_list output_list;
|
||||
};
|
||||
|
||||
@@ -396,16 +412,6 @@ struct drm_output_state {
|
||||
struct wl_list plane_list;
|
||||
};
|
||||
|
||||
/**
|
||||
* An instance of this class is created each time we believe we have a plane
|
||||
* suitable to be used by a view as a direct scan-out. The list is initialized
|
||||
* and populated locally.
|
||||
*/
|
||||
struct drm_plane_zpos {
|
||||
struct drm_plane *plane;
|
||||
struct wl_list link; /**< :candidate_plane_zpos_list */
|
||||
};
|
||||
|
||||
/**
|
||||
* Plane state holds the dynamic state for a plane: where it is positioned,
|
||||
* and which buffer it is currently displaying.
|
||||
@@ -461,7 +467,7 @@ struct drm_plane_state {
|
||||
struct drm_plane {
|
||||
struct weston_plane base;
|
||||
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
|
||||
enum wdrm_plane_type type;
|
||||
|
||||
@@ -483,7 +489,7 @@ struct drm_plane {
|
||||
};
|
||||
|
||||
struct drm_connector {
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
|
||||
drmModeConnector *conn;
|
||||
uint32_t connector_id;
|
||||
@@ -495,16 +501,15 @@ struct drm_connector {
|
||||
};
|
||||
|
||||
struct drm_writeback {
|
||||
/* drm_backend::writeback_connector_list */
|
||||
/* drm_device::writeback_connector_list */
|
||||
struct wl_list link;
|
||||
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
struct drm_connector connector;
|
||||
};
|
||||
|
||||
struct drm_head {
|
||||
struct weston_head base;
|
||||
struct drm_backend *backend;
|
||||
struct drm_connector connector;
|
||||
|
||||
struct drm_edid edid;
|
||||
@@ -512,13 +517,17 @@ struct drm_head {
|
||||
struct backlight *backlight;
|
||||
|
||||
drmModeModeInfo inherited_mode; /**< Original mode on the connector */
|
||||
uint32_t inherited_max_bpc; /**< Original max_bpc on the connector */
|
||||
uint32_t inherited_crtc_id; /**< Original CRTC assignment */
|
||||
|
||||
/* drm_output::disable_head */
|
||||
struct wl_list disable_head_link;
|
||||
};
|
||||
|
||||
struct drm_crtc {
|
||||
/* drm_backend::crtc_list */
|
||||
/* drm_device::crtc_list */
|
||||
struct wl_list link;
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
|
||||
/* The output driven by the CRTC */
|
||||
struct drm_output *output;
|
||||
@@ -532,14 +541,18 @@ struct drm_crtc {
|
||||
|
||||
struct drm_output {
|
||||
struct weston_output base;
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
/* drm_head::disable_head_link */
|
||||
struct wl_list disable_head;
|
||||
|
||||
bool page_flip_pending;
|
||||
bool atomic_complete_pending;
|
||||
bool destroy_pending;
|
||||
bool disable_pending;
|
||||
bool dpms_off_pending;
|
||||
bool mode_switch_pending;
|
||||
|
||||
uint32_t gbm_cursor_handle[2];
|
||||
struct drm_fb *gbm_cursor_fb[2];
|
||||
@@ -552,6 +565,11 @@ struct drm_output {
|
||||
uint32_t gbm_format;
|
||||
uint32_t gbm_bo_flags;
|
||||
|
||||
uint32_t hdr_output_metadata_blob_id;
|
||||
uint64_t ackd_color_outcome_serial;
|
||||
|
||||
unsigned max_bpc;
|
||||
|
||||
/* Plane being displayed directly on the CRTC */
|
||||
struct drm_plane *scanout_plane;
|
||||
|
||||
@@ -572,19 +590,36 @@ struct drm_output {
|
||||
struct wl_event_source *pageflip_timer;
|
||||
|
||||
bool virtual;
|
||||
void (*virtual_destroy)(struct weston_output *base);
|
||||
|
||||
submit_frame_cb virtual_submit_frame;
|
||||
};
|
||||
|
||||
void
|
||||
drm_head_destroy(struct weston_head *head_base);
|
||||
|
||||
static inline struct drm_head *
|
||||
to_drm_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != drm_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct drm_head, base);
|
||||
}
|
||||
|
||||
void
|
||||
drm_output_destroy(struct weston_output *output_base);
|
||||
void
|
||||
drm_virtual_output_destroy(struct weston_output *output_base);
|
||||
|
||||
static inline struct drm_output *
|
||||
to_drm_output(struct weston_output *base)
|
||||
{
|
||||
if (
|
||||
#ifdef BUILD_DRM_VIRTUAL
|
||||
base->destroy != drm_virtual_output_destroy &&
|
||||
#endif
|
||||
base->destroy != drm_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct drm_output, base);
|
||||
}
|
||||
|
||||
@@ -617,7 +652,7 @@ drm_output_get_plane_type_name(struct drm_plane *p)
|
||||
}
|
||||
|
||||
struct drm_crtc *
|
||||
drm_crtc_find(struct drm_backend *b, uint32_t crtc_id);
|
||||
drm_crtc_find(struct drm_device *device, uint32_t crtc_id);
|
||||
|
||||
struct drm_head *
|
||||
drm_head_find_by_connector(struct drm_backend *backend, uint32_t connector_id);
|
||||
@@ -642,7 +677,7 @@ drm_view_transform_supported(struct weston_view *ev, struct weston_output *outpu
|
||||
}
|
||||
|
||||
int
|
||||
drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode);
|
||||
drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode);
|
||||
|
||||
struct drm_mode *
|
||||
drm_output_choose_mode(struct drm_output *output,
|
||||
@@ -651,7 +686,7 @@ void
|
||||
update_head_from_connector(struct drm_head *head);
|
||||
|
||||
void
|
||||
drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list);
|
||||
drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list);
|
||||
|
||||
void
|
||||
drm_output_print_modes(struct drm_output *output);
|
||||
@@ -662,7 +697,7 @@ drm_output_set_mode(struct weston_output *base,
|
||||
const char *modeline);
|
||||
|
||||
void
|
||||
drm_property_info_populate(struct drm_backend *b,
|
||||
drm_property_info_populate(struct drm_device *device,
|
||||
const struct drm_property_info *src,
|
||||
struct drm_property_info *info,
|
||||
unsigned int num_infos,
|
||||
@@ -690,7 +725,7 @@ extern const struct drm_property_info connector_props[];
|
||||
extern const struct drm_property_info crtc_props[];
|
||||
|
||||
int
|
||||
init_kms_caps(struct drm_backend *b);
|
||||
init_kms_caps(struct drm_device *device);
|
||||
|
||||
int
|
||||
drm_pending_state_test(struct drm_pending_state *pending_state);
|
||||
@@ -717,15 +752,18 @@ void
|
||||
drm_fb_unref(struct drm_fb *fb);
|
||||
|
||||
struct drm_fb *
|
||||
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
drm_fb_create_dumb(struct drm_device *device, int width, int height,
|
||||
uint32_t format);
|
||||
struct drm_fb *
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device,
|
||||
bool is_opaque, enum drm_fb_type type);
|
||||
|
||||
void
|
||||
drm_output_set_cursor_view(struct drm_output *output, struct weston_view *ev);
|
||||
|
||||
int
|
||||
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output);
|
||||
|
||||
#ifdef BUILD_DRM_GBM
|
||||
extern struct drm_fb *
|
||||
drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
@@ -749,7 +787,7 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
|
||||
#endif
|
||||
|
||||
struct drm_pending_state *
|
||||
drm_pending_state_alloc(struct drm_backend *backend);
|
||||
drm_pending_state_alloc(struct drm_device *device);
|
||||
void
|
||||
drm_pending_state_free(struct drm_pending_state *pending_state);
|
||||
struct drm_output_state *
|
||||
@@ -800,7 +838,7 @@ void
|
||||
drm_plane_reset_state(struct drm_plane *plane);
|
||||
|
||||
void
|
||||
drm_assign_planes(struct weston_output *output_base, void *repaint_data);
|
||||
drm_assign_planes(struct weston_output *output_base);
|
||||
|
||||
bool
|
||||
drm_plane_is_available(struct drm_plane *plane, struct drm_output *output);
|
||||
@@ -837,9 +875,6 @@ drm_output_fini_egl(struct drm_output *output);
|
||||
struct drm_fb *
|
||||
drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage);
|
||||
|
||||
void
|
||||
renderer_switch_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data);
|
||||
#else
|
||||
inline static int
|
||||
init_egl(struct drm_backend *b)
|
||||
@@ -864,11 +899,4 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline static void
|
||||
renderer_switch_binding(struct weston_keyboard *keyboard,
|
||||
const struct timespec *time, uint32_t key, void *data)
|
||||
{
|
||||
weston_log("Compiled without GBM/EGL support\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
* CRTC's. Also, as this is a fake CRTC, it will not try to populate props.
|
||||
*/
|
||||
static struct drm_crtc *
|
||||
drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
|
||||
drm_virtual_crtc_create(struct drm_device *device, struct drm_output *output)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
@@ -55,7 +55,7 @@ drm_virtual_crtc_create(struct drm_backend *b, struct drm_output *output)
|
||||
if (!crtc)
|
||||
return NULL;
|
||||
|
||||
crtc->backend = b;
|
||||
crtc->device = device;
|
||||
crtc->output = output;
|
||||
|
||||
crtc->crtc_id = 0;
|
||||
@@ -81,17 +81,32 @@ drm_virtual_crtc_destroy(struct drm_crtc *crtc)
|
||||
free(crtc);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
get_drm_plane_index_maximum(struct drm_device *device)
|
||||
{
|
||||
uint32_t max = 0;
|
||||
struct drm_plane *p;
|
||||
|
||||
wl_list_for_each(p, &device->plane_list, link) {
|
||||
if (p->plane_idx > max)
|
||||
max = p->plane_idx;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a drm_plane for virtual output
|
||||
*
|
||||
* Call drm_virtual_plane_destroy to clean up the plane.
|
||||
*
|
||||
* @param b DRM compositor backend
|
||||
* @param device DRM device
|
||||
* @param output Output to create internal plane for
|
||||
*/
|
||||
static struct drm_plane *
|
||||
drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
drm_virtual_plane_create(struct drm_device *device, struct drm_output *output)
|
||||
{
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane *plane;
|
||||
struct weston_drm_format *fmt;
|
||||
uint64_t mod;
|
||||
@@ -103,7 +118,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
}
|
||||
|
||||
plane->type = WDRM_PLANE_TYPE_PRIMARY;
|
||||
plane->backend = b;
|
||||
plane->device = device;
|
||||
plane->state_cur = drm_plane_state_alloc(NULL, plane);
|
||||
plane->state_cur->complete = true;
|
||||
|
||||
@@ -115,7 +130,7 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
/* If output supports linear modifier, we add it to the plane.
|
||||
* Otherwise we add DRM_FORMAT_MOD_INVALID, as explicit modifiers
|
||||
* are not supported. */
|
||||
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && b->fb_modifiers)
|
||||
if ((output->gbm_bo_flags & GBM_BO_USE_LINEAR) && device->fb_modifiers)
|
||||
mod = DRM_FORMAT_MOD_LINEAR;
|
||||
else
|
||||
mod = DRM_FORMAT_MOD_INVALID;
|
||||
@@ -124,7 +139,8 @@ drm_virtual_plane_create(struct drm_backend *b, struct drm_output *output)
|
||||
goto err;
|
||||
|
||||
weston_plane_init(&plane->base, b->compositor, 0, 0);
|
||||
wl_list_insert(&b->plane_list, &plane->link);
|
||||
plane->plane_idx = get_drm_plane_index_maximum(device) + 1;
|
||||
wl_list_insert(&device->plane_list, &plane->link);
|
||||
|
||||
return plane;
|
||||
|
||||
@@ -163,11 +179,10 @@ static int
|
||||
drm_virtual_output_submit_frame(struct drm_output *output,
|
||||
struct drm_fb *fb)
|
||||
{
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
int fd, ret;
|
||||
|
||||
assert(fb->num_planes == 1);
|
||||
ret = drmPrimeHandleToFD(b->drm.fd, fb->handles[0], DRM_CLOEXEC, &fd);
|
||||
ret = drmPrimeHandleToFD(fb->fd, fb->handles[0], DRM_CLOEXEC, &fd);
|
||||
if (ret) {
|
||||
weston_log("drmPrimeHandleFD failed, errno=%d\n", errno);
|
||||
return -1;
|
||||
@@ -185,17 +200,20 @@ drm_virtual_output_submit_frame(struct drm_output *output,
|
||||
|
||||
static int
|
||||
drm_virtual_output_repaint(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct drm_pending_state *pending_state = repaint_data;
|
||||
struct drm_output_state *state = NULL;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_plane_state *scanout_state;
|
||||
struct drm_pending_state *pending_state;
|
||||
struct drm_device *device;
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
device = output->device;
|
||||
pending_state = device->repaint_data;
|
||||
|
||||
if (output->disable_pending || output->destroy_pending)
|
||||
goto err;
|
||||
|
||||
@@ -242,7 +260,7 @@ drm_virtual_output_deinit(struct weston_output *base)
|
||||
drm_virtual_crtc_destroy(output->crtc);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
drm_virtual_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
@@ -256,6 +274,9 @@ drm_virtual_output_destroy(struct weston_output *base)
|
||||
|
||||
drm_output_state_free(output->state_cur);
|
||||
|
||||
if (output->virtual_destroy)
|
||||
output->virtual_destroy(base);
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
@@ -263,7 +284,8 @@ static int
|
||||
drm_virtual_output_enable(struct weston_output *output_base)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *b = to_drm_backend(output_base->compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
|
||||
assert(output->virtual);
|
||||
|
||||
@@ -277,7 +299,7 @@ drm_virtual_output_enable(struct weston_output *output_base)
|
||||
goto err;
|
||||
}
|
||||
|
||||
output->scanout_plane = drm_virtual_plane_create(b, output);
|
||||
output->scanout_plane = drm_virtual_plane_create(device, output);
|
||||
if (!output->scanout_plane) {
|
||||
weston_log("Failed to find primary plane for output %s\n",
|
||||
output->base.name);
|
||||
@@ -320,22 +342,27 @@ drm_virtual_output_disable(struct weston_output *base)
|
||||
}
|
||||
|
||||
static struct weston_output *
|
||||
drm_virtual_output_create(struct weston_compositor *c, char *name)
|
||||
drm_virtual_output_create(struct weston_compositor *c, char *name,
|
||||
void (*destroy_func)(struct weston_output *))
|
||||
{
|
||||
struct drm_output *output;
|
||||
struct drm_backend *b = to_drm_backend(c);
|
||||
/* Always use the main device for virtual outputs */
|
||||
struct drm_device *device = b->drm;
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
output->crtc = drm_virtual_crtc_create(b, output);
|
||||
output->device = device;
|
||||
output->crtc = drm_virtual_crtc_create(device, output);
|
||||
if (!output->crtc) {
|
||||
free(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
output->virtual = true;
|
||||
output->virtual_destroy = destroy_func;
|
||||
output->gbm_bo_flags = GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING;
|
||||
|
||||
weston_output_init(&output->base, c, name);
|
||||
@@ -357,7 +384,8 @@ drm_virtual_output_set_gbm_format(struct weston_output *base,
|
||||
const char *gbm_format)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
struct drm_backend *b = to_drm_backend(base->compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
|
||||
if (parse_gbm_format(gbm_format, b->gbm_format, &output->gbm_format) == -1)
|
||||
output->gbm_format = b->gbm_format;
|
||||
|
||||
+405
-282
File diff suppressed because it is too large
Load Diff
+72
-46
@@ -69,7 +69,7 @@ drm_fb_destroy_dumb(struct drm_fb *fb)
|
||||
}
|
||||
|
||||
static int
|
||||
drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
drm_fb_addfb(struct drm_device *device, struct drm_fb *fb)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
uint64_t mods[4] = { };
|
||||
@@ -77,7 +77,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
|
||||
/* If we have a modifier set, we must only use the WithModifiers
|
||||
* entrypoint; we cannot import it through legacy ioctls. */
|
||||
if (b->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
if (device->fb_modifiers && fb->modifier != DRM_FORMAT_MOD_INVALID) {
|
||||
/* KMS demands that if a modifier is set, it must be the same
|
||||
* for all planes. */
|
||||
for (i = 0; i < ARRAY_LENGTH(mods) && fb->handles[i]; i++)
|
||||
@@ -98,7 +98,7 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
|
||||
/* Legacy AddFB can't always infer the format from depth/bpp alone, so
|
||||
* check if our format is one of the lucky ones. */
|
||||
if (!fb->format->depth || !fb->format->bpp)
|
||||
if (!fb->format->addfb_legacy_depth || !fb->format->bpp)
|
||||
return ret;
|
||||
|
||||
/* Cannot fall back to AddFB for multi-planar formats either. */
|
||||
@@ -106,13 +106,13 @@ drm_fb_addfb(struct drm_backend *b, struct drm_fb *fb)
|
||||
return ret;
|
||||
|
||||
ret = drmModeAddFB(fb->fd, fb->width, fb->height,
|
||||
fb->format->depth, fb->format->bpp,
|
||||
fb->format->addfb_legacy_depth, fb->format->bpp,
|
||||
fb->strides[0], fb->handles[0], &fb->fb_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct drm_fb *
|
||||
drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
drm_fb_create_dumb(struct drm_device *device, int width, int height,
|
||||
uint32_t format)
|
||||
{
|
||||
struct drm_fb *fb;
|
||||
@@ -134,7 +134,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
goto err_fb;
|
||||
}
|
||||
|
||||
if (!fb->format->depth || !fb->format->bpp) {
|
||||
if (!fb->format->addfb_legacy_depth || !fb->format->bpp) {
|
||||
weston_log("format 0x%lx is not compatible with dumb buffers\n",
|
||||
(unsigned long) format);
|
||||
goto err_fb;
|
||||
@@ -145,7 +145,7 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
create_arg.width = width;
|
||||
create_arg.height = height;
|
||||
|
||||
ret = drmIoctl(b->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
|
||||
ret = drmIoctl(device->drm.fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg);
|
||||
if (ret)
|
||||
goto err_fb;
|
||||
|
||||
@@ -157,9 +157,9 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
fb->size = create_arg.size;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
fb->fd = b->drm.fd;
|
||||
fb->fd = device->drm.fd;
|
||||
|
||||
if (drm_fb_addfb(b, fb) != 0) {
|
||||
if (drm_fb_addfb(device, fb) != 0) {
|
||||
weston_log("failed to create kms fb: %s\n", strerror(errno));
|
||||
goto err_bo;
|
||||
}
|
||||
@@ -171,18 +171,18 @@ drm_fb_create_dumb(struct drm_backend *b, int width, int height,
|
||||
goto err_add_fb;
|
||||
|
||||
fb->map = mmap(NULL, fb->size, PROT_WRITE,
|
||||
MAP_SHARED, b->drm.fd, map_arg.offset);
|
||||
MAP_SHARED, device->drm.fd, map_arg.offset);
|
||||
if (fb->map == MAP_FAILED)
|
||||
goto err_add_fb;
|
||||
|
||||
return fb;
|
||||
|
||||
err_add_fb:
|
||||
drmModeRmFB(b->drm.fd, fb->fb_id);
|
||||
drmModeRmFB(device->drm.fd, fb->fb_id);
|
||||
err_bo:
|
||||
memset(&destroy_arg, 0, sizeof(destroy_arg));
|
||||
destroy_arg.handle = create_arg.handle;
|
||||
drmIoctl(b->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
|
||||
drmIoctl(device->drm.fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg);
|
||||
err_fb:
|
||||
free(fb);
|
||||
return NULL;
|
||||
@@ -218,7 +218,7 @@ drm_fb_destroy_dmabuf(struct drm_fb *fb)
|
||||
|
||||
static struct drm_fb *
|
||||
drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
struct drm_backend *backend, bool is_opaque,
|
||||
struct drm_device *device, bool is_opaque,
|
||||
uint32_t *try_view_on_plane_failure_reasons)
|
||||
{
|
||||
#ifndef HAVE_GBM_FD_IMPORT
|
||||
@@ -227,6 +227,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
* of GBM_BO_IMPORT_FD_MODIFIER. */
|
||||
return NULL;
|
||||
#else
|
||||
struct drm_backend *backend = device->backend;
|
||||
struct drm_fb *fb;
|
||||
int i;
|
||||
struct gbm_import_fd_modifier_data import_mod = {
|
||||
@@ -287,7 +288,7 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
fb->height = dmabuf->attributes.height;
|
||||
fb->modifier = dmabuf->attributes.modifier[0];
|
||||
fb->size = 0;
|
||||
fb->fd = backend->drm.fd;
|
||||
fb->fd = device->drm.fd;
|
||||
|
||||
ARRAY_COPY(fb->strides, dmabuf->attributes.stride);
|
||||
ARRAY_COPY(fb->offsets, dmabuf->attributes.offset);
|
||||
@@ -302,10 +303,10 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
if (is_opaque)
|
||||
fb->format = pixel_format_get_opaque_substitute(fb->format);
|
||||
|
||||
if (backend->min_width > fb->width ||
|
||||
fb->width > backend->max_width ||
|
||||
backend->min_height > fb->height ||
|
||||
fb->height > backend->max_height) {
|
||||
if (device->min_width > fb->width ||
|
||||
fb->width > device->max_width ||
|
||||
device->min_height > fb->height ||
|
||||
fb->height > device->max_height) {
|
||||
weston_log("bo geometry out of bounds\n");
|
||||
goto err_free;
|
||||
}
|
||||
@@ -315,12 +316,15 @@ drm_fb_get_from_dmabuf(struct linux_dmabuf_buffer *dmabuf,
|
||||
union gbm_bo_handle handle;
|
||||
|
||||
handle = gbm_bo_get_handle_for_plane(fb->bo, i);
|
||||
if (handle.s32 == -1)
|
||||
if (handle.s32 == -1) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED;
|
||||
goto err_free;
|
||||
}
|
||||
fb->handles[i] = handle.u32;
|
||||
}
|
||||
|
||||
if (drm_fb_addfb(backend, fb) != 0) {
|
||||
if (drm_fb_addfb(device, fb) != 0) {
|
||||
if (try_view_on_plane_failure_reasons)
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_ADD_FB_FAILED;
|
||||
@@ -336,7 +340,7 @@ err_free:
|
||||
}
|
||||
|
||||
struct drm_fb *
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_device *device,
|
||||
bool is_opaque, enum drm_fb_type type)
|
||||
{
|
||||
struct drm_fb *fb = gbm_bo_get_user_data(bo);
|
||||
@@ -356,7 +360,7 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
fb->type = type;
|
||||
fb->refcnt = 1;
|
||||
fb->bo = bo;
|
||||
fb->fd = backend->drm.fd;
|
||||
fb->fd = device->drm.fd;
|
||||
|
||||
fb->width = gbm_bo_get_width(bo);
|
||||
fb->height = gbm_bo_get_height(bo);
|
||||
@@ -389,15 +393,15 @@ drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_backend *backend,
|
||||
if (is_opaque)
|
||||
fb->format = pixel_format_get_opaque_substitute(fb->format);
|
||||
|
||||
if (backend->min_width > fb->width ||
|
||||
fb->width > backend->max_width ||
|
||||
backend->min_height > fb->height ||
|
||||
fb->height > backend->max_height) {
|
||||
if (device->min_width > fb->width ||
|
||||
fb->width > device->max_width ||
|
||||
device->min_height > fb->height ||
|
||||
fb->height > device->max_height) {
|
||||
weston_log("bo geometry out of bounds\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (drm_fb_addfb(backend, fb) != 0) {
|
||||
if (drm_fb_addfb(device, fb) != 0) {
|
||||
if (type == BUFFER_GBM_SURFACE)
|
||||
weston_log("failed to create kms fb: %s\n",
|
||||
strerror(errno));
|
||||
@@ -453,22 +457,25 @@ drm_can_scanout_dmabuf(struct weston_compositor *ec,
|
||||
{
|
||||
struct drm_fb *fb;
|
||||
struct drm_backend *b = to_drm_backend(ec);
|
||||
struct drm_device *device = b->drm;
|
||||
bool ret = false;
|
||||
uint32_t try_reason = 0x0;
|
||||
|
||||
fb = drm_fb_get_from_dmabuf(dmabuf, b, true, NULL);
|
||||
fb = drm_fb_get_from_dmabuf(dmabuf, device, true, &try_reason);
|
||||
if (fb)
|
||||
ret = true;
|
||||
|
||||
drm_fb_unref(fb);
|
||||
drm_debug(b, "[dmabuf] dmabuf %p, import test %s\n", dmabuf,
|
||||
ret ? "succeeded" : "failed");
|
||||
drm_debug(b, "[dmabuf] dmabuf %p, import test %s, with reason 0x%x\n", dmabuf,
|
||||
ret ? "succeeded" : "failed", try_reason);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
drm_fb_compatible_with_plane(struct drm_fb *fb, struct drm_plane *plane)
|
||||
{
|
||||
struct drm_backend *b = plane->backend;
|
||||
struct drm_device *device = plane->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct weston_drm_format *fmt;
|
||||
|
||||
/* Check whether the format is supported */
|
||||
@@ -508,6 +515,8 @@ drm_fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
|
||||
struct drm_buffer_fb *buf_fb =
|
||||
container_of(listener, struct drm_buffer_fb, buffer_destroy_listener);
|
||||
|
||||
wl_list_remove(&buf_fb->buffer_destroy_listener.link);
|
||||
|
||||
if (buf_fb->fb) {
|
||||
assert(buf_fb->fb->type == BUFFER_CLIENT ||
|
||||
buf_fb->fb->type == BUFFER_DMABUF);
|
||||
@@ -523,25 +532,36 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||
struct drm_buffer_fb *buf_fb;
|
||||
bool is_opaque = weston_view_is_opaque(ev, &ev->transform.boundingbox);
|
||||
struct linux_dmabuf_buffer *dmabuf;
|
||||
struct drm_fb *fb;
|
||||
struct drm_plane *plane;
|
||||
|
||||
if (ev->alpha != 1.0f)
|
||||
if (ev->alpha != 1.0f) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_GLOBAL_ALPHA;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!drm_view_transform_supported(ev, &output->base))
|
||||
if (!drm_view_transform_supported(ev, &output->base)) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_INCOMPATIBLE_TRANSFORM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ev->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED &&
|
||||
ev->surface->desired_protection > output->base.current_protection)
|
||||
ev->surface->desired_protection > output->base.current_protection) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_INADEQUATE_CONTENT_PROTECTION;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!buffer)
|
||||
if (!buffer) {
|
||||
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_BUFFER;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buffer->backend_private) {
|
||||
buf_fb = buffer->backend_private;
|
||||
@@ -554,20 +574,18 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
buf_fb->buffer_destroy_listener.notify = drm_fb_handle_buffer_destroy;
|
||||
wl_signal_add(&buffer->destroy_signal, &buf_fb->buffer_destroy_listener);
|
||||
|
||||
if (wl_shm_buffer_get(buffer->resource))
|
||||
goto unsuitable;
|
||||
|
||||
/* GBM is used for dmabuf import as well as from client wl_buffer. */
|
||||
if (!b->gbm)
|
||||
if (!b->gbm) {
|
||||
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_NO_GBM;
|
||||
goto unsuitable;
|
||||
}
|
||||
|
||||
dmabuf = linux_dmabuf_buffer_get(buffer->resource);
|
||||
if (dmabuf) {
|
||||
fb = drm_fb_get_from_dmabuf(dmabuf, b, is_opaque,
|
||||
if (buffer->type == WESTON_BUFFER_DMABUF) {
|
||||
fb = drm_fb_get_from_dmabuf(buffer->dmabuf, device, is_opaque,
|
||||
&buf_fb->failure_reasons);
|
||||
if (!fb)
|
||||
goto unsuitable;
|
||||
} else {
|
||||
} else if (buffer->type == WESTON_BUFFER_RENDERER_OPAQUE) {
|
||||
struct gbm_bo *bo;
|
||||
|
||||
bo = gbm_bo_import(b->gbm, GBM_BO_IMPORT_WL_BUFFER,
|
||||
@@ -575,16 +593,24 @@ drm_fb_get_from_view(struct drm_output_state *state, struct weston_view *ev,
|
||||
if (!bo)
|
||||
goto unsuitable;
|
||||
|
||||
fb = drm_fb_get_from_bo(bo, b, is_opaque, BUFFER_CLIENT);
|
||||
fb = drm_fb_get_from_bo(bo, device, is_opaque, BUFFER_CLIENT);
|
||||
if (!fb) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
(1 << FAILURE_REASONS_ADD_FB_FAILED);
|
||||
gbm_bo_destroy(bo);
|
||||
goto unsuitable;
|
||||
}
|
||||
} else {
|
||||
*try_view_on_plane_failure_reasons |= FAILURE_REASONS_BUFFER_TYPE;
|
||||
goto unsuitable;
|
||||
}
|
||||
|
||||
/* Check if this buffer can ever go on any planes. If it can't, we have
|
||||
* no reason to ever have a drm_fb, so we fail it here. */
|
||||
wl_list_for_each(plane, &b->plane_list, link) {
|
||||
wl_list_for_each(plane, &device->plane_list, link) {
|
||||
/* only SHM buffers can go into cursor planes */
|
||||
if (plane->type == WDRM_PLANE_TYPE_CURSOR)
|
||||
continue;
|
||||
if (drm_fb_compatible_with_plane(fb, plane))
|
||||
fb->plane_mask |= (1 << plane->plane_idx);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2021-2022 Collabora, Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <libweston/libweston.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "drm-internal.h"
|
||||
|
||||
static inline uint16_t
|
||||
color_xy_to_u16(float v)
|
||||
{
|
||||
assert(v >= 0.0f);
|
||||
assert(v <= 1.0f);
|
||||
/*
|
||||
* CTA-861-G
|
||||
* 6.9.1 Static Metadata Type 1
|
||||
* chromaticity coordinate encoding
|
||||
*/
|
||||
return (uint16_t)round(v * 50000.0);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
nits_to_u16(float nits)
|
||||
{
|
||||
assert(nits >= 1.0f);
|
||||
assert(nits <= 65535.0f);
|
||||
/*
|
||||
* CTA-861-G
|
||||
* 6.9.1 Static Metadata Type 1
|
||||
* max display mastering luminance, max content light level,
|
||||
* max frame-average light level
|
||||
*/
|
||||
return (uint16_t)round(nits);
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
nits_to_u16_dark(float nits)
|
||||
{
|
||||
assert(nits >= 0.0001f);
|
||||
assert(nits <= 6.5535f);
|
||||
/*
|
||||
* CTA-861-G
|
||||
* 6.9.1 Static Metadata Type 1
|
||||
* min display mastering luminance
|
||||
*/
|
||||
return (uint16_t)round(nits * 10000.0);
|
||||
}
|
||||
|
||||
static void
|
||||
weston_hdr_metadata_type1_to_kms(struct hdr_metadata_infoframe *dst,
|
||||
const struct weston_hdr_metadata_type1 *src)
|
||||
{
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
dst->display_primaries[i].x = color_xy_to_u16(src->primary[i].x);
|
||||
dst->display_primaries[i].y = color_xy_to_u16(src->primary[i].y);
|
||||
}
|
||||
}
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_WHITE) {
|
||||
dst->white_point.x = color_xy_to_u16(src->white.x);
|
||||
dst->white_point.y = color_xy_to_u16(src->white.y);
|
||||
}
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML)
|
||||
dst->max_display_mastering_luminance = nits_to_u16(src->maxDML);
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MINDML)
|
||||
dst->min_display_mastering_luminance = nits_to_u16_dark(src->minDML);
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL)
|
||||
dst->max_cll = nits_to_u16(src->maxCLL);
|
||||
|
||||
if (src->group_mask & WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL)
|
||||
dst->max_fall = nits_to_u16(src->maxFALL);
|
||||
}
|
||||
|
||||
int
|
||||
drm_output_ensure_hdr_output_metadata_blob(struct drm_output *output)
|
||||
{
|
||||
struct drm_device *device = output->device;
|
||||
const struct weston_hdr_metadata_type1 *src;
|
||||
struct hdr_output_metadata meta;
|
||||
uint32_t blob_id = 0;
|
||||
int ret;
|
||||
|
||||
if (output->hdr_output_metadata_blob_id &&
|
||||
output->ackd_color_outcome_serial == output->base.color_outcome_serial)
|
||||
return 0;
|
||||
|
||||
src = weston_output_get_hdr_metadata_type1(&output->base);
|
||||
|
||||
/*
|
||||
* Set up the data for Dynamic Range and Mastering InfoFrame,
|
||||
* CTA-861-G, a.k.a the static HDR metadata.
|
||||
*/
|
||||
|
||||
memset(&meta, 0, sizeof meta);
|
||||
|
||||
meta.metadata_type = 0; /* Static Metadata Type 1 */
|
||||
|
||||
/* Duplicated field in UABI struct */
|
||||
meta.hdmi_metadata_type1.metadata_type = meta.metadata_type;
|
||||
|
||||
switch (output->base.eotf_mode) {
|
||||
case WESTON_EOTF_MODE_NONE:
|
||||
assert(0 && "bad eotf_mode: none");
|
||||
return -1;
|
||||
case WESTON_EOTF_MODE_SDR:
|
||||
/*
|
||||
* Do not send any static HDR metadata. Video sinks should
|
||||
* respond by switching to traditional SDR mode. If they
|
||||
* do not, the kernel should fix that up.
|
||||
*/
|
||||
assert(output->hdr_output_metadata_blob_id == 0);
|
||||
return 0;
|
||||
case WESTON_EOTF_MODE_TRADITIONAL_HDR:
|
||||
meta.hdmi_metadata_type1.eotf = 1; /* from CTA-861-G */
|
||||
break;
|
||||
case WESTON_EOTF_MODE_ST2084:
|
||||
meta.hdmi_metadata_type1.eotf = 2; /* from CTA-861-G */
|
||||
weston_hdr_metadata_type1_to_kms(&meta.hdmi_metadata_type1, src);
|
||||
break;
|
||||
case WESTON_EOTF_MODE_HLG:
|
||||
meta.hdmi_metadata_type1.eotf = 3; /* from CTA-861-G */
|
||||
break;
|
||||
}
|
||||
|
||||
if (meta.hdmi_metadata_type1.eotf == 0) {
|
||||
assert(0 && "bad eotf_mode");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = drmModeCreatePropertyBlob(device->drm.fd,
|
||||
&meta, sizeof meta, &blob_id);
|
||||
if (ret != 0) {
|
||||
weston_log("Error: failed to create KMS blob for HDR metadata on output '%s': %s\n",
|
||||
output->base.name, strerror(-ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
drmModeDestroyPropertyBlob(device->drm.fd,
|
||||
output->hdr_output_metadata_blob_id);
|
||||
|
||||
output->hdr_output_metadata_blob_id = blob_id;
|
||||
output->ackd_color_outcome_serial = output->base.color_outcome_serial;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+173
-126
@@ -143,6 +143,10 @@ const struct drm_property_info connector_props[] = {
|
||||
.enum_values = panel_orientation_enums,
|
||||
.num_enum_values = WDRM_PANEL_ORIENTATION__COUNT,
|
||||
},
|
||||
[WDRM_CONNECTOR_HDR_OUTPUT_METADATA] = {
|
||||
.name = "HDR_OUTPUT_METADATA",
|
||||
},
|
||||
[WDRM_CONNECTOR_MAX_BPC] = { .name = "max bpc", },
|
||||
};
|
||||
|
||||
const struct drm_property_info crtc_props[] = {
|
||||
@@ -272,14 +276,14 @@ drm_property_get_range_values(struct drm_property_info *info,
|
||||
* The values given in enum_names are searched for, and stored in the
|
||||
* same-indexed field of the map array.
|
||||
*
|
||||
* @param b DRM backend object
|
||||
* @param device DRM device object
|
||||
* @param src DRM property info array to source from
|
||||
* @param info DRM property info array to copy into
|
||||
* @param num_infos Number of entries in the source array
|
||||
* @param props DRM object properties for the object
|
||||
*/
|
||||
void
|
||||
drm_property_info_populate(struct drm_backend *b,
|
||||
drm_property_info_populate(struct drm_device *device,
|
||||
const struct drm_property_info *src,
|
||||
struct drm_property_info *info,
|
||||
unsigned int num_infos,
|
||||
@@ -311,7 +315,7 @@ drm_property_info_populate(struct drm_backend *b,
|
||||
for (i = 0; i < props->count_props; i++) {
|
||||
unsigned int k;
|
||||
|
||||
prop = drmModeGetProperty(b->drm.fd, props->props[i]);
|
||||
prop = drmModeGetProperty(device->drm.fd, props->props[i]);
|
||||
if (!prop)
|
||||
continue;
|
||||
|
||||
@@ -411,19 +415,6 @@ drm_property_info_free(struct drm_property_info *info, int num_props)
|
||||
memset(info, 0, sizeof(*info) * num_props);
|
||||
}
|
||||
|
||||
static inline uint32_t *
|
||||
formats_ptr(struct drm_format_modifier_blob *blob)
|
||||
{
|
||||
return (uint32_t *)(((char *)blob) + blob->formats_offset);
|
||||
}
|
||||
|
||||
static inline struct drm_format_modifier *
|
||||
modifiers_ptr(struct drm_format_modifier_blob *blob)
|
||||
{
|
||||
return (struct drm_format_modifier *)
|
||||
(((char *)blob) + blob->modifiers_offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the plane's formats array, using either the IN_FORMATS blob
|
||||
* property (if available), or the plane's format list if not.
|
||||
@@ -433,13 +424,11 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
|
||||
const drmModeObjectProperties *props,
|
||||
const bool use_modifiers)
|
||||
{
|
||||
unsigned i, j;
|
||||
struct drm_device *device = plane->device;
|
||||
uint32_t i, blob_id, fmt_prev = DRM_FORMAT_INVALID;
|
||||
drmModeFormatModifierIterator drm_iter = {0};
|
||||
struct weston_drm_format *fmt = NULL;
|
||||
drmModePropertyBlobRes *blob = NULL;
|
||||
struct drm_format_modifier_blob *fmt_mod_blob;
|
||||
struct drm_format_modifier *blob_modifiers;
|
||||
uint32_t *blob_formats;
|
||||
uint32_t blob_id;
|
||||
struct weston_drm_format *fmt;
|
||||
int ret = 0;
|
||||
|
||||
if (!use_modifiers)
|
||||
@@ -451,39 +440,26 @@ drm_plane_populate_formats(struct drm_plane *plane, const drmModePlane *kplane,
|
||||
if (blob_id == 0)
|
||||
goto fallback;
|
||||
|
||||
blob = drmModeGetPropertyBlob(plane->backend->drm.fd, blob_id);
|
||||
blob = drmModeGetPropertyBlob(device->drm.fd, blob_id);
|
||||
if (!blob)
|
||||
goto fallback;
|
||||
|
||||
fmt_mod_blob = blob->data;
|
||||
blob_formats = formats_ptr(fmt_mod_blob);
|
||||
blob_modifiers = modifiers_ptr(fmt_mod_blob);
|
||||
|
||||
assert(kplane->count_formats == fmt_mod_blob->count_formats);
|
||||
|
||||
for (i = 0; i < fmt_mod_blob->count_formats; i++) {
|
||||
while (drmModeFormatModifierBlobIterNext(blob, &drm_iter)) {
|
||||
if (fmt_prev != drm_iter.fmt) {
|
||||
fmt = weston_drm_format_array_add_format(&plane->formats,
|
||||
blob_formats[i]);
|
||||
drm_iter.fmt);
|
||||
if (!fmt) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (j = 0; j < fmt_mod_blob->count_modifiers; j++) {
|
||||
struct drm_format_modifier *mod = &blob_modifiers[j];
|
||||
|
||||
if ((i < mod->offset) || (i > mod->offset + 63))
|
||||
continue;
|
||||
if (!(mod->formats & (1 << (i - mod->offset))))
|
||||
continue;
|
||||
|
||||
ret = weston_drm_format_add_modifier(fmt, mod->modifier);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
fmt_prev = drm_iter.fmt;
|
||||
}
|
||||
|
||||
if (fmt->modifiers.size == 0)
|
||||
weston_drm_format_array_remove_latest_format(&plane->formats);
|
||||
ret = weston_drm_format_add_modifier(fmt, drm_iter.mod);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -510,14 +486,15 @@ drm_output_set_gamma(struct weston_output *output_base,
|
||||
{
|
||||
int rc;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *backend =
|
||||
to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
|
||||
assert(output);
|
||||
|
||||
/* check */
|
||||
if (output_base->gamma_size != size)
|
||||
return;
|
||||
|
||||
rc = drmModeCrtcSetGamma(backend->drm.fd,
|
||||
rc = drmModeCrtcSetGamma(device->drm.fd,
|
||||
output->crtc->crtc_id,
|
||||
size, r, g, b);
|
||||
if (rc)
|
||||
@@ -535,7 +512,8 @@ drm_output_assign_state(struct drm_output_state *state,
|
||||
enum drm_state_apply_mode mode)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_head *head;
|
||||
|
||||
@@ -552,13 +530,13 @@ drm_output_assign_state(struct drm_output_state *state,
|
||||
|
||||
output->state_cur = state;
|
||||
|
||||
if (b->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
|
||||
if (device->atomic_modeset && mode == DRM_STATE_APPLY_ASYNC) {
|
||||
drm_debug(b, "\t[CRTC:%u] setting pending flip\n",
|
||||
output->crtc->crtc_id);
|
||||
output->atomic_complete_pending = true;
|
||||
}
|
||||
|
||||
if (b->atomic_modeset &&
|
||||
if (device->atomic_modeset &&
|
||||
state->protection == WESTON_HDCP_DISABLE)
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link)
|
||||
weston_head_set_content_protection_status(&head->base,
|
||||
@@ -580,7 +558,7 @@ drm_output_assign_state(struct drm_output_state *state,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
continue;
|
||||
|
||||
assert(plane->type != WDRM_PLANE_TYPE_OVERLAY);
|
||||
@@ -593,7 +571,7 @@ static void
|
||||
drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_crtc *crtc = output->crtc;
|
||||
struct drm_plane *plane = output->cursor_plane;
|
||||
struct drm_plane_state *state;
|
||||
@@ -609,7 +587,7 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
if (!state->fb) {
|
||||
pixman_region32_fini(&plane->base.damage);
|
||||
pixman_region32_init(&plane->base.damage);
|
||||
drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -618,8 +596,8 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
|
||||
handle = output->gbm_cursor_handle[output->current_cursor];
|
||||
if (plane->state_cur->fb != state->fb) {
|
||||
if (drmModeSetCursor(b->drm.fd, crtc->crtc_id, handle,
|
||||
b->cursor_width, b->cursor_height)) {
|
||||
if (drmModeSetCursor(device->drm.fd, crtc->crtc_id, handle,
|
||||
device->cursor_width, device->cursor_height)) {
|
||||
weston_log("failed to set cursor: %s\n",
|
||||
strerror(errno));
|
||||
goto err;
|
||||
@@ -629,7 +607,7 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
pixman_region32_fini(&plane->base.damage);
|
||||
pixman_region32_init(&plane->base.damage);
|
||||
|
||||
if (drmModeMoveCursor(b->drm.fd, crtc->crtc_id,
|
||||
if (drmModeMoveCursor(device->drm.fd, crtc->crtc_id,
|
||||
state->dest_x, state->dest_y)) {
|
||||
weston_log("failed to move cursor: %s\n", strerror(errno));
|
||||
goto err;
|
||||
@@ -638,15 +616,16 @@ drm_output_set_cursor(struct drm_output_state *output_state)
|
||||
return;
|
||||
|
||||
err:
|
||||
b->cursors_are_broken = true;
|
||||
drmModeSetCursor(b->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
device->cursors_are_broken = true;
|
||||
drmModeSetCursor(device->drm.fd, crtc->crtc_id, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *backend = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *backend = device->backend;
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_crtc *crtc = output->crtc;
|
||||
struct drm_property_info *dpms_prop;
|
||||
@@ -678,14 +657,14 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
|
||||
if (state->dpms != WESTON_DPMS_ON) {
|
||||
if (output->cursor_plane) {
|
||||
ret = drmModeSetCursor(backend->drm.fd, crtc->crtc_id,
|
||||
ret = drmModeSetCursor(device->drm.fd, crtc->crtc_id,
|
||||
0, 0, 0);
|
||||
if (ret)
|
||||
weston_log("drmModeSetCursor failed disable: %s\n",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
NULL, 0, NULL);
|
||||
if (ret)
|
||||
weston_log("drmModeSetCrtc failed disabling: %s\n",
|
||||
@@ -719,12 +698,12 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
assert(scanout_state->in_fence_fd == -1);
|
||||
|
||||
mode = to_drm_mode(output->base.current_mode);
|
||||
if (backend->state_invalid ||
|
||||
if (device->state_invalid ||
|
||||
!scanout_plane->state_cur->fb ||
|
||||
scanout_plane->state_cur->fb->strides[0] !=
|
||||
scanout_state->fb->strides[0]) {
|
||||
|
||||
ret = drmModeSetCrtc(backend->drm.fd, crtc->crtc_id,
|
||||
ret = drmModeSetCrtc(device->drm.fd, crtc->crtc_id,
|
||||
scanout_state->fb->fb_id,
|
||||
0, 0,
|
||||
connectors, n_conn,
|
||||
@@ -740,7 +719,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
crtc->crtc_id, scanout_state->plane->plane_id,
|
||||
pinfo ? pinfo->drm_format_name : "UNKNOWN");
|
||||
|
||||
if (drmModePageFlip(backend->drm.fd, crtc->crtc_id,
|
||||
if (drmModePageFlip(device->drm.fd, crtc->crtc_id,
|
||||
scanout_state->fb->fb_id,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
|
||||
weston_log("queueing pageflip failed: %s\n", strerror(errno));
|
||||
@@ -761,7 +740,7 @@ drm_output_apply_state_legacy(struct drm_output_state *state)
|
||||
if (dpms_prop->prop_id == 0)
|
||||
continue;
|
||||
|
||||
ret = drmModeConnectorSetProperty(backend->drm.fd,
|
||||
ret = drmModeConnectorSetProperty(device->drm.fd,
|
||||
head->connector.connector_id,
|
||||
dpms_prop->prop_id,
|
||||
state->dpms);
|
||||
@@ -786,6 +765,8 @@ static int
|
||||
crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
|
||||
enum wdrm_crtc_property prop, uint64_t val)
|
||||
{
|
||||
struct drm_device *device = crtc->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_property_info *info = &crtc->props_crtc[prop];
|
||||
int ret;
|
||||
|
||||
@@ -794,7 +775,7 @@ crtc_add_prop(drmModeAtomicReq *req, struct drm_crtc *crtc,
|
||||
|
||||
ret = drmModeAtomicAddProperty(req, crtc->crtc_id, info->prop_id,
|
||||
val);
|
||||
drm_debug(crtc->backend, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
drm_debug(b, "\t\t\t[CRTC:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
(unsigned long) crtc->crtc_id,
|
||||
(unsigned long) info->prop_id, info->name,
|
||||
(unsigned long long) val, (unsigned long long) val);
|
||||
@@ -805,6 +786,8 @@ static int
|
||||
connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
|
||||
enum wdrm_connector_property prop, uint64_t val)
|
||||
{
|
||||
struct drm_device *device = connector->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_property_info *info = &connector->props[prop];
|
||||
uint32_t connector_id = connector->connector_id;
|
||||
int ret;
|
||||
@@ -813,7 +796,7 @@ connector_add_prop(drmModeAtomicReq *req, struct drm_connector *connector,
|
||||
return -1;
|
||||
|
||||
ret = drmModeAtomicAddProperty(req, connector_id, info->prop_id, val);
|
||||
drm_debug(connector->backend, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
drm_debug(b, "\t\t\t[CONN:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
(unsigned long) connector_id,
|
||||
(unsigned long) info->prop_id, info->name,
|
||||
(unsigned long long) val, (unsigned long long) val);
|
||||
@@ -824,6 +807,8 @@ static int
|
||||
plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
|
||||
enum wdrm_plane_property prop, uint64_t val)
|
||||
{
|
||||
struct drm_device *device = plane->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_property_info *info = &plane->props[prop];
|
||||
int ret;
|
||||
|
||||
@@ -832,7 +817,7 @@ plane_add_prop(drmModeAtomicReq *req, struct drm_plane *plane,
|
||||
|
||||
ret = drmModeAtomicAddProperty(req, plane->plane_id, info->prop_id,
|
||||
val);
|
||||
drm_debug(plane->backend, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
drm_debug(b, "\t\t\t[PLANE:%lu] %lu (%s) -> %llu (0x%llx)\n",
|
||||
(unsigned long) plane->plane_id,
|
||||
(unsigned long) info->prop_id, info->name,
|
||||
(unsigned long long) val, (unsigned long long) val);
|
||||
@@ -921,17 +906,52 @@ drm_connector_set_hdcp_property(struct drm_connector *connector,
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_connector_set_max_bpc(struct drm_connector *connector,
|
||||
struct drm_output *output,
|
||||
drmModeAtomicReq *req)
|
||||
{
|
||||
const struct drm_property_info *info;
|
||||
struct drm_head *head;
|
||||
struct drm_backend *backend = output->device->backend;
|
||||
uint64_t max_bpc;
|
||||
uint64_t a, b;
|
||||
|
||||
if (!drm_connector_has_prop(connector, WDRM_CONNECTOR_MAX_BPC))
|
||||
return 0;
|
||||
|
||||
if (output->max_bpc == 0) {
|
||||
/* A value of 0 means that the current max_bpc must be programmed. */
|
||||
head = drm_head_find_by_connector(backend, connector->connector_id);
|
||||
max_bpc = head->inherited_max_bpc;
|
||||
} else {
|
||||
info = &connector->props[WDRM_CONNECTOR_MAX_BPC];
|
||||
assert(info->flags & DRM_MODE_PROP_RANGE);
|
||||
assert(info->num_range_values == 2);
|
||||
a = info->range_values[0];
|
||||
b = info->range_values[1];
|
||||
assert(a <= b);
|
||||
|
||||
max_bpc = MAX(a, MIN(output->max_bpc, b));
|
||||
}
|
||||
|
||||
return connector_add_prop(req, connector,
|
||||
WDRM_CONNECTOR_MAX_BPC, max_bpc);
|
||||
}
|
||||
|
||||
static int
|
||||
drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
drmModeAtomicReq *req,
|
||||
uint32_t *flags)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_crtc *crtc = output->crtc;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct drm_mode *current_mode = to_drm_mode(output->base.current_mode);
|
||||
struct drm_head *head;
|
||||
struct drm_head *tmp;
|
||||
int ret = 0;
|
||||
|
||||
drm_debug(b, "\t\t[atomic] %s output %lu (%s) state\n",
|
||||
@@ -944,7 +964,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
}
|
||||
|
||||
if (state->dpms == WESTON_DPMS_ON) {
|
||||
ret = drm_mode_ensure_blob(b, current_mode);
|
||||
ret = drm_mode_ensure_blob(device, current_mode);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
@@ -968,12 +988,30 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link)
|
||||
ret |= connector_add_prop(req, &head->connector,
|
||||
WDRM_CONNECTOR_CRTC_ID, 0);
|
||||
|
||||
wl_list_for_each_safe(head, tmp, &output->disable_head,
|
||||
disable_head_link) {
|
||||
ret |= connector_add_prop(req, &head->connector,
|
||||
WDRM_CONNECTOR_CRTC_ID, 0);
|
||||
wl_list_remove(&head->disable_head_link);
|
||||
wl_list_init(&head->disable_head_link);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link)
|
||||
wl_list_for_each(head, &output->base.head_list, base.output_link) {
|
||||
drm_connector_set_hdcp_property(&head->connector,
|
||||
state->protection, req);
|
||||
|
||||
if (drm_connector_has_prop(&head->connector,
|
||||
WDRM_CONNECTOR_HDR_OUTPUT_METADATA)) {
|
||||
ret |= connector_add_prop(req, &head->connector,
|
||||
WDRM_CONNECTOR_HDR_OUTPUT_METADATA,
|
||||
output->hdr_output_metadata_blob_id);
|
||||
}
|
||||
|
||||
ret |= drm_connector_set_max_bpc(&head->connector, output, req);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
weston_log("couldn't set atomic CRTC/connector state\n");
|
||||
return ret;
|
||||
@@ -1010,7 +1048,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state,
|
||||
if (plane_state->fb && plane_state->fb->format)
|
||||
pinfo = plane_state->fb->format;
|
||||
|
||||
drm_debug(plane->backend, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
|
||||
drm_debug(b, "\t\t\t[PLANE:%lu] FORMAT: %s\n",
|
||||
(unsigned long) plane->plane_id,
|
||||
pinfo ? pinfo->drm_format_name : "UNKNOWN");
|
||||
|
||||
@@ -1044,7 +1082,8 @@ static int
|
||||
drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
enum drm_state_apply_mode mode)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_output_state *output_state, *tmp;
|
||||
struct drm_plane *plane;
|
||||
drmModeAtomicReq *req = drmModeAtomicAlloc();
|
||||
@@ -1066,7 +1105,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
break;
|
||||
}
|
||||
|
||||
if (b->state_invalid) {
|
||||
if (device->state_invalid) {
|
||||
struct weston_head *head_base;
|
||||
struct drm_head *head;
|
||||
struct drm_crtc *crtc;
|
||||
@@ -1082,12 +1121,16 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
wl_list_for_each(head_base,
|
||||
&b->compositor->head_list, compositor_link) {
|
||||
struct drm_property_info *info;
|
||||
head = to_drm_head(head_base);
|
||||
if (!head)
|
||||
continue;
|
||||
|
||||
if (weston_head_is_enabled(head_base))
|
||||
continue;
|
||||
|
||||
head = to_drm_head(head_base);
|
||||
connector_id = head->connector.connector_id;
|
||||
if (head->connector.device != device)
|
||||
continue;
|
||||
|
||||
drm_debug(b, "\t\t[atomic] disabling inactive head %s\n",
|
||||
head_base->name);
|
||||
@@ -1103,7 +1146,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
wl_list_for_each(crtc, &b->crtc_list, link) {
|
||||
wl_list_for_each(crtc, &device->crtc_list, link) {
|
||||
struct drm_property_info *info;
|
||||
drmModeObjectProperties *props;
|
||||
uint64_t active;
|
||||
@@ -1116,7 +1159,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
* off, as the kernel will refuse to generate an event
|
||||
* for an off->off state and fail the commit.
|
||||
*/
|
||||
props = drmModeObjectGetProperties(b->drm.fd,
|
||||
props = drmModeObjectGetProperties(device->drm.fd,
|
||||
crtc->crtc_id,
|
||||
DRM_MODE_OBJECT_CRTC);
|
||||
if (!props) {
|
||||
@@ -1139,7 +1182,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
|
||||
/* Disable all the planes; planes which are being used will
|
||||
* override this state in the output-state application. */
|
||||
wl_list_for_each(plane, &b->plane_list, link) {
|
||||
wl_list_for_each(plane, &device->plane_list, link) {
|
||||
drm_debug(b, "\t\t[atomic] starting with plane %lu disabled\n",
|
||||
(unsigned long) plane->plane_id);
|
||||
plane_add_prop(req, plane, WDRM_PLANE_CRTC_ID, 0);
|
||||
@@ -1162,7 +1205,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drmModeAtomicCommit(b->drm.fd, req, flags, b);
|
||||
ret = drmModeAtomicCommit(device->drm.fd, req, flags, device);
|
||||
drm_debug(b, "[atomic] drmModeAtomicCommit\n");
|
||||
|
||||
/* Test commits do not take ownership of the state; return
|
||||
@@ -1182,7 +1225,7 @@ drm_pending_state_apply_atomic(struct drm_pending_state *pending_state,
|
||||
link)
|
||||
drm_output_assign_state(output_state, mode);
|
||||
|
||||
b->state_invalid = false;
|
||||
device->state_invalid = false;
|
||||
|
||||
assert(wl_list_empty(&pending_state->output_list));
|
||||
|
||||
@@ -1213,9 +1256,9 @@ out:
|
||||
int
|
||||
drm_pending_state_test(struct drm_pending_state *pending_state)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
return drm_pending_state_apply_atomic(pending_state,
|
||||
DRM_STATE_TEST_ONLY);
|
||||
|
||||
@@ -1234,24 +1277,25 @@ drm_pending_state_test(struct drm_pending_state *pending_state)
|
||||
int
|
||||
drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_output_state *output_state, *tmp;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
return drm_pending_state_apply_atomic(pending_state,
|
||||
DRM_STATE_APPLY_ASYNC);
|
||||
|
||||
if (b->state_invalid) {
|
||||
if (device->state_invalid) {
|
||||
/* If we need to reset all our state (e.g. because we've
|
||||
* just started, or just been VT-switched in), explicitly
|
||||
* disable all the CRTCs we aren't using. This also disables
|
||||
* all connectors on these CRTCs, so we don't need to do that
|
||||
* separately with the pre-atomic API. */
|
||||
wl_list_for_each(crtc, &b->crtc_list, link) {
|
||||
wl_list_for_each(crtc, &device->crtc_list, link) {
|
||||
if (crtc->output)
|
||||
continue;
|
||||
drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1274,7 +1318,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
weston_output_repaint_failed(&output->base);
|
||||
drm_output_state_free(output->state_cur);
|
||||
output->state_cur = drm_output_state_alloc(output, NULL);
|
||||
b->state_invalid = true;
|
||||
device->state_invalid = true;
|
||||
if (!b->use_pixman) {
|
||||
drm_output_fini_egl(output);
|
||||
drm_output_init_egl(output, b);
|
||||
@@ -1282,7 +1326,7 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
}
|
||||
}
|
||||
|
||||
b->state_invalid = false;
|
||||
device->state_invalid = false;
|
||||
|
||||
assert(wl_list_empty(&pending_state->output_list));
|
||||
|
||||
@@ -1301,24 +1345,24 @@ drm_pending_state_apply(struct drm_pending_state *pending_state)
|
||||
int
|
||||
drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
|
||||
{
|
||||
struct drm_backend *b = pending_state->backend;
|
||||
struct drm_device *device = pending_state->device;
|
||||
struct drm_output_state *output_state, *tmp;
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
return drm_pending_state_apply_atomic(pending_state,
|
||||
DRM_STATE_APPLY_SYNC);
|
||||
|
||||
if (b->state_invalid) {
|
||||
if (device->state_invalid) {
|
||||
/* If we need to reset all our state (e.g. because we've
|
||||
* just started, or just been VT-switched in), explicitly
|
||||
* disable all the CRTCs we aren't using. This also disables
|
||||
* all connectors on these CRTCs, so we don't need to do that
|
||||
* separately with the pre-atomic API. */
|
||||
wl_list_for_each(crtc, &b->crtc_list, link) {
|
||||
wl_list_for_each(crtc, &device->crtc_list, link) {
|
||||
if (crtc->output)
|
||||
continue;
|
||||
drmModeSetCrtc(b->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
drmModeSetCrtc(device->drm.fd, crtc->crtc_id, 0, 0, 0,
|
||||
NULL, 0, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1335,7 +1379,7 @@ drm_pending_state_apply_sync(struct drm_pending_state *pending_state)
|
||||
}
|
||||
}
|
||||
|
||||
b->state_invalid = false;
|
||||
device->state_invalid = false;
|
||||
|
||||
assert(wl_list_empty(&pending_state->output_list));
|
||||
|
||||
@@ -1360,14 +1404,14 @@ page_flip_handler(int fd, unsigned int frame,
|
||||
unsigned int sec, unsigned int usec, void *data)
|
||||
{
|
||||
struct drm_output *output = data;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
|
||||
|
||||
drm_output_update_msc(output, frame);
|
||||
|
||||
assert(!b->atomic_modeset);
|
||||
assert(!device->atomic_modeset);
|
||||
assert(output->page_flip_pending);
|
||||
output->page_flip_pending = false;
|
||||
|
||||
@@ -1378,14 +1422,15 @@ static void
|
||||
atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
||||
unsigned int usec, unsigned int crtc_id, void *data)
|
||||
{
|
||||
struct drm_backend *b = data;
|
||||
struct drm_device *device = data;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_output *output;
|
||||
uint32_t flags = WP_PRESENTATION_FEEDBACK_KIND_VSYNC |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION |
|
||||
WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
|
||||
|
||||
crtc = drm_crtc_find(b, crtc_id);
|
||||
crtc = drm_crtc_find(device, crtc_id);
|
||||
assert(crtc);
|
||||
|
||||
output = crtc->output;
|
||||
@@ -1399,7 +1444,7 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
||||
drm_output_update_msc(output, frame);
|
||||
|
||||
drm_debug(b, "[atomic][CRTC:%u] flip processing started\n", crtc_id);
|
||||
assert(b->atomic_modeset);
|
||||
assert(device->atomic_modeset);
|
||||
assert(output->atomic_complete_pending);
|
||||
output->atomic_complete_pending = false;
|
||||
|
||||
@@ -1410,12 +1455,12 @@ atomic_flip_handler(int fd, unsigned int frame, unsigned int sec,
|
||||
int
|
||||
on_drm_input(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct drm_backend *b = data;
|
||||
struct drm_device *device = data;
|
||||
drmEventContext evctx;
|
||||
|
||||
memset(&evctx, 0, sizeof evctx);
|
||||
evctx.version = 3;
|
||||
if (b->atomic_modeset)
|
||||
if (device->atomic_modeset)
|
||||
evctx.page_flip_handler2 = atomic_flip_handler;
|
||||
else
|
||||
evctx.page_flip_handler = page_flip_handler;
|
||||
@@ -1425,61 +1470,63 @@ on_drm_input(int fd, uint32_t mask, void *data)
|
||||
}
|
||||
|
||||
int
|
||||
init_kms_caps(struct drm_backend *b)
|
||||
init_kms_caps(struct drm_device *device)
|
||||
{
|
||||
struct drm_backend *b = device->backend;
|
||||
struct weston_compositor *compositor = b->compositor;
|
||||
uint64_t cap;
|
||||
int ret;
|
||||
|
||||
weston_log("using %s\n", b->drm.filename);
|
||||
weston_log("using %s\n", device->drm.filename);
|
||||
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
|
||||
if (ret != 0 || cap != 1) {
|
||||
weston_log("Error: kernel DRM KMS does not support DRM_CAP_TIMESTAMP_MONOTONIC.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (weston_compositor_set_presentation_clock(b->compositor, CLOCK_MONOTONIC) < 0) {
|
||||
if (weston_compositor_set_presentation_clock(compositor, CLOCK_MONOTONIC) < 0) {
|
||||
weston_log("Error: failed to set presentation clock to CLOCK_MONOTONIC.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_WIDTH, &cap);
|
||||
if (ret == 0)
|
||||
b->cursor_width = cap;
|
||||
device->cursor_width = cap;
|
||||
else
|
||||
b->cursor_width = 64;
|
||||
device->cursor_width = 64;
|
||||
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_CURSOR_HEIGHT, &cap);
|
||||
if (ret == 0)
|
||||
b->cursor_height = cap;
|
||||
device->cursor_height = cap;
|
||||
else
|
||||
b->cursor_height = 64;
|
||||
device->cursor_height = 64;
|
||||
|
||||
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||
if (ret) {
|
||||
weston_log("Error: drm card doesn't support universal planes!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!getenv("WESTON_DISABLE_ATOMIC")) {
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_CRTC_IN_VBLANK_EVENT, &cap);
|
||||
if (ret != 0)
|
||||
cap = 0;
|
||||
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
||||
b->atomic_modeset = ((ret == 0) && (cap == 1));
|
||||
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
||||
device->atomic_modeset = ((ret == 0) && (cap == 1));
|
||||
}
|
||||
weston_log("DRM: %s atomic modesetting\n",
|
||||
b->atomic_modeset ? "supports" : "does not support");
|
||||
device->atomic_modeset ? "supports" : "does not support");
|
||||
|
||||
if (!getenv("WESTON_DISABLE_GBM_MODIFIERS")) {
|
||||
ret = drmGetCap(b->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
|
||||
ret = drmGetCap(device->drm.fd, DRM_CAP_ADDFB2_MODIFIERS, &cap);
|
||||
if (ret == 0)
|
||||
b->fb_modifiers = cap;
|
||||
device->fb_modifiers = cap;
|
||||
}
|
||||
weston_log("DRM: %s GBM modifiers\n",
|
||||
b->fb_modifiers ? "supports" : "does not support");
|
||||
device->fb_modifiers ? "supports" : "does not support");
|
||||
|
||||
drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
|
||||
drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
|
||||
|
||||
/*
|
||||
* KMS support for hardware planes cannot properly synchronize
|
||||
@@ -1489,13 +1536,13 @@ init_kms_caps(struct drm_backend *b)
|
||||
* to a fraction. For cursors, it's not so bad, so they are
|
||||
* enabled.
|
||||
*/
|
||||
if (!b->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
|
||||
b->sprites_are_broken = true;
|
||||
if (!device->atomic_modeset || getenv("WESTON_FORCE_RENDERER"))
|
||||
device->sprites_are_broken = true;
|
||||
|
||||
ret = drmSetClientCap(b->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
|
||||
b->aspect_ratio_supported = (ret == 0);
|
||||
ret = drmSetClientCap(device->drm.fd, DRM_CLIENT_CAP_ASPECT_RATIO, 1);
|
||||
device->aspect_ratio_supported = (ret == 0);
|
||||
weston_log("DRM: %s picture aspect ratio\n",
|
||||
b->aspect_ratio_supported ? "supports" : "does not support");
|
||||
device->aspect_ratio_supported ? "supports" : "does not support");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -53,8 +53,10 @@ static long backlight_get(struct backlight *backlight, char *node)
|
||||
int fd, value;
|
||||
long ret;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight->path, node) < 0)
|
||||
str_printf(&path, "%s/%s", backlight->path, node);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = -1;
|
||||
@@ -67,6 +69,9 @@ static long backlight_get(struct backlight *backlight, char *node)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buffer[ret - 1] == '\n')
|
||||
buffer[ret - 1] = '\0';
|
||||
|
||||
if (!safe_strtoint(buffer, &value)) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
@@ -103,7 +108,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness)
|
||||
int fd;
|
||||
long ret;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight->path, "brightness") < 0)
|
||||
str_printf(&path, "%s/%s", backlight->path, "brightness");
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
fd = open(path, O_RDWR);
|
||||
@@ -118,7 +124,8 @@ long backlight_set_brightness(struct backlight *backlight, long brightness)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (asprintf(&buffer, "%ld", brightness) < 0) {
|
||||
str_printf(&buffer, "%ld", brightness);
|
||||
if (!buffer) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
@@ -171,7 +178,8 @@ struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
if (!syspath)
|
||||
return NULL;
|
||||
|
||||
if (asprintf(&path, "%s/%s", syspath, "device") < 0)
|
||||
str_printf(&path, "%s/%s", syspath, "device");
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
ret = readlink(path, buffer, sizeof(buffer) - 1);
|
||||
@@ -214,11 +222,13 @@ struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if (asprintf(&backlight_path, "%s/%s", "/sys/class/backlight",
|
||||
entry->d_name) < 0)
|
||||
str_printf(&backlight_path, "%s/%s", "/sys/class/backlight",
|
||||
entry->d_name);
|
||||
if (!backlight_path)
|
||||
goto err;
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight_path, "type") < 0) {
|
||||
str_printf(&path, "%s/%s", backlight_path, "type");
|
||||
if (!path) {
|
||||
free(backlight_path);
|
||||
goto err;
|
||||
}
|
||||
@@ -255,7 +265,8 @@ struct backlight *backlight_init(struct udev_device *drm_device,
|
||||
|
||||
free (path);
|
||||
|
||||
if (asprintf(&path, "%s/%s", backlight_path, "device") < 0)
|
||||
str_printf(&path, "%s/%s", backlight_path, "device");
|
||||
if (!path)
|
||||
goto err;
|
||||
|
||||
ret = readlink(path, buffer, sizeof(buffer) - 1);
|
||||
|
||||
@@ -24,6 +24,7 @@ srcs_drm = [
|
||||
'fb.c',
|
||||
'modes.c',
|
||||
'kms.c',
|
||||
'kms-color.c',
|
||||
'state-helpers.c',
|
||||
'state-propose.c',
|
||||
linux_dmabuf_unstable_v1_protocol_c,
|
||||
@@ -32,6 +33,7 @@ srcs_drm = [
|
||||
]
|
||||
|
||||
deps_drm = [
|
||||
dep_libm,
|
||||
dep_libdl,
|
||||
dep_libweston_private,
|
||||
dep_session_helper,
|
||||
|
||||
@@ -98,14 +98,15 @@ drm_subpixel_to_wayland(int drm_value)
|
||||
}
|
||||
|
||||
int
|
||||
drm_mode_ensure_blob(struct drm_backend *backend, struct drm_mode *mode)
|
||||
drm_mode_ensure_blob(struct drm_device *device, struct drm_mode *mode)
|
||||
{
|
||||
struct drm_backend *backend = device->backend;
|
||||
int ret;
|
||||
|
||||
if (mode->blob_id)
|
||||
return 0;
|
||||
|
||||
ret = drmModeCreatePropertyBlob(backend->drm.fd,
|
||||
ret = drmModeCreatePropertyBlob(device->drm.fd,
|
||||
&mode->mode_info,
|
||||
sizeof(mode->mode_info),
|
||||
&mode->blob_id);
|
||||
@@ -304,6 +305,8 @@ edid_parse(struct drm_edid *edid, const uint8_t *data, size_t length)
|
||||
* \param[out] make The monitor make (PNP ID).
|
||||
* \param[out] model The monitor model (name).
|
||||
* \param[out] serial_number The monitor serial number.
|
||||
* \param[out] eotf_mask The monitor supported EOTF modes, combination of
|
||||
* enum weston_eotf_mode bits.
|
||||
*
|
||||
* Each of \c *make, \c *model and \c *serial_number are set only if the
|
||||
* information is found in the EDID. The pointers they are set to must not
|
||||
@@ -315,8 +318,10 @@ find_and_parse_output_edid(struct drm_head *head,
|
||||
drmModeObjectPropertiesPtr props,
|
||||
const char **make,
|
||||
const char **model,
|
||||
const char **serial_number)
|
||||
const char **serial_number,
|
||||
uint32_t *eotf_mask)
|
||||
{
|
||||
struct drm_device *device = head->connector.device;
|
||||
drmModePropertyBlobPtr edid_blob = NULL;
|
||||
uint32_t blob_id;
|
||||
int rc;
|
||||
@@ -328,7 +333,7 @@ find_and_parse_output_edid(struct drm_head *head,
|
||||
if (!blob_id)
|
||||
return;
|
||||
|
||||
edid_blob = drmModeGetPropertyBlob(head->backend->drm.fd, blob_id);
|
||||
edid_blob = drmModeGetPropertyBlob(device->drm.fd, blob_id);
|
||||
if (!edid_blob)
|
||||
return;
|
||||
|
||||
@@ -344,6 +349,21 @@ find_and_parse_output_edid(struct drm_head *head,
|
||||
*serial_number = head->edid.serial_number;
|
||||
}
|
||||
drmModeFreePropertyBlob(edid_blob);
|
||||
|
||||
/* TODO: parse this from EDID */
|
||||
*eotf_mask = WESTON_EOTF_MODE_ALL_MASK;
|
||||
}
|
||||
|
||||
static void
|
||||
prune_eotf_modes_by_kms_support(struct drm_head *head, uint32_t *eotf_mask)
|
||||
{
|
||||
const struct drm_property_info *info;
|
||||
|
||||
/* Without the KMS property, cannot do anything but SDR. */
|
||||
|
||||
info = &head->connector.props[WDRM_CONNECTOR_HDR_OUTPUT_METADATA];
|
||||
if (!head->connector.device->atomic_modeset || info->prop_id == 0)
|
||||
*eotf_mask = WESTON_EOTF_MODE_SDR;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
@@ -406,26 +426,26 @@ drm_output_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
* Destroys a mode, and removes it from the list.
|
||||
*/
|
||||
static void
|
||||
drm_output_destroy_mode(struct drm_backend *backend, struct drm_mode *mode)
|
||||
drm_output_destroy_mode(struct drm_device *device, struct drm_mode *mode)
|
||||
{
|
||||
if (mode->blob_id)
|
||||
drmModeDestroyPropertyBlob(backend->drm.fd, mode->blob_id);
|
||||
drmModeDestroyPropertyBlob(device->drm.fd, mode->blob_id);
|
||||
wl_list_remove(&mode->base.link);
|
||||
free(mode);
|
||||
}
|
||||
|
||||
/** Destroy a list of drm_modes
|
||||
*
|
||||
* @param backend The backend for releasing mode property blobs.
|
||||
* @param device The device for releasing mode property blobs.
|
||||
* @param mode_list The list linked by drm_mode::base.link.
|
||||
*/
|
||||
void
|
||||
drm_mode_list_destroy(struct drm_backend *backend, struct wl_list *mode_list)
|
||||
drm_mode_list_destroy(struct drm_device *device, struct wl_list *mode_list)
|
||||
{
|
||||
struct drm_mode *mode, *next;
|
||||
|
||||
wl_list_for_each_safe(mode, next, mode_list, base.link)
|
||||
drm_output_destroy_mode(backend, mode);
|
||||
drm_output_destroy_mode(device, mode);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -469,16 +489,16 @@ drm_output_choose_mode(struct drm_output *output,
|
||||
struct drm_mode *tmp_mode = NULL, *mode_fall_back = NULL, *mode;
|
||||
enum weston_mode_aspect_ratio src_aspect = WESTON_MODE_PIC_AR_NONE;
|
||||
enum weston_mode_aspect_ratio target_aspect = WESTON_MODE_PIC_AR_NONE;
|
||||
struct drm_backend *b;
|
||||
struct drm_device *device;
|
||||
|
||||
b = to_drm_backend(output->base.compositor);
|
||||
device = output->device;
|
||||
target_aspect = target_mode->aspect_ratio;
|
||||
src_aspect = output->base.current_mode->aspect_ratio;
|
||||
if (output->base.current_mode->width == target_mode->width &&
|
||||
output->base.current_mode->height == target_mode->height &&
|
||||
(output->base.current_mode->refresh == target_mode->refresh ||
|
||||
target_mode->refresh == 0)) {
|
||||
if (!b->aspect_ratio_supported || src_aspect == target_aspect)
|
||||
if (!device->aspect_ratio_supported || src_aspect == target_aspect)
|
||||
return to_drm_mode(output->base.current_mode);
|
||||
}
|
||||
|
||||
@@ -489,7 +509,7 @@ drm_output_choose_mode(struct drm_output *output,
|
||||
mode->mode_info.vdisplay == target_mode->height) {
|
||||
if (mode->base.refresh == target_mode->refresh ||
|
||||
target_mode->refresh == 0) {
|
||||
if (!b->aspect_ratio_supported ||
|
||||
if (!device->aspect_ratio_supported ||
|
||||
src_aspect == target_aspect)
|
||||
return mode;
|
||||
else if (!mode_fall_back)
|
||||
@@ -515,9 +535,12 @@ update_head_from_connector(struct drm_head *head)
|
||||
const char *make = "unknown";
|
||||
const char *model = "unknown";
|
||||
const char *serial_number = "unknown";
|
||||
uint32_t eotf_mask = WESTON_EOTF_MODE_SDR;
|
||||
|
||||
find_and_parse_output_edid(head, props, &make, &model, &serial_number);
|
||||
find_and_parse_output_edid(head, props, &make, &model, &serial_number, &eotf_mask);
|
||||
weston_head_set_monitor_strings(&head->base, make, model, serial_number);
|
||||
prune_eotf_modes_by_kms_support(head, &eotf_mask);
|
||||
weston_head_set_supported_eotf_mask(&head->base, eotf_mask);
|
||||
weston_head_set_non_desktop(&head->base,
|
||||
check_non_desktop(connector, props));
|
||||
weston_head_set_subpixel(&head->base,
|
||||
@@ -539,7 +562,7 @@ update_head_from_connector(struct drm_head *head)
|
||||
* Find the most suitable mode to use for initial setup (or reconfiguration on
|
||||
* hotplug etc) for a DRM output.
|
||||
*
|
||||
* @param backend the DRM backend
|
||||
* @param device the DRM device
|
||||
* @param output DRM output to choose mode for
|
||||
* @param mode Strategy and preference to use when choosing mode
|
||||
* @param modeline Manually-entered mode (may be NULL)
|
||||
@@ -547,7 +570,7 @@ update_head_from_connector(struct drm_head *head)
|
||||
* @returns A mode from the output's mode list, or NULL if none available
|
||||
*/
|
||||
static struct drm_mode *
|
||||
drm_output_choose_initial_mode(struct drm_backend *backend,
|
||||
drm_output_choose_initial_mode(struct drm_device *device,
|
||||
struct drm_output *output,
|
||||
enum weston_drm_backend_output_mode mode,
|
||||
const char *modeline,
|
||||
@@ -571,7 +594,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
|
||||
if (mode == WESTON_DRM_BACKEND_OUTPUT_PREFERRED && modeline) {
|
||||
n = sscanf(modeline, "%dx%d@%d %u:%u", &width, &height,
|
||||
&refresh, &aspect_width, &aspect_height);
|
||||
if (backend->aspect_ratio_supported && n == 5) {
|
||||
if (device->aspect_ratio_supported && n == 5) {
|
||||
if (aspect_width == 4 && aspect_height == 3)
|
||||
aspect_ratio = WESTON_MODE_PIC_AR_4_3;
|
||||
else if (aspect_width == 16 && aspect_height == 9)
|
||||
@@ -602,7 +625,7 @@ drm_output_choose_initial_mode(struct drm_backend *backend,
|
||||
if (width == drm_mode->base.width &&
|
||||
height == drm_mode->base.height &&
|
||||
(refresh == 0 || refresh == drm_mode->mode_info.vrefresh)) {
|
||||
if (!backend->aspect_ratio_supported ||
|
||||
if (!device->aspect_ratio_supported ||
|
||||
aspect_ratio == drm_mode->base.aspect_ratio)
|
||||
configured = drm_mode;
|
||||
else
|
||||
@@ -714,7 +737,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
{
|
||||
struct weston_mode *base;
|
||||
struct drm_mode *mode = NULL;
|
||||
struct drm_backend *backend;
|
||||
struct drm_device *device = output->device;
|
||||
const drmModeModeInfo *chosen = NULL;
|
||||
|
||||
assert(info);
|
||||
@@ -728,8 +751,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
|
||||
if (chosen == info) {
|
||||
assert(mode);
|
||||
backend = to_drm_backend(output->base.compositor);
|
||||
drm_output_destroy_mode(backend, mode);
|
||||
drm_output_destroy_mode(device, mode);
|
||||
chosen = NULL;
|
||||
}
|
||||
|
||||
@@ -756,7 +778,7 @@ drm_output_try_add_mode(struct drm_output *output, const drmModeModeInfo *info)
|
||||
static int
|
||||
drm_output_update_modelist_from_heads(struct drm_output *output)
|
||||
{
|
||||
struct drm_backend *backend = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct weston_head *head_base;
|
||||
struct drm_head *head;
|
||||
drmModeConnector *conn;
|
||||
@@ -765,7 +787,7 @@ drm_output_update_modelist_from_heads(struct drm_output *output)
|
||||
|
||||
assert(!output->base.enabled);
|
||||
|
||||
drm_mode_list_destroy(backend, &output->base.mode_list);
|
||||
drm_mode_list_destroy(device, &output->base.mode_list);
|
||||
|
||||
wl_list_for_each(head_base, &output->base.head_list, output_link) {
|
||||
head = to_drm_head(head_base);
|
||||
@@ -786,7 +808,7 @@ drm_output_set_mode(struct weston_output *base,
|
||||
const char *modeline)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(base);
|
||||
struct drm_backend *b = to_drm_backend(base->compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_head *head = to_drm_head(weston_output_get_first_head(base));
|
||||
|
||||
struct drm_mode *current;
|
||||
@@ -797,7 +819,7 @@ drm_output_set_mode(struct weston_output *base,
|
||||
if (drm_output_update_modelist_from_heads(output) < 0)
|
||||
return -1;
|
||||
|
||||
current = drm_output_choose_initial_mode(b, output, mode, modeline,
|
||||
current = drm_output_choose_initial_mode(device, output, mode, modeline,
|
||||
&head->inherited_mode);
|
||||
if (!current)
|
||||
return -1;
|
||||
|
||||
@@ -73,6 +73,8 @@ drm_plane_state_alloc(struct drm_output_state *state_output,
|
||||
void
|
||||
drm_plane_state_free(struct drm_plane_state *state, bool force)
|
||||
{
|
||||
struct drm_device *device;
|
||||
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
@@ -86,14 +88,17 @@ drm_plane_state_free(struct drm_plane_state *state, bool force)
|
||||
* by the kernel, which means we can safely discard it.
|
||||
*/
|
||||
if (state->damage_blob_id != 0) {
|
||||
drmModeDestroyPropertyBlob(state->plane->backend->drm.fd,
|
||||
device = state->plane->device;
|
||||
|
||||
drmModeDestroyPropertyBlob(device->drm.fd,
|
||||
state->damage_blob_id);
|
||||
state->damage_blob_id = 0;
|
||||
}
|
||||
|
||||
if (force || state != state->plane->state_cur) {
|
||||
drm_fb_unref(state->fb);
|
||||
weston_buffer_reference(&state->fb_ref.buffer, NULL);
|
||||
weston_buffer_reference(&state->fb_ref.buffer, NULL,
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&state->fb_ref.release, NULL);
|
||||
free(state);
|
||||
}
|
||||
@@ -135,10 +140,20 @@ drm_plane_state_duplicate(struct drm_output_state *state_output,
|
||||
* buffer, then we must also transfer the reference on the client
|
||||
* buffer. */
|
||||
if (src->fb) {
|
||||
struct weston_buffer *buffer;
|
||||
|
||||
dst->fb = drm_fb_ref(src->fb);
|
||||
memset(&dst->fb_ref, 0, sizeof(dst->fb_ref));
|
||||
weston_buffer_reference(&dst->fb_ref.buffer,
|
||||
src->fb_ref.buffer.buffer);
|
||||
|
||||
if (src->fb->type == BUFFER_CLIENT ||
|
||||
src->fb->type == BUFFER_DMABUF) {
|
||||
buffer = src->fb_ref.buffer.buffer;
|
||||
} else {
|
||||
buffer = NULL;
|
||||
}
|
||||
weston_buffer_reference(&dst->fb_ref.buffer, buffer,
|
||||
buffer ? BUFFER_MAY_BE_ACCESSED :
|
||||
BUFFER_WILL_NOT_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&dst->fb_ref.release,
|
||||
src->fb_ref.release.buffer_release);
|
||||
} else {
|
||||
@@ -421,11 +436,11 @@ drm_output_state_free(struct drm_output_state *state)
|
||||
* Allocate a new, empty, 'pending state' structure to be used across a
|
||||
* repaint cycle or similar.
|
||||
*
|
||||
* @param backend DRM backend
|
||||
* @param device DRM device
|
||||
* @returns Newly-allocated pending state structure
|
||||
*/
|
||||
struct drm_pending_state *
|
||||
drm_pending_state_alloc(struct drm_backend *backend)
|
||||
drm_pending_state_alloc(struct drm_device *device)
|
||||
{
|
||||
struct drm_pending_state *ret;
|
||||
|
||||
@@ -433,7 +448,7 @@ drm_pending_state_alloc(struct drm_backend *backend)
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->backend = backend;
|
||||
ret->device = device;
|
||||
wl_list_init(&ret->output_list);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "color.h"
|
||||
#include "linux-dmabuf.h"
|
||||
#include "presentation-time-server-protocol.h"
|
||||
#include "linux-dmabuf-unstable-v1-server-protocol.h"
|
||||
|
||||
enum drm_output_propose_state_mode {
|
||||
DRM_OUTPUT_PROPOSE_STATE_MIXED, /**< mix renderer & planes */
|
||||
@@ -63,55 +64,6 @@ drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode)
|
||||
return drm_output_propose_state_mode_as_string[mode];
|
||||
}
|
||||
|
||||
static void
|
||||
drm_output_add_zpos_plane(struct drm_plane *plane, struct wl_list *planes)
|
||||
{
|
||||
struct drm_backend *b = plane->backend;
|
||||
struct drm_plane_zpos *h_plane;
|
||||
struct drm_plane_zpos *plane_zpos;
|
||||
|
||||
plane_zpos = zalloc(sizeof(*plane_zpos));
|
||||
if (!plane_zpos)
|
||||
return;
|
||||
|
||||
plane_zpos->plane = plane;
|
||||
|
||||
drm_debug(b, "\t\t\t\t[plane] plane %d added to candidate list\n",
|
||||
plane->plane_id);
|
||||
|
||||
if (wl_list_empty(planes)) {
|
||||
wl_list_insert(planes, &plane_zpos->link);
|
||||
return;
|
||||
}
|
||||
|
||||
h_plane = wl_container_of(planes->next, h_plane, link);
|
||||
if (h_plane->plane->zpos_max >= plane->zpos_max) {
|
||||
wl_list_insert(planes->prev, &plane_zpos->link);
|
||||
} else {
|
||||
struct drm_plane_zpos *p_zpos = NULL;
|
||||
|
||||
if (wl_list_length(planes) == 1) {
|
||||
wl_list_insert(planes->prev, &plane_zpos->link);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_list_for_each(p_zpos, planes, link) {
|
||||
if (p_zpos->plane->zpos_max >
|
||||
plane_zpos->plane->zpos_max)
|
||||
break;
|
||||
}
|
||||
|
||||
wl_list_insert(p_zpos->link.prev, &plane_zpos->link);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
drm_output_destroy_zpos_plane(struct drm_plane_zpos *plane_zpos)
|
||||
{
|
||||
wl_list_remove(&plane_zpos->link);
|
||||
free(plane_zpos);
|
||||
}
|
||||
|
||||
static bool
|
||||
drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
|
||||
struct drm_output_state *output_state)
|
||||
@@ -125,87 +77,79 @@ drm_output_check_plane_has_view_assigned(struct drm_plane *plane,
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_prepare_overlay_view(struct drm_plane *plane,
|
||||
drm_output_try_view_on_plane(struct drm_plane *plane,
|
||||
struct drm_output_state *output_state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct drm_backend *b = to_drm_backend(ec);
|
||||
struct weston_surface *surface = ev->surface;
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane_state *state = NULL;
|
||||
int ret;
|
||||
|
||||
assert(!b->sprites_are_broken);
|
||||
assert(b->atomic_modeset);
|
||||
|
||||
if (!fb) {
|
||||
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
|
||||
" couldn't get fb\n", ev);
|
||||
return NULL;
|
||||
}
|
||||
assert(!device->sprites_are_broken);
|
||||
assert(device->atomic_modeset);
|
||||
assert(fb);
|
||||
assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY ||
|
||||
(mode == DRM_OUTPUT_PROPOSE_STATE_MIXED &&
|
||||
plane->type == WDRM_PLANE_TYPE_OVERLAY));
|
||||
|
||||
state = drm_output_state_get_plane(output_state, plane);
|
||||
/* we can't have a 'pending' framebuffer as never set one before reaching here */
|
||||
assert(!state->fb);
|
||||
|
||||
state->ev = ev;
|
||||
state->output = output;
|
||||
|
||||
if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
|
||||
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
|
||||
drm_debug(b, "\t\t\t\t[view] not placing view %p on plane: "
|
||||
"unsuitable transform\n", ev);
|
||||
drm_plane_state_put_back(state);
|
||||
state = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If the surface buffer has an in-fence fd, but the plane
|
||||
* doesn't support fences, we can't place the buffer on this
|
||||
* plane. */
|
||||
if (ev->surface->acquire_fence_fd >= 0 &&
|
||||
plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
|
||||
drm_debug(b, "\t\t\t\t[overlay] not placing view %p on overlay: "
|
||||
"no in-fence support\n", ev);
|
||||
drm_plane_state_put_back(state);
|
||||
state = NULL;
|
||||
goto out;
|
||||
/* Should've been ensured by weston_view_matches_entire_output. */
|
||||
if (plane->type == WDRM_PLANE_TYPE_PRIMARY) {
|
||||
assert(state->dest_x == 0 && state->dest_y == 0 &&
|
||||
state->dest_w == (unsigned) output->base.current_mode->width &&
|
||||
state->dest_h == (unsigned) output->base.current_mode->height);
|
||||
}
|
||||
|
||||
/* We hold one reference for the lifetime of this function; from
|
||||
* calling drm_fb_get_from_view() in drm_output_prepare_plane_view(),
|
||||
* so, we take another reference here to live within the state. */
|
||||
state->ev = ev;
|
||||
state->fb = drm_fb_ref(fb);
|
||||
|
||||
state->in_fence_fd = ev->surface->acquire_fence_fd;
|
||||
|
||||
/* In planes-only mode, we don't have an incremental state to
|
||||
* test against, so we just hope it'll work. */
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
|
||||
drm_debug(b, "\t\t\t[overlay] provisionally placing "
|
||||
"view %p on overlay %lu in planes-only mode\n",
|
||||
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY &&
|
||||
drm_pending_state_test(output_state->pending_state) != 0) {
|
||||
drm_debug(b, "\t\t\t[view] not placing view %p on plane %lu: "
|
||||
"atomic test failed\n",
|
||||
ev, (unsigned long) plane->plane_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drm_pending_state_test(output_state->pending_state);
|
||||
if (ret == 0) {
|
||||
drm_debug(b, "\t\t\t[overlay] provisionally placing "
|
||||
"view %p on overlay %d in mixed mode\n",
|
||||
ev, plane->plane_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
drm_debug(b, "\t\t\t[overlay] not placing view %p on overlay %lu "
|
||||
"in mixed mode: kernel test failed\n",
|
||||
drm_debug(b, "\t\t\t[view] provisionally placing view %p on plane %lu\n",
|
||||
ev, (unsigned long) plane->plane_id);
|
||||
|
||||
drm_plane_state_put_back(state);
|
||||
state = NULL;
|
||||
/* Take a reference on the buffer so that we don't release it
|
||||
* back to the client until we're done with it; cursor buffers
|
||||
* don't require a reference since we copy them. */
|
||||
assert(state->fb_ref.buffer.buffer == NULL);
|
||||
assert(state->fb_ref.release.buffer_release == NULL);
|
||||
weston_buffer_reference(&state->fb_ref.buffer,
|
||||
surface->buffer_ref.buffer,
|
||||
BUFFER_MAY_BE_ACCESSED);
|
||||
weston_buffer_release_reference(&state->fb_ref.release,
|
||||
surface->buffer_release_ref.buffer_release);
|
||||
|
||||
return state;
|
||||
|
||||
out:
|
||||
return state;
|
||||
drm_plane_state_put_back(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef BUILD_DRM_GBM
|
||||
@@ -218,18 +162,18 @@ out:
|
||||
static void
|
||||
cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
|
||||
{
|
||||
struct drm_backend *b = plane_state->plane->backend;
|
||||
struct drm_output *output = plane_state->output;
|
||||
struct drm_device *device = output->device;
|
||||
struct gbm_bo *bo = plane_state->fb->bo;
|
||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||
uint32_t buf[b->cursor_width * b->cursor_height];
|
||||
uint32_t buf[device->cursor_width * device->cursor_height];
|
||||
int32_t stride;
|
||||
uint8_t *s;
|
||||
int i;
|
||||
|
||||
assert(buffer && buffer->shm_buffer);
|
||||
assert(buffer->shm_buffer == wl_shm_buffer_get(buffer->resource));
|
||||
assert(buffer->width <= b->cursor_width);
|
||||
assert(buffer->height <= b->cursor_height);
|
||||
assert(buffer->width <= device->cursor_width);
|
||||
assert(buffer->height <= device->cursor_height);
|
||||
|
||||
memset(buf, 0, sizeof buf);
|
||||
stride = wl_shm_buffer_get_stride(buffer->shm_buffer);
|
||||
@@ -237,7 +181,7 @@ cursor_bo_update(struct drm_plane_state *plane_state, struct weston_view *ev)
|
||||
|
||||
wl_shm_buffer_begin_access(buffer->shm_buffer);
|
||||
for (i = 0; i < buffer->height; i++)
|
||||
memcpy(buf + i * b->cursor_width,
|
||||
memcpy(buf + i * device->cursor_width,
|
||||
s + i * stride,
|
||||
buffer->width * 4);
|
||||
wl_shm_buffer_end_access(buffer->shm_buffer);
|
||||
@@ -251,32 +195,23 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
struct weston_view *ev, uint64_t zpos)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_plane *plane = output->cursor_plane;
|
||||
struct drm_plane_state *plane_state;
|
||||
bool needs_update = false;
|
||||
struct weston_buffer *buffer = ev->surface->buffer_ref.buffer;
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
|
||||
assert(!b->cursors_are_broken);
|
||||
|
||||
if (!plane)
|
||||
return NULL;
|
||||
|
||||
if (!plane->state_cur->complete)
|
||||
return NULL;
|
||||
|
||||
if (plane->state_cur->output && plane->state_cur->output != output)
|
||||
return NULL;
|
||||
assert(!device->cursors_are_broken);
|
||||
assert(plane);
|
||||
assert(plane->state_cur->complete);
|
||||
assert(!plane->state_cur->output || plane->state_cur->output == output);
|
||||
|
||||
/* We use GBM to import SHM buffers. */
|
||||
if (b->gbm == NULL)
|
||||
return NULL;
|
||||
assert(b->gbm);
|
||||
|
||||
plane_state = drm_output_state_get_plane(output_state, plane);
|
||||
|
||||
if (plane_state && plane_state->fb)
|
||||
return NULL;
|
||||
assert(!plane_state->fb);
|
||||
|
||||
/* We can't scale with the legacy API, and we don't try to account for
|
||||
* simple cropping/translation in cursor_bo_update. */
|
||||
@@ -287,19 +222,9 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (buffer->width > b->cursor_width ||
|
||||
buffer->height > b->cursor_height) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
|
||||
"(surface buffer (%dx%d) larger than permitted"
|
||||
" (%dx%d))\n", p_name, ev, p_name,
|
||||
buffer->width, buffer->height,
|
||||
b->cursor_width, b->cursor_height);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
|
||||
plane_state->src_w > (unsigned) b->cursor_width << 16 ||
|
||||
plane_state->src_h > (unsigned) b->cursor_height << 16 ||
|
||||
plane_state->src_w > (unsigned) device->cursor_width << 16 ||
|
||||
plane_state->src_h > (unsigned) device->cursor_height << 16 ||
|
||||
plane_state->src_w != plane_state->dest_w << 16 ||
|
||||
plane_state->src_h != plane_state->dest_h << 16) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not assigning view %p to %s plane "
|
||||
@@ -338,10 +263,10 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
* a buffer which is always cursor_width x cursor_height, even if the
|
||||
* surface we want to promote is actually smaller than this. Manually
|
||||
* mangle the plane state to deal with this. */
|
||||
plane_state->src_w = b->cursor_width << 16;
|
||||
plane_state->src_h = b->cursor_height << 16;
|
||||
plane_state->dest_w = b->cursor_width;
|
||||
plane_state->dest_h = b->cursor_height;
|
||||
plane_state->src_w = device->cursor_width << 16;
|
||||
plane_state->src_h = device->cursor_height << 16;
|
||||
plane_state->dest_w = device->cursor_width;
|
||||
plane_state->dest_h = device->cursor_height;
|
||||
|
||||
drm_debug(b, "\t\t\t\t[%s] provisionally assigned view %p to cursor\n",
|
||||
p_name, ev);
|
||||
@@ -361,190 +286,6 @@ drm_output_prepare_cursor_view(struct drm_output_state *output_state,
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_prepare_scanout_view(struct drm_output_state *output_state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
{
|
||||
struct drm_output *output = output_state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_plane *scanout_plane = output->scanout_plane;
|
||||
struct drm_plane_state *state;
|
||||
const char *p_name = drm_output_get_plane_type_name(scanout_plane);
|
||||
|
||||
assert(!b->sprites_are_broken);
|
||||
assert(b->atomic_modeset);
|
||||
assert(mode == DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY);
|
||||
|
||||
/* Check the view spans exactly the output size, calculated in the
|
||||
* logical co-ordinate space. */
|
||||
if (!weston_view_matches_output_entirely(ev, &output->base)) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
" view does not match output entirely\n",
|
||||
p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If the surface buffer has an in-fence fd, but the plane doesn't
|
||||
* support fences, we can't place the buffer on this plane. */
|
||||
if (ev->surface->acquire_fence_fd >= 0 &&
|
||||
scanout_plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
"no in-fence support\n", p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!fb) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
" couldn't get fb\n", p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state = drm_output_state_get_plane(output_state, scanout_plane);
|
||||
|
||||
/* The only way we can already have a buffer in the scanout plane is
|
||||
* if we are in mixed mode, or if a client buffer has already been
|
||||
* placed into scanout. The former case will never call into here,
|
||||
* and in the latter case, the view must have been marked as occluded,
|
||||
* meaning we should never have ended up here. */
|
||||
assert(!state->fb);
|
||||
|
||||
/* take another reference here to live within the state */
|
||||
state->fb = drm_fb_ref(fb);
|
||||
state->ev = ev;
|
||||
state->output = output;
|
||||
if (!drm_plane_state_coords_for_view(state, ev, zpos)) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
"unsuitable transform\n", p_name, ev, p_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (state->dest_x != 0 || state->dest_y != 0 ||
|
||||
state->dest_w != (unsigned) output->base.current_mode->width ||
|
||||
state->dest_h != (unsigned) output->base.current_mode->height) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
" invalid plane state\n", p_name, ev, p_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
state->in_fence_fd = ev->surface->acquire_fence_fd;
|
||||
|
||||
/* In plane-only mode, we don't need to test the state now, as we
|
||||
* will only test it once at the end. */
|
||||
return state;
|
||||
|
||||
err:
|
||||
drm_plane_state_put_back(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_try_view_on_plane(struct drm_plane *plane,
|
||||
struct drm_output_state *state,
|
||||
struct weston_view *ev,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_fb *fb, uint64_t zpos)
|
||||
{
|
||||
struct drm_backend *b = state->pending_state->backend;
|
||||
struct weston_output *wet_output = &state->output->base;
|
||||
bool view_matches_entire_output, scanout_has_view_assigned;
|
||||
struct drm_plane *scanout_plane = state->output->scanout_plane;
|
||||
struct drm_plane_state *ps = NULL;
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
struct weston_surface *surface = ev->surface;
|
||||
enum {
|
||||
NO_PLANES, /* generic err-handle */
|
||||
NO_PLANES_ACCEPTED,
|
||||
PLACED_ON_PLANE,
|
||||
} availability = NO_PLANES;
|
||||
|
||||
/* sanity checks in case we over/underflow zpos or pass incorrect
|
||||
* values */
|
||||
assert(zpos <= plane->zpos_max ||
|
||||
zpos != DRM_PLANE_ZPOS_INVALID_PLANE);
|
||||
|
||||
switch (plane->type) {
|
||||
case WDRM_PLANE_TYPE_CURSOR:
|
||||
if (b->cursors_are_broken) {
|
||||
availability = NO_PLANES_ACCEPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps = drm_output_prepare_cursor_view(state, ev, zpos);
|
||||
if (ps)
|
||||
availability = PLACED_ON_PLANE;
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_OVERLAY:
|
||||
/* do not attempt to place it in the overlay if we don't have
|
||||
* anything in the scanout/primary and the view doesn't cover
|
||||
* the entire output */
|
||||
view_matches_entire_output =
|
||||
weston_view_matches_output_entirely(ev, wet_output);
|
||||
scanout_has_view_assigned =
|
||||
drm_output_check_plane_has_view_assigned(scanout_plane,
|
||||
state);
|
||||
|
||||
if (view_matches_entire_output && !scanout_has_view_assigned) {
|
||||
availability = NO_PLANES_ACCEPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps = drm_output_prepare_overlay_view(plane, state, ev, mode,
|
||||
fb, zpos);
|
||||
if (ps)
|
||||
availability = PLACED_ON_PLANE;
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_PRIMARY:
|
||||
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY) {
|
||||
availability = NO_PLANES_ACCEPTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps = drm_output_prepare_scanout_view(state, ev, mode,
|
||||
fb, zpos);
|
||||
if (ps)
|
||||
availability = PLACED_ON_PLANE;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
switch (availability) {
|
||||
case NO_PLANES:
|
||||
/* set initial to this catch-all case, such that
|
||||
* prepare_cursor/overlay/scanout() should have/contain the
|
||||
* reason for failling */
|
||||
break;
|
||||
case NO_PLANES_ACCEPTED:
|
||||
drm_debug(b, "\t\t\t\t[plane] plane %d refusing to "
|
||||
"place view %p in %s\n",
|
||||
plane->plane_id, ev, p_name);
|
||||
break;
|
||||
case PLACED_ON_PLANE:
|
||||
/* Take a reference on the buffer so that we don't release it
|
||||
* back to the client until we're done with it; cursor buffers
|
||||
* don't require a reference since we copy them. */
|
||||
assert(ps->fb_ref.buffer.buffer == NULL);
|
||||
assert(ps->fb_ref.release.buffer_release == NULL);
|
||||
if (ps->plane->type == WDRM_PLANE_TYPE_CURSOR) {
|
||||
assert(ps->fb->type == BUFFER_CURSOR);
|
||||
} else if (fb->type == BUFFER_CLIENT || fb->type == BUFFER_DMABUF) {
|
||||
assert(ps->fb == fb);
|
||||
weston_buffer_reference(&ps->fb_ref.buffer,
|
||||
surface->buffer_ref.buffer);
|
||||
weston_buffer_release_reference(&ps->fb_ref.release,
|
||||
surface->buffer_release_ref.buffer_release);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return ps;
|
||||
}
|
||||
|
||||
static void
|
||||
drm_output_check_zpos_plane_states(struct drm_output_state *state)
|
||||
{
|
||||
@@ -585,12 +326,13 @@ drm_output_check_zpos_plane_states(struct drm_output_state *state)
|
||||
}
|
||||
|
||||
static bool
|
||||
dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
|
||||
dmabuf_feedback_maybe_update(struct drm_device *device, struct weston_view *ev,
|
||||
uint32_t try_view_on_plane_failure_reasons)
|
||||
{
|
||||
struct weston_dmabuf_feedback *dmabuf_feedback = ev->surface->dmabuf_feedback;
|
||||
struct weston_dmabuf_feedback_tranche *scanout_tranche;
|
||||
dev_t scanout_dev = b->drm.devnum;
|
||||
struct drm_backend *b = device->backend;
|
||||
dev_t scanout_dev = device->drm.devnum;
|
||||
uint32_t scanout_flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT;
|
||||
uint32_t action_needed = ACTION_NEEDED_NONE;
|
||||
struct timespec current_time, delta_time;
|
||||
@@ -603,7 +345,8 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
|
||||
(FAILURE_REASONS_ADD_FB_FAILED |
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE |
|
||||
FAILURE_REASONS_DMABUF_MODIFIER_INVALID |
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED))
|
||||
FAILURE_REASONS_GBM_BO_IMPORT_FAILED |
|
||||
FAILURE_REASONS_GBM_BO_GET_HANDLE_FAILED))
|
||||
action_needed |= ACTION_NEEDED_ADD_SCANOUT_TRANCHE;
|
||||
|
||||
assert(action_needed != (ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE |
|
||||
@@ -681,58 +424,161 @@ dmabuf_feedback_maybe_update(struct drm_backend *b, struct weston_view *ev,
|
||||
}
|
||||
|
||||
static struct drm_plane_state *
|
||||
drm_output_prepare_plane_view(struct drm_output_state *state,
|
||||
struct weston_view *ev,
|
||||
drm_output_find_plane_for_view(struct drm_output_state *state,
|
||||
struct weston_paint_node *pnode,
|
||||
enum drm_output_propose_state_mode mode,
|
||||
struct drm_plane_state *scanout_state,
|
||||
uint64_t current_lowest_zpos,
|
||||
uint32_t *try_view_on_plane_failure_reasons)
|
||||
uint64_t current_lowest_zpos)
|
||||
{
|
||||
struct drm_output *output = state->output;
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
|
||||
struct drm_plane_state *ps = NULL;
|
||||
struct drm_plane *plane;
|
||||
struct drm_plane_zpos *p_zpos, *p_zpos_next;
|
||||
struct wl_list zpos_candidate_list;
|
||||
|
||||
struct weston_view *ev = pnode->view;
|
||||
struct weston_buffer *buffer;
|
||||
struct wl_shm_buffer *shmbuf;
|
||||
struct drm_fb *fb;
|
||||
struct drm_fb *fb = NULL;
|
||||
|
||||
wl_list_init(&zpos_candidate_list);
|
||||
bool view_matches_entire_output, scanout_has_view_assigned;
|
||||
uint32_t possible_plane_mask = 0;
|
||||
|
||||
pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
|
||||
|
||||
/* check view for valid buffer, doesn't make sense to even try */
|
||||
if (!weston_view_has_valid_buffer(ev))
|
||||
return ps;
|
||||
if (!weston_view_has_valid_buffer(ev)) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = ev->surface->buffer_ref.buffer;
|
||||
shmbuf = wl_shm_buffer_get(buffer->resource);
|
||||
fb = drm_fb_get_from_view(state, ev, try_view_on_plane_failure_reasons);
|
||||
if (!shmbuf && !fb)
|
||||
if (buffer->type == WESTON_BUFFER_SOLID) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
} else if (buffer->type == WESTON_BUFFER_SHM) {
|
||||
if (!output->cursor_plane || device->cursors_are_broken) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Even though this is a SHM buffer, pixel_format stores the
|
||||
* format code as DRM FourCC */
|
||||
if (buffer->pixel_format->format != DRM_FORMAT_ARGB8888) {
|
||||
drm_debug(b, "\t\t\t\t[view] not placing view %p on "
|
||||
"plane; SHM buffers must be ARGB8888 for "
|
||||
"cursor view\n", ev);
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buffer->width > device->cursor_width ||
|
||||
buffer->height > device->cursor_height) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(buffer (%dx%d) too large for cursor plane)\n",
|
||||
ev, buffer->width, buffer->height);
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
possible_plane_mask = (1 << output->cursor_plane->plane_idx);
|
||||
} else {
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p "
|
||||
"to plane: renderer-only mode\n", ev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fb = drm_fb_get_from_view(state, ev,
|
||||
&pnode->try_view_on_plane_failure_reasons);
|
||||
if (!fb) {
|
||||
drm_debug(b, "\t\t\t[view] couldn't get FB for view: 0x%lx\n",
|
||||
(unsigned long) pnode->try_view_on_plane_failure_reasons);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
possible_plane_mask = fb->plane_mask;
|
||||
}
|
||||
|
||||
view_matches_entire_output =
|
||||
weston_view_matches_output_entirely(ev, &output->base);
|
||||
scanout_has_view_assigned =
|
||||
drm_output_check_plane_has_view_assigned(output->scanout_plane,
|
||||
state);
|
||||
|
||||
/* assemble a list with possible candidates */
|
||||
wl_list_for_each(plane, &b->plane_list, link) {
|
||||
wl_list_for_each(plane, &device->plane_list, link) {
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
uint64_t zpos;
|
||||
|
||||
if (possible_plane_mask == 0)
|
||||
break;
|
||||
|
||||
if (!(possible_plane_mask & (1 << plane->plane_idx)))
|
||||
continue;
|
||||
|
||||
possible_plane_mask &= ~(1 << plane->plane_idx);
|
||||
|
||||
switch (plane->type) {
|
||||
case WDRM_PLANE_TYPE_CURSOR:
|
||||
assert(buffer->shm_buffer);
|
||||
assert(plane == output->cursor_plane);
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_PRIMARY:
|
||||
assert(fb);
|
||||
if (plane != output->scanout_plane)
|
||||
continue;
|
||||
if (mode != DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY)
|
||||
continue;
|
||||
if (!view_matches_entire_output)
|
||||
continue;
|
||||
break;
|
||||
case WDRM_PLANE_TYPE_OVERLAY:
|
||||
assert(fb);
|
||||
assert(mode != DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY);
|
||||
/* if the view covers the whole output, put it in the
|
||||
* scanout plane, not overlay */
|
||||
if (view_matches_entire_output &&
|
||||
!scanout_has_view_assigned)
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
assert(false && "unknown plane type");
|
||||
}
|
||||
|
||||
if (!drm_plane_is_available(plane, output))
|
||||
continue;
|
||||
|
||||
if (drm_output_check_plane_has_view_assigned(plane, state)) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to"
|
||||
" candidate list: view already assigned "
|
||||
"to a plane\n", plane->plane_id);
|
||||
drm_debug(b, "\t\t\t\t[plane] not trying plane %d: "
|
||||
"another view already assigned\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plane->zpos_min >= current_lowest_zpos) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
|
||||
"candidate list: minimum zpos (%"PRIu64") "
|
||||
"plane's above current lowest zpos "
|
||||
"(%"PRIu64")\n", plane->plane_id,
|
||||
plane->zpos_min, current_lowest_zpos);
|
||||
drm_debug(b, "\t\t\t\t[plane] not trying plane %d: "
|
||||
"plane's minimum zpos (%"PRIu64") above "
|
||||
"current lowest zpos (%"PRIu64")\n",
|
||||
plane->plane_id, plane->zpos_min,
|
||||
current_lowest_zpos);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the surface buffer has an in-fence fd, but the plane doesn't
|
||||
* support fences, we can't place the buffer on this plane. */
|
||||
if (ev->surface->acquire_fence_fd >= 0 &&
|
||||
plane->props[WDRM_PLANE_IN_FENCE_FD].prop_id == 0) {
|
||||
drm_debug(b, "\t\t\t\t[%s] not placing view %p on %s: "
|
||||
"no in-fence support\n", p_name, ev, p_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_MIXED) {
|
||||
assert(scanout_state != NULL);
|
||||
if (scanout_state->zpos >= plane->zpos_max) {
|
||||
@@ -746,49 +592,6 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == DRM_OUTPUT_PROPOSE_STATE_RENDERER_ONLY &&
|
||||
(plane->type == WDRM_PLANE_TYPE_OVERLAY ||
|
||||
plane->type == WDRM_PLANE_TYPE_PRIMARY)) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
|
||||
"candidate list: renderer-only mode\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plane->type == WDRM_PLANE_TYPE_CURSOR &&
|
||||
(!shmbuf || wl_shm_buffer_get_format(shmbuf) != WL_SHM_FORMAT_ARGB8888)) {
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d, type cursor to "
|
||||
"candidate list: cursor planes only support ARGB8888"
|
||||
"wl_shm buffers and the view buffer is of another type\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (plane->type != WDRM_PLANE_TYPE_CURSOR &&
|
||||
(!fb || !(fb->plane_mask & (1 << plane->plane_idx)))) {
|
||||
*try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_FB_FORMAT_INCOMPATIBLE;
|
||||
drm_debug(b, "\t\t\t\t[plane] not adding plane %d to "
|
||||
"candidate list: invalid pixel format\n",
|
||||
plane->plane_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
drm_output_add_zpos_plane(plane, &zpos_candidate_list);
|
||||
}
|
||||
|
||||
/* go over the potential candidate list and try to find a possible
|
||||
* plane suitable for \c ev; start with the highest zpos value of a
|
||||
* plane to maximize our chances, but do note we pass the zpos value
|
||||
* based on current tracked value by \c current_lowest_zpos_in_use */
|
||||
while (!wl_list_empty(&zpos_candidate_list)) {
|
||||
struct drm_plane_zpos *head_p_zpos =
|
||||
wl_container_of(zpos_candidate_list.next,
|
||||
head_p_zpos, link);
|
||||
struct drm_plane *plane = head_p_zpos->plane;
|
||||
const char *p_name = drm_output_get_plane_type_name(plane);
|
||||
uint64_t zpos;
|
||||
|
||||
if (current_lowest_zpos == DRM_PLANE_ZPOS_INVALID_PLANE)
|
||||
zpos = plane->zpos_max;
|
||||
else
|
||||
@@ -798,20 +601,32 @@ drm_output_prepare_plane_view(struct drm_output_state *state,
|
||||
"from candidate list, type: %s\n",
|
||||
plane->plane_id, p_name);
|
||||
|
||||
if (plane->type == WDRM_PLANE_TYPE_CURSOR) {
|
||||
ps = drm_output_prepare_cursor_view(state, ev, zpos);
|
||||
} else {
|
||||
ps = drm_output_try_view_on_plane(plane, state, ev,
|
||||
mode, fb, zpos);
|
||||
drm_output_destroy_zpos_plane(head_p_zpos);
|
||||
}
|
||||
|
||||
if (ps) {
|
||||
drm_debug(b, "\t\t\t\t[view] view %p has been placed to "
|
||||
"%s plane with computed zpos %"PRIu64"\n",
|
||||
ev, p_name, zpos);
|
||||
break;
|
||||
}
|
||||
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_PLANES_REJECTED;
|
||||
}
|
||||
|
||||
wl_list_for_each_safe(p_zpos, p_zpos_next, &zpos_candidate_list, link)
|
||||
drm_output_destroy_zpos_plane(p_zpos);
|
||||
if (!ps &&
|
||||
pnode->try_view_on_plane_failure_reasons == FAILURE_REASONS_NONE) {
|
||||
pnode->try_view_on_plane_failure_reasons |=
|
||||
FAILURE_REASONS_NO_PLANES_AVAILABLE;
|
||||
}
|
||||
|
||||
/* if we have a plane state, it has its own ref to the fb; if not then
|
||||
* we drop ours here */
|
||||
drm_fb_unref(fb);
|
||||
return ps;
|
||||
}
|
||||
@@ -822,7 +637,8 @@ drm_output_propose_state(struct weston_output *output_base,
|
||||
enum drm_output_propose_state_mode mode)
|
||||
{
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_backend *b = to_drm_backend(output->base.compositor);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct weston_paint_node *pnode;
|
||||
struct drm_output_state *state;
|
||||
struct drm_plane_state *scanout_state = NULL;
|
||||
@@ -957,12 +773,29 @@ drm_output_propose_state(struct weston_output *output_base,
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
if (!b->gbm) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(GBM not available)\n", ev);
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
if (!weston_view_has_valid_buffer(ev)) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(no buffer available)\n", ev);
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
/* We can support this with the 'CRTC background colour' property,
|
||||
* if it is fullscreen (i.e. we disable the primary plane), and
|
||||
* opaque (as it is only shown in the absence of any covering
|
||||
* plane, not as a replacement for the primary plane per se). */
|
||||
if (ev->surface->buffer_ref.buffer &&
|
||||
ev->surface->buffer_ref.buffer->type == WESTON_BUFFER_SOLID) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
"(solid-colour surface)\n", ev);
|
||||
force_renderer = true;
|
||||
}
|
||||
|
||||
if (pnode->surf_xform.transform != NULL ||
|
||||
!pnode->surf_xform.identity_pipeline) {
|
||||
drm_debug(b, "\t\t\t\t[view] not assigning view %p to plane "
|
||||
@@ -996,15 +829,9 @@ drm_output_propose_state(struct weston_output *output_base,
|
||||
if (!force_renderer) {
|
||||
drm_debug(b, "\t\t\t[plane] started with zpos %"PRIu64"\n",
|
||||
current_lowest_zpos);
|
||||
ps = drm_output_prepare_plane_view(state, ev, mode,
|
||||
ps = drm_output_find_plane_for_view(state, pnode, mode,
|
||||
scanout_state,
|
||||
current_lowest_zpos,
|
||||
&pnode->try_view_on_plane_failure_reasons);
|
||||
/* If we were able to place the view in a plane, set
|
||||
* failure reasons to none. */
|
||||
if (ps)
|
||||
pnode->try_view_on_plane_failure_reasons =
|
||||
FAILURE_REASONS_NONE;
|
||||
current_lowest_zpos);
|
||||
} else {
|
||||
/* We are forced to place the view in the renderer, set
|
||||
* the failure reason accordingly. */
|
||||
@@ -1088,21 +915,24 @@ err:
|
||||
}
|
||||
|
||||
void
|
||||
drm_assign_planes(struct weston_output *output_base, void *repaint_data)
|
||||
drm_assign_planes(struct weston_output *output_base)
|
||||
{
|
||||
struct drm_backend *b = to_drm_backend(output_base->compositor);
|
||||
struct drm_pending_state *pending_state = repaint_data;
|
||||
struct drm_output *output = to_drm_output(output_base);
|
||||
struct drm_device *device = output->device;
|
||||
struct drm_backend *b = device->backend;
|
||||
struct drm_pending_state *pending_state = device->repaint_data;
|
||||
struct drm_output_state *state = NULL;
|
||||
struct drm_plane_state *plane_state;
|
||||
struct weston_paint_node *pnode;
|
||||
struct weston_plane *primary = &output_base->compositor->primary_plane;
|
||||
enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY;
|
||||
|
||||
assert(output);
|
||||
|
||||
drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n",
|
||||
output_base->name, (unsigned long) output_base->id);
|
||||
|
||||
if (!b->sprites_are_broken && !output->virtual) {
|
||||
if (!device->sprites_are_broken && !output->virtual && b->gbm) {
|
||||
drm_debug(b, "\t[repaint] trying planes-only build state\n");
|
||||
state = drm_output_propose_state(output_base, pending_state, mode);
|
||||
if (!state) {
|
||||
@@ -1144,26 +974,24 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data)
|
||||
|
||||
/* Update dmabuf-feedback if needed */
|
||||
if (ev->surface->dmabuf_feedback)
|
||||
dmabuf_feedback_maybe_update(b, ev,
|
||||
dmabuf_feedback_maybe_update(device, ev,
|
||||
pnode->try_view_on_plane_failure_reasons);
|
||||
pnode->try_view_on_plane_failure_reasons = FAILURE_REASONS_NONE;
|
||||
|
||||
/* Test whether this buffer can ever go into a plane:
|
||||
* non-shm, or small enough to be a cursor.
|
||||
*
|
||||
* Also, keep a reference when using the pixman renderer.
|
||||
* That makes it possible to do a seamless switch to the GL
|
||||
* renderer and since the pixman renderer keeps a reference
|
||||
* to the buffer anyway, there is no side effects.
|
||||
*/
|
||||
if (b->use_pixman ||
|
||||
(weston_view_has_valid_buffer(ev) &&
|
||||
(!wl_shm_buffer_get(ev->surface->buffer_ref.buffer->resource) ||
|
||||
(ev->surface->width <= b->cursor_width &&
|
||||
ev->surface->height <= b->cursor_height))))
|
||||
ev->surface->keep_buffer = true;
|
||||
else
|
||||
* non-shm, or small enough to be a cursor. */
|
||||
ev->surface->keep_buffer = false;
|
||||
if (weston_view_has_valid_buffer(ev)) {
|
||||
struct weston_buffer *buffer =
|
||||
ev->surface->buffer_ref.buffer;
|
||||
if (buffer->type == WESTON_BUFFER_DMABUF ||
|
||||
buffer->type == WESTON_BUFFER_RENDERER_OPAQUE)
|
||||
ev->surface->keep_buffer = true;
|
||||
else if (buffer->type == WESTON_BUFFER_SHM &&
|
||||
(ev->surface->width <= device->cursor_width &&
|
||||
ev->surface->height <= device->cursor_height))
|
||||
ev->surface->keep_buffer = true;
|
||||
}
|
||||
|
||||
/* This is a bit unpleasant, but lacking a temporary place to
|
||||
* hang a plane off the view, we have to do a nested walk.
|
||||
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
* Copyright © 2011 Intel Corporation
|
||||
* Copyright © 2012 Raspberry Pi Foundation
|
||||
* Copyright © 2013 Philip Withnall
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/input.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <libudev.h>
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/backend-fbdev.h>
|
||||
#include "launcher-util.h"
|
||||
#include "pixman-renderer.h"
|
||||
#include "libinput-seat.h"
|
||||
#include "presentation-time-server-protocol.h"
|
||||
|
||||
struct fbdev_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
uint32_t prev_state;
|
||||
|
||||
struct udev *udev;
|
||||
struct udev_input input;
|
||||
uint32_t output_transform;
|
||||
struct wl_listener session_listener;
|
||||
};
|
||||
|
||||
struct fbdev_screeninfo {
|
||||
unsigned int x_resolution; /* pixels, visible area */
|
||||
unsigned int y_resolution; /* pixels, visible area */
|
||||
unsigned int width_mm; /* visible screen width in mm */
|
||||
unsigned int height_mm; /* visible screen height in mm */
|
||||
unsigned int bits_per_pixel;
|
||||
|
||||
size_t buffer_length; /* length of frame buffer memory in bytes */
|
||||
size_t line_length; /* length of a line in bytes */
|
||||
char id[16]; /* screen identifier */
|
||||
|
||||
pixman_format_code_t pixel_format; /* frame buffer pixel format */
|
||||
unsigned int refresh_rate; /* Hertz */
|
||||
};
|
||||
|
||||
struct fbdev_head {
|
||||
struct weston_head base;
|
||||
|
||||
/* Frame buffer details. */
|
||||
char *device;
|
||||
struct fbdev_screeninfo fb_info;
|
||||
};
|
||||
|
||||
struct fbdev_output {
|
||||
struct fbdev_backend *backend;
|
||||
struct weston_output base;
|
||||
|
||||
struct weston_mode mode;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
|
||||
/* framebuffer mmap details */
|
||||
size_t buffer_length;
|
||||
void *fb;
|
||||
|
||||
/* pixman details. */
|
||||
pixman_image_t *hw_surface;
|
||||
};
|
||||
|
||||
static const char default_seat[] = "seat0";
|
||||
|
||||
static inline struct fbdev_head *
|
||||
to_fbdev_head(struct weston_head *base)
|
||||
{
|
||||
return container_of(base, struct fbdev_head, base);
|
||||
}
|
||||
|
||||
static inline struct fbdev_output *
|
||||
to_fbdev_output(struct weston_output *base)
|
||||
{
|
||||
return container_of(base, struct fbdev_output, base);
|
||||
}
|
||||
|
||||
static inline struct fbdev_backend *
|
||||
to_fbdev_backend(struct weston_compositor *base)
|
||||
{
|
||||
return container_of(base->backend, struct fbdev_backend, base);
|
||||
}
|
||||
|
||||
static struct fbdev_head *
|
||||
fbdev_output_get_head(struct fbdev_output *output)
|
||||
{
|
||||
if (wl_list_length(&output->base.head_list) != 1)
|
||||
return NULL;
|
||||
|
||||
return container_of(output->base.head_list.next,
|
||||
struct fbdev_head, base.output_link);
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_start_repaint_loop(struct weston_output *output)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
weston_compositor_read_presentation_clock(output->compositor, &ts);
|
||||
weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
|
||||
/* Repaint the damaged region onto the back buffer. */
|
||||
pixman_renderer_output_set_buffer(base, output->hw_surface);
|
||||
ec->renderer->repaint_output(base, damage);
|
||||
|
||||
/* Update the damage region. */
|
||||
pixman_region32_subtract(&ec->primary_plane.damage,
|
||||
&ec->primary_plane.damage, damage);
|
||||
|
||||
/* Schedule the end of the frame. We do not sync this to the frame
|
||||
* buffer clock because users who want that should be using the DRM
|
||||
* compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
|
||||
* panning, which is broken in most kernel drivers.
|
||||
*
|
||||
* Finish the frame synchronised to the specified refresh rate. The
|
||||
* refresh rate is given in mHz and the interval in ms. */
|
||||
wl_event_source_timer_update(output->finish_frame_timer,
|
||||
1000000 / output->mode.refresh);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
finish_frame_handler(void *data)
|
||||
{
|
||||
struct fbdev_output *output = data;
|
||||
struct timespec ts;
|
||||
|
||||
weston_compositor_read_presentation_clock(output->base.compositor, &ts);
|
||||
weston_output_finish_frame(&output->base, &ts, 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static pixman_format_code_t
|
||||
calculate_pixman_format(struct fb_var_screeninfo *vinfo,
|
||||
struct fb_fix_screeninfo *finfo)
|
||||
{
|
||||
/* Calculate the pixman format supported by the frame buffer from the
|
||||
* buffer's metadata. Return 0 if no known pixman format is supported
|
||||
* (since this has depth 0 it's guaranteed to not conflict with any
|
||||
* actual pixman format).
|
||||
*
|
||||
* Documentation on the vinfo and finfo structures:
|
||||
* http://www.mjmwired.net/kernel/Documentation/fb/api.txt
|
||||
*
|
||||
* TODO: Try a bit harder to support other formats, including setting
|
||||
* the preferred format in the hardware. */
|
||||
int type;
|
||||
|
||||
weston_log("Calculating pixman format from:\n"
|
||||
STAMP_SPACE " - type: %i (aux: %i)\n"
|
||||
STAMP_SPACE " - visual: %i\n"
|
||||
STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
|
||||
STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
|
||||
STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
|
||||
STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
|
||||
STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
|
||||
finfo->type, finfo->type_aux, finfo->visual,
|
||||
vinfo->bits_per_pixel, vinfo->grayscale,
|
||||
vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
|
||||
vinfo->green.offset, vinfo->green.length,
|
||||
vinfo->green.msb_right,
|
||||
vinfo->blue.offset, vinfo->blue.length,
|
||||
vinfo->blue.msb_right,
|
||||
vinfo->transp.offset, vinfo->transp.length,
|
||||
vinfo->transp.msb_right);
|
||||
|
||||
/* We only handle packed formats at the moment. */
|
||||
if (finfo->type != FB_TYPE_PACKED_PIXELS)
|
||||
return 0;
|
||||
|
||||
/* We only handle true-colour frame buffers at the moment. */
|
||||
switch(finfo->visual) {
|
||||
case FB_VISUAL_TRUECOLOR:
|
||||
case FB_VISUAL_DIRECTCOLOR:
|
||||
if (vinfo->grayscale != 0)
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We only support formats with MSBs on the left. */
|
||||
if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
|
||||
vinfo->blue.msb_right != 0)
|
||||
return 0;
|
||||
|
||||
/* Work out the format type from the offsets. We only support RGBA, ARGB
|
||||
* and ABGR at the moment. */
|
||||
type = PIXMAN_TYPE_OTHER;
|
||||
|
||||
if ((vinfo->transp.offset >= vinfo->red.offset ||
|
||||
vinfo->transp.length == 0) &&
|
||||
vinfo->red.offset >= vinfo->green.offset &&
|
||||
vinfo->green.offset >= vinfo->blue.offset)
|
||||
type = PIXMAN_TYPE_ARGB;
|
||||
else if (vinfo->red.offset >= vinfo->green.offset &&
|
||||
vinfo->green.offset >= vinfo->blue.offset &&
|
||||
vinfo->blue.offset >= vinfo->transp.offset)
|
||||
type = PIXMAN_TYPE_RGBA;
|
||||
else if (vinfo->transp.offset >= vinfo->blue.offset &&
|
||||
vinfo->blue.offset >= vinfo->green.offset &&
|
||||
vinfo->green.offset >= vinfo->red.offset)
|
||||
type = PIXMAN_TYPE_ABGR;
|
||||
|
||||
if (type == PIXMAN_TYPE_OTHER)
|
||||
return 0;
|
||||
|
||||
/* Build the format. */
|
||||
return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
|
||||
vinfo->transp.length,
|
||||
vinfo->red.length,
|
||||
vinfo->green.length,
|
||||
vinfo->blue.length);
|
||||
}
|
||||
|
||||
static int
|
||||
calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
|
||||
{
|
||||
uint64_t quot;
|
||||
|
||||
/* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
|
||||
quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
|
||||
quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
|
||||
quot *= vinfo->pixclock;
|
||||
|
||||
if (quot > 0) {
|
||||
uint64_t refresh_rate;
|
||||
|
||||
refresh_rate = 1000000000000000LLU / quot;
|
||||
if (refresh_rate > 200000)
|
||||
refresh_rate = 200000; /* cap at 200 Hz */
|
||||
|
||||
if (refresh_rate >= 1000) /* at least 1 Hz */
|
||||
return refresh_rate;
|
||||
}
|
||||
|
||||
return 60 * 1000; /* default to 60 Hz */
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info)
|
||||
{
|
||||
struct fb_var_screeninfo varinfo;
|
||||
struct fb_fix_screeninfo fixinfo;
|
||||
|
||||
/* Probe the device for screen information. */
|
||||
if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
|
||||
ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Store the pertinent data. */
|
||||
info->x_resolution = varinfo.xres;
|
||||
info->y_resolution = varinfo.yres;
|
||||
info->width_mm = varinfo.width;
|
||||
info->height_mm = varinfo.height;
|
||||
info->bits_per_pixel = varinfo.bits_per_pixel;
|
||||
|
||||
info->buffer_length = fixinfo.smem_len;
|
||||
info->line_length = fixinfo.line_length;
|
||||
strncpy(info->id, fixinfo.id, sizeof(info->id));
|
||||
info->id[sizeof(info->id)-1] = '\0';
|
||||
|
||||
info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
|
||||
info->refresh_rate = calculate_refresh_rate(&varinfo);
|
||||
|
||||
if (info->pixel_format == 0) {
|
||||
weston_log("Frame buffer uses an unsupported format.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info)
|
||||
{
|
||||
struct fb_var_screeninfo varinfo;
|
||||
|
||||
/* Grab the current screen information. */
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Update the information. */
|
||||
varinfo.xres = info->x_resolution;
|
||||
varinfo.yres = info->y_resolution;
|
||||
varinfo.width = info->width_mm;
|
||||
varinfo.height = info->height_mm;
|
||||
varinfo.bits_per_pixel = info->bits_per_pixel;
|
||||
|
||||
/* Try to set up an ARGB (x8r8g8b8) pixel format. */
|
||||
varinfo.grayscale = 0;
|
||||
varinfo.transp.offset = 24;
|
||||
varinfo.transp.length = 0;
|
||||
varinfo.transp.msb_right = 0;
|
||||
varinfo.red.offset = 16;
|
||||
varinfo.red.length = 8;
|
||||
varinfo.red.msb_right = 0;
|
||||
varinfo.green.offset = 8;
|
||||
varinfo.green.length = 8;
|
||||
varinfo.green.msb_right = 0;
|
||||
varinfo.blue.offset = 0;
|
||||
varinfo.blue.length = 8;
|
||||
varinfo.blue.msb_right = 0;
|
||||
|
||||
/* Set the device's screen information. */
|
||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_wakeup_screen(int fd, struct fbdev_screeninfo *info)
|
||||
{
|
||||
struct fb_var_screeninfo varinfo;
|
||||
|
||||
/* Grab the current screen information. */
|
||||
if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* force the framebuffer to wake up */
|
||||
varinfo.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE;
|
||||
|
||||
/* Set the device's screen information. */
|
||||
if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Returns an FD for the frame buffer device. */
|
||||
static int
|
||||
fbdev_frame_buffer_open(const char *fb_dev,
|
||||
struct fbdev_screeninfo *screen_info)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
weston_log("Opening fbdev frame buffer.\n");
|
||||
|
||||
/* Open the frame buffer device. */
|
||||
fd = open(fb_dev, O_RDWR | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
weston_log("Failed to open frame buffer device ‘%s’: %s\n",
|
||||
fb_dev, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Grab the screen info. */
|
||||
if (fbdev_query_screen_info(fd, screen_info) < 0) {
|
||||
weston_log("Failed to get frame buffer info: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Attempt to wake up the framebuffer device, needed for secondary
|
||||
* framebuffer devices */
|
||||
if (fbdev_wakeup_screen(fd, screen_info) < 0) {
|
||||
weston_log("Failed to activate framebuffer display. "
|
||||
"Attempting to open output anyway.\n");
|
||||
}
|
||||
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Closes the FD on success or failure. */
|
||||
static int
|
||||
fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
|
||||
{
|
||||
struct fbdev_head *head;
|
||||
int retval = -1;
|
||||
|
||||
head = fbdev_output_get_head(output);
|
||||
|
||||
weston_log("Mapping fbdev frame buffer.\n");
|
||||
|
||||
/* Map the frame buffer. Write-only mode, since we don't want to read
|
||||
* anything back (because it's slow). */
|
||||
output->buffer_length = head->fb_info.buffer_length;
|
||||
output->fb = mmap(NULL, output->buffer_length,
|
||||
PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (output->fb == MAP_FAILED) {
|
||||
weston_log("Failed to mmap frame buffer: %s\n",
|
||||
strerror(errno));
|
||||
output->fb = NULL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/* Create a pixman image to wrap the memory mapped frame buffer. */
|
||||
output->hw_surface =
|
||||
pixman_image_create_bits(head->fb_info.pixel_format,
|
||||
head->fb_info.x_resolution,
|
||||
head->fb_info.y_resolution,
|
||||
output->fb,
|
||||
head->fb_info.line_length);
|
||||
if (output->hw_surface == NULL) {
|
||||
weston_log("Failed to create surface for frame buffer.\n");
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
/* Success! */
|
||||
retval = 0;
|
||||
|
||||
out_unmap:
|
||||
if (retval != 0 && output->fb != NULL) {
|
||||
munmap(output->fb, output->buffer_length);
|
||||
output->fb = NULL;
|
||||
}
|
||||
|
||||
out_close:
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_frame_buffer_unmap(struct fbdev_output *output)
|
||||
{
|
||||
if (!output->fb) {
|
||||
assert(!output->hw_surface);
|
||||
return;
|
||||
}
|
||||
|
||||
weston_log("Unmapping fbdev frame buffer.\n");
|
||||
|
||||
if (output->hw_surface)
|
||||
pixman_image_unref(output->hw_surface);
|
||||
output->hw_surface = NULL;
|
||||
|
||||
if (munmap(output->fb, output->buffer_length) < 0)
|
||||
weston_log("Failed to munmap frame buffer: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
output->fb = NULL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
fbdev_output_attach_head(struct weston_output *output_base,
|
||||
struct weston_head *head_base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(output_base);
|
||||
struct fbdev_head *head = to_fbdev_head(head_base);
|
||||
|
||||
/* Clones not supported. */
|
||||
if (!wl_list_empty(&output->base.head_list))
|
||||
return -1;
|
||||
|
||||
/* only one static mode in list */
|
||||
output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||
output->mode.width = head->fb_info.x_resolution;
|
||||
output->mode.height = head->fb_info.y_resolution;
|
||||
output->mode.refresh = head->fb_info.refresh_rate;
|
||||
wl_list_init(&output->base.mode_list);
|
||||
wl_list_insert(&output->base.mode_list, &output->mode.link);
|
||||
output->base.current_mode = &output->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fbdev_output_destroy(struct weston_output *base);
|
||||
|
||||
static int
|
||||
fbdev_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
|
||||
struct fbdev_head *head;
|
||||
int fb_fd;
|
||||
struct wl_event_loop *loop;
|
||||
const struct pixman_renderer_output_options options = {
|
||||
.use_shadow = true,
|
||||
};
|
||||
|
||||
head = fbdev_output_get_head(output);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
|
||||
weston_log("Mapping frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
|
||||
output->base.repaint = fbdev_output_repaint;
|
||||
|
||||
if (pixman_renderer_output_create(&output->base, &options) < 0)
|
||||
goto out_hw_surface;
|
||||
|
||||
loop = wl_display_get_event_loop(backend->compositor->wl_display);
|
||||
output->finish_frame_timer =
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
|
||||
weston_log("fbdev output %d×%d px\n",
|
||||
output->mode.width, output->mode.height);
|
||||
weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
|
||||
output->mode.refresh / 1000);
|
||||
|
||||
return 0;
|
||||
|
||||
out_hw_surface:
|
||||
fbdev_frame_buffer_unmap(output);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
|
||||
if (!base->enabled)
|
||||
return 0;
|
||||
|
||||
wl_event_source_remove(output->finish_frame_timer);
|
||||
output->finish_frame_timer = NULL;
|
||||
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
fbdev_frame_buffer_unmap(output);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fbdev_head *
|
||||
fbdev_head_create(struct fbdev_backend *backend, const char *device)
|
||||
{
|
||||
struct fbdev_head *head;
|
||||
int fb_fd;
|
||||
|
||||
head = zalloc(sizeof *head);
|
||||
if (!head)
|
||||
return NULL;
|
||||
|
||||
head->device = strdup(device);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer head failed.\n");
|
||||
goto out_free;
|
||||
}
|
||||
close(fb_fd);
|
||||
|
||||
weston_head_init(&head->base, "fbdev");
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_head_set_monitor_strings(&head->base, "unknown",
|
||||
head->fb_info.id, NULL);
|
||||
weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN);
|
||||
weston_head_set_physical_size(&head->base, head->fb_info.width_mm,
|
||||
head->fb_info.height_mm);
|
||||
|
||||
weston_compositor_add_head(backend->compositor, &head->base);
|
||||
|
||||
weston_log("Created head '%s' for device %s (%s)\n",
|
||||
head->base.name, head->device, head->base.model);
|
||||
|
||||
return head;
|
||||
|
||||
out_free:
|
||||
free(head->device);
|
||||
free(head);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_head_destroy(struct fbdev_head *head)
|
||||
{
|
||||
weston_head_release(&head->base);
|
||||
free(head->device);
|
||||
free(head);
|
||||
}
|
||||
|
||||
static struct weston_output *
|
||||
fbdev_output_create(struct weston_compositor *compositor,
|
||||
const char *name)
|
||||
{
|
||||
struct fbdev_output *output;
|
||||
|
||||
weston_log("Creating fbdev output.\n");
|
||||
|
||||
output = zalloc(sizeof *output);
|
||||
if (output == NULL)
|
||||
return NULL;
|
||||
|
||||
output->backend = to_fbdev_backend(compositor);
|
||||
|
||||
weston_output_init(&output->base, compositor, name);
|
||||
|
||||
output->base.destroy = fbdev_output_destroy;
|
||||
output->base.disable = fbdev_output_disable;
|
||||
output->base.enable = fbdev_output_enable;
|
||||
output->base.attach_head = fbdev_output_attach_head;
|
||||
|
||||
weston_compositor_add_pending_output(&output->base, compositor);
|
||||
|
||||
return &output->base;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
|
||||
weston_log("Destroying fbdev output.\n");
|
||||
|
||||
fbdev_output_disable(base);
|
||||
|
||||
/* Remove the output. */
|
||||
weston_output_release(&output->base);
|
||||
|
||||
free(output);
|
||||
}
|
||||
|
||||
/* strcmp()-style return values. */
|
||||
static int
|
||||
compare_screen_info (const struct fbdev_screeninfo *a,
|
||||
const struct fbdev_screeninfo *b)
|
||||
{
|
||||
if (a->x_resolution == b->x_resolution &&
|
||||
a->y_resolution == b->y_resolution &&
|
||||
a->width_mm == b->width_mm &&
|
||||
a->height_mm == b->height_mm &&
|
||||
a->bits_per_pixel == b->bits_per_pixel &&
|
||||
a->pixel_format == b->pixel_format &&
|
||||
a->refresh_rate == b->refresh_rate)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
fbdev_output_reenable(struct fbdev_backend *backend,
|
||||
struct weston_output *base)
|
||||
{
|
||||
struct fbdev_output *output = to_fbdev_output(base);
|
||||
struct fbdev_head *head;
|
||||
struct fbdev_screeninfo new_screen_info;
|
||||
int fb_fd;
|
||||
|
||||
head = fbdev_output_get_head(output);
|
||||
|
||||
weston_log("Re-enabling fbdev output.\n");
|
||||
assert(output->base.enabled);
|
||||
|
||||
/* Create the frame buffer. */
|
||||
fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info);
|
||||
if (fb_fd < 0) {
|
||||
weston_log("Creating frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Check whether the frame buffer details have changed since we were
|
||||
* disabled. */
|
||||
if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) {
|
||||
/* Perform a mode-set to restore the old mode. */
|
||||
if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) {
|
||||
weston_log("Failed to restore mode settings. "
|
||||
"Attempting to re-open output anyway.\n");
|
||||
}
|
||||
|
||||
close(fb_fd);
|
||||
|
||||
/* Disable and enable the output so that resources depending on
|
||||
* the frame buffer X/Y resolution (such as the shadow buffer)
|
||||
* are re-initialised. */
|
||||
fbdev_output_disable(&output->base);
|
||||
return fbdev_output_enable(&output->base);
|
||||
}
|
||||
|
||||
/* Map the device if it has the same details as before. */
|
||||
if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
|
||||
weston_log("Mapping frame buffer failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fbdev_backend_destroy(struct weston_compositor *base)
|
||||
{
|
||||
struct fbdev_backend *backend = to_fbdev_backend(base);
|
||||
struct weston_head *head, *next;
|
||||
|
||||
udev_input_destroy(&backend->input);
|
||||
|
||||
/* Destroy the output. */
|
||||
weston_compositor_shutdown(base);
|
||||
|
||||
wl_list_for_each_safe(head, next, &base->head_list, compositor_link)
|
||||
fbdev_head_destroy(to_fbdev_head(head));
|
||||
|
||||
/* Chain up. */
|
||||
weston_launcher_destroy(base->launcher);
|
||||
|
||||
udev_unref(backend->udev);
|
||||
|
||||
free(backend);
|
||||
}
|
||||
|
||||
static void
|
||||
session_notify(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct weston_compositor *compositor = data;
|
||||
struct fbdev_backend *backend = to_fbdev_backend(compositor);
|
||||
struct weston_output *output;
|
||||
|
||||
if (compositor->session_active) {
|
||||
weston_log("entering VT\n");
|
||||
compositor->state = backend->prev_state;
|
||||
|
||||
wl_list_for_each(output, &compositor->output_list, link) {
|
||||
fbdev_output_reenable(backend, output);
|
||||
}
|
||||
|
||||
weston_compositor_damage_all(compositor);
|
||||
|
||||
udev_input_enable(&backend->input);
|
||||
} else {
|
||||
weston_log("leaving VT\n");
|
||||
udev_input_disable(&backend->input);
|
||||
|
||||
wl_list_for_each(output, &compositor->output_list, link) {
|
||||
fbdev_frame_buffer_unmap(to_fbdev_output(output));
|
||||
}
|
||||
|
||||
backend->prev_state = compositor->state;
|
||||
weston_compositor_offscreen(compositor);
|
||||
|
||||
/* If we have a repaint scheduled (from the idle handler), make
|
||||
* sure we cancel that so we don't try to pageflip when we're
|
||||
* vt switched away. The OFFSCREEN state will prevent
|
||||
* further attempts at repainting. When we switch
|
||||
* back, we schedule a repaint, which will process
|
||||
* pending frame callbacks. */
|
||||
|
||||
wl_list_for_each(output,
|
||||
&compositor->output_list, link) {
|
||||
output->repaint_needed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
find_framebuffer_device(struct fbdev_backend *b, const char *seat)
|
||||
{
|
||||
struct udev_enumerate *e;
|
||||
struct udev_list_entry *entry;
|
||||
const char *path, *device_seat, *id;
|
||||
char *fb_device_path = NULL;
|
||||
struct udev_device *device, *fb_device, *pci;
|
||||
|
||||
e = udev_enumerate_new(b->udev);
|
||||
udev_enumerate_add_match_subsystem(e, "graphics");
|
||||
udev_enumerate_add_match_sysname(e, "fb[0-9]*");
|
||||
|
||||
udev_enumerate_scan_devices(e);
|
||||
fb_device = NULL;
|
||||
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
|
||||
bool is_boot_vga = false;
|
||||
|
||||
path = udev_list_entry_get_name(entry);
|
||||
device = udev_device_new_from_syspath(b->udev, path);
|
||||
if (!device)
|
||||
continue;
|
||||
device_seat = udev_device_get_property_value(device, "ID_SEAT");
|
||||
if (!device_seat)
|
||||
device_seat = default_seat;
|
||||
if (strcmp(device_seat, seat)) {
|
||||
udev_device_unref(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
pci = udev_device_get_parent_with_subsystem_devtype(device,
|
||||
"pci", NULL);
|
||||
if (pci) {
|
||||
id = udev_device_get_sysattr_value(pci, "boot_vga");
|
||||
if (id && !strcmp(id, "1"))
|
||||
is_boot_vga = true;
|
||||
}
|
||||
|
||||
/* If a framebuffer device was found, and this device isn't
|
||||
* the boot-VGA device, don't use it. */
|
||||
if (!is_boot_vga && fb_device) {
|
||||
udev_device_unref(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* There can only be one boot_vga device. Try to use it
|
||||
* at all costs. */
|
||||
if (is_boot_vga) {
|
||||
if (fb_device)
|
||||
udev_device_unref(fb_device);
|
||||
fb_device = device;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Per the (!is_boot_vga && fb_device) test above, only
|
||||
* trump existing saved devices with boot-VGA devices, so if
|
||||
* the test ends up here, this must be the first device seen. */
|
||||
assert(!fb_device);
|
||||
fb_device = device;
|
||||
}
|
||||
|
||||
udev_enumerate_unref(e);
|
||||
|
||||
if (fb_device) {
|
||||
fb_device_path = strdup(udev_device_get_devnode(fb_device));
|
||||
udev_device_unref(fb_device);
|
||||
}
|
||||
|
||||
return fb_device_path;
|
||||
}
|
||||
|
||||
static struct fbdev_backend *
|
||||
fbdev_backend_create(struct weston_compositor *compositor,
|
||||
struct weston_fbdev_backend_config *param)
|
||||
{
|
||||
struct fbdev_backend *backend;
|
||||
const char *seat_id = default_seat;
|
||||
const char *session_seat;
|
||||
|
||||
session_seat = getenv("XDG_SEAT");
|
||||
if (session_seat)
|
||||
seat_id = session_seat;
|
||||
if (param->seat_id)
|
||||
seat_id = param->seat_id;
|
||||
|
||||
weston_log("initializing fbdev backend\n");
|
||||
weston_log("warning: the fbdev backend is deprecated, please migrate "
|
||||
"to the DRM backend\n");
|
||||
|
||||
backend = zalloc(sizeof *backend);
|
||||
if (backend == NULL)
|
||||
return NULL;
|
||||
|
||||
backend->compositor = compositor;
|
||||
compositor->backend = &backend->base;
|
||||
if (weston_compositor_set_presentation_clock_software(
|
||||
compositor) < 0)
|
||||
goto out_compositor;
|
||||
|
||||
backend->udev = udev_new();
|
||||
if (backend->udev == NULL) {
|
||||
weston_log("Failed to initialize udev context.\n");
|
||||
goto out_compositor;
|
||||
}
|
||||
|
||||
if (!param->device)
|
||||
param->device = find_framebuffer_device(backend, seat_id);
|
||||
if (!param->device) {
|
||||
weston_log("fatal: no framebuffer devices detected.\n");
|
||||
goto out_udev;
|
||||
}
|
||||
|
||||
/* Set up the TTY. */
|
||||
backend->session_listener.notify = session_notify;
|
||||
wl_signal_add(&compositor->session_signal,
|
||||
&backend->session_listener);
|
||||
compositor->launcher =
|
||||
weston_launcher_connect(compositor, param->tty, seat_id, false);
|
||||
if (!compositor->launcher) {
|
||||
weston_log("fatal: fbdev backend should be run using "
|
||||
"weston-launch binary, or your system should "
|
||||
"provide the logind D-Bus API.\n");
|
||||
goto out_udev;
|
||||
}
|
||||
|
||||
backend->base.destroy = fbdev_backend_destroy;
|
||||
backend->base.create_output = fbdev_output_create;
|
||||
|
||||
backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
|
||||
|
||||
weston_setup_vt_switch_bindings(compositor);
|
||||
|
||||
if (pixman_renderer_init(compositor) < 0)
|
||||
goto out_launcher;
|
||||
|
||||
if (!fbdev_head_create(backend, param->device))
|
||||
goto out_launcher;
|
||||
|
||||
free(param->device);
|
||||
|
||||
udev_input_init(&backend->input, compositor, backend->udev,
|
||||
seat_id, param->configure_device);
|
||||
|
||||
return backend;
|
||||
|
||||
out_launcher:
|
||||
free(param->device);
|
||||
weston_launcher_destroy(compositor->launcher);
|
||||
|
||||
out_udev:
|
||||
udev_unref(backend->udev);
|
||||
|
||||
out_compositor:
|
||||
weston_compositor_shutdown(compositor);
|
||||
free(backend);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
config_init_to_defaults(struct weston_fbdev_backend_config *config)
|
||||
{
|
||||
config->tty = 0; /* default to current tty */
|
||||
config->device = NULL;
|
||||
config->seat_id = NULL;
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
weston_backend_init(struct weston_compositor *compositor,
|
||||
struct weston_backend_config *config_base)
|
||||
{
|
||||
struct fbdev_backend *b;
|
||||
struct weston_fbdev_backend_config config = {{ 0, }};
|
||||
|
||||
if (config_base == NULL ||
|
||||
config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
|
||||
config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
|
||||
weston_log("fbdev backend config structure is invalid\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
config_init_to_defaults(&config);
|
||||
memcpy(&config, config_base, config_base->struct_size);
|
||||
|
||||
b = fbdev_backend_create(compositor, &config);
|
||||
if (b == NULL)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
if not get_option('deprecated-backend-fbdev')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
warning('Support for the deprecated fbdev backend is enabled.')
|
||||
warning('This feature will be removed in a future version.')
|
||||
|
||||
config_h.set('BUILD_FBDEV_COMPOSITOR', '1')
|
||||
|
||||
srcs_fbdev = [
|
||||
'fbdev.c',
|
||||
presentation_time_server_protocol_h,
|
||||
]
|
||||
|
||||
deps_fbdev = [
|
||||
dep_libweston_private,
|
||||
dep_session_helper,
|
||||
dep_libinput_backend,
|
||||
dependency('libudev', version: '>= 136'),
|
||||
]
|
||||
|
||||
plugin_fbdev = shared_library(
|
||||
'fbdev-backend',
|
||||
srcs_fbdev,
|
||||
include_directories: common_inc,
|
||||
dependencies: deps_fbdev,
|
||||
name_prefix: '',
|
||||
install: true,
|
||||
install_dir: dir_module_libweston
|
||||
)
|
||||
env_modmap += 'fbdev-backend.so=@0@;'.format(plugin_fbdev.full_path())
|
||||
|
||||
install_headers(backend_fbdev_h, subdir: dir_include_libweston_install)
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <libweston/backend-headless.h>
|
||||
#include "shared/helpers.h"
|
||||
#include "linux-explicit-synchronization.h"
|
||||
#include "pixel-formats.h"
|
||||
#include "pixman-renderer.h"
|
||||
#include "renderer-gl/gl-renderer.h"
|
||||
#include "shared/weston-drm-fourcc.h"
|
||||
@@ -70,24 +71,33 @@ struct headless_output {
|
||||
|
||||
struct weston_mode mode;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
uint32_t *image_buf;
|
||||
pixman_image_t *image;
|
||||
};
|
||||
|
||||
static const uint32_t headless_formats[] = {
|
||||
DRM_FORMAT_XRGB8888,
|
||||
DRM_FORMAT_XRGB8888, /* default for pixman-renderer */
|
||||
DRM_FORMAT_ARGB8888,
|
||||
};
|
||||
|
||||
static void
|
||||
headless_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct headless_head *
|
||||
to_headless_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != headless_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct headless_head, base);
|
||||
}
|
||||
|
||||
static void
|
||||
headless_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct headless_output *
|
||||
to_headless_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != headless_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct headless_output, base);
|
||||
}
|
||||
|
||||
@@ -122,11 +132,14 @@ finish_frame_handler(void *data)
|
||||
|
||||
static int
|
||||
headless_output_repaint(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct weston_compositor *ec;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
|
||||
ec->renderer->repaint_output(&output->base, damage);
|
||||
|
||||
@@ -152,18 +165,21 @@ headless_output_disable_pixman(struct headless_output *output)
|
||||
{
|
||||
pixman_renderer_output_destroy(&output->base);
|
||||
pixman_image_unref(output->image);
|
||||
free(output->image_buf);
|
||||
}
|
||||
|
||||
static int
|
||||
headless_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(base);
|
||||
struct headless_backend *b = to_headless_backend(base->compositor);
|
||||
struct headless_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
if (!output->base.enabled)
|
||||
return 0;
|
||||
|
||||
b = to_headless_backend(base->compositor);
|
||||
|
||||
wl_event_source_remove(output->finish_frame_timer);
|
||||
|
||||
switch (b->renderer_type) {
|
||||
@@ -185,6 +201,8 @@ headless_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(base);
|
||||
|
||||
assert(output);
|
||||
|
||||
headless_output_disable(&output->base);
|
||||
weston_output_release(&output->base);
|
||||
|
||||
@@ -214,20 +232,20 @@ headless_output_enable_gl(struct headless_output *output)
|
||||
static int
|
||||
headless_output_enable_pixman(struct headless_output *output)
|
||||
{
|
||||
const struct pixel_format_info *pfmt;
|
||||
const struct pixman_renderer_output_options options = {
|
||||
.use_shadow = true,
|
||||
};
|
||||
|
||||
output->image_buf = malloc(output->base.current_mode->width *
|
||||
output->base.current_mode->height * 4);
|
||||
if (!output->image_buf)
|
||||
return -1;
|
||||
pfmt = pixel_format_get_info(headless_formats[0]);
|
||||
|
||||
output->image = pixman_image_create_bits(PIXMAN_x8r8g8b8,
|
||||
output->image =
|
||||
pixman_image_create_bits_no_clear(pfmt->pixman_format,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
output->image_buf,
|
||||
output->base.current_mode->width * 4);
|
||||
NULL, 0);
|
||||
if (!output->image)
|
||||
return -1;
|
||||
|
||||
if (pixman_renderer_output_create(&output->base, &options) < 0)
|
||||
goto err_renderer;
|
||||
@@ -238,7 +256,6 @@ headless_output_enable_pixman(struct headless_output *output)
|
||||
|
||||
err_renderer:
|
||||
pixman_image_unref(output->image);
|
||||
free(output->image_buf);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -247,10 +264,14 @@ static int
|
||||
headless_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct headless_output *output = to_headless_output(base);
|
||||
struct headless_backend *b = to_headless_backend(base->compositor);
|
||||
struct headless_backend *b;
|
||||
struct wl_event_loop *loop;
|
||||
int ret = 0;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_headless_backend(base->compositor);
|
||||
|
||||
loop = wl_display_get_event_loop(b->compositor->wl_display);
|
||||
output->finish_frame_timer =
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
@@ -287,6 +308,9 @@ headless_output_set_size(struct weston_output *base,
|
||||
struct weston_head *head;
|
||||
int output_width, output_height;
|
||||
|
||||
if (!output)
|
||||
return -1;
|
||||
|
||||
/* We can only be called once. */
|
||||
assert(!output->base.current_mode);
|
||||
|
||||
@@ -361,7 +385,12 @@ headless_head_create(struct weston_compositor *compositor,
|
||||
return -1;
|
||||
|
||||
weston_head_init(&head->base, name);
|
||||
|
||||
head->base.backend_id = headless_head_destroy;
|
||||
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_head_set_supported_eotf_mask(&head->base,
|
||||
WESTON_EOTF_MODE_ALL_MASK);
|
||||
|
||||
/* Ideally all attributes of the head would be set here, so that the
|
||||
* user has all the information when deciding to create outputs.
|
||||
@@ -374,8 +403,12 @@ headless_head_create(struct weston_compositor *compositor,
|
||||
}
|
||||
|
||||
static void
|
||||
headless_head_destroy(struct headless_head *head)
|
||||
headless_head_destroy(struct weston_head *base)
|
||||
{
|
||||
struct headless_head *head = to_headless_head(base);
|
||||
|
||||
assert(head);
|
||||
|
||||
weston_head_release(&head->base);
|
||||
free(head);
|
||||
}
|
||||
@@ -388,8 +421,10 @@ headless_destroy(struct weston_compositor *ec)
|
||||
|
||||
weston_compositor_shutdown(ec);
|
||||
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
|
||||
headless_head_destroy(to_headless_head(base));
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
|
||||
if (to_headless_head(base))
|
||||
headless_head_destroy(base);
|
||||
}
|
||||
|
||||
free(b);
|
||||
}
|
||||
|
||||
@@ -4,24 +4,36 @@ endif
|
||||
|
||||
config_h.set('BUILD_RDP_COMPOSITOR', '1')
|
||||
|
||||
dep_frdp = dependency('freerdp2', version: '>= 2.2.0', required: false)
|
||||
dep_frdp = dependency('freerdp2', version: '>= 2.3.0', required: false)
|
||||
if not dep_frdp.found()
|
||||
error('RDP-backend requires freerdp >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
error('RDP-backend requires freerdp >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
endif
|
||||
|
||||
dep_wpr = dependency('winpr2', version: '>= 2.2.0', required: false)
|
||||
dep_frdp_server = dependency('freerdp-server2', version: '>= 2.3.0', required: false)
|
||||
if not dep_frdp_server.found()
|
||||
error('RDP-backend requires freerdp-server2 >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
endif
|
||||
|
||||
dep_wpr = dependency('winpr2', version: '>= 2.3.0', required: false)
|
||||
if not dep_wpr.found()
|
||||
error('RDP-backend requires winpr >= 2.2.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
error('RDP-backend requires winpr >= 2.3.0 which was not found. Or, you can use \'-Dbackend-rdp=false\'.')
|
||||
endif
|
||||
|
||||
deps_rdp = [
|
||||
dep_libweston_private,
|
||||
dep_frdp,
|
||||
dep_frdp_server,
|
||||
dep_wpr,
|
||||
]
|
||||
srcs_rdp = [
|
||||
'rdp.c',
|
||||
'rdpclip.c',
|
||||
'rdputil.c',
|
||||
]
|
||||
|
||||
plugin_rdp = shared_library(
|
||||
'rdp-backend',
|
||||
'rdp.c',
|
||||
srcs_rdp,
|
||||
include_directories: common_inc,
|
||||
dependencies: deps_rdp,
|
||||
name_prefix: '',
|
||||
|
||||
+609
-212
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright © 2013 Hardening <rdp.effort@gmail.com>
|
||||
* Copyright © 2020 Microsoft
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef RDP_H
|
||||
#define RDP_H
|
||||
|
||||
#include <freerdp/version.h>
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/listener.h>
|
||||
#include <freerdp/update.h>
|
||||
#include <freerdp/input.h>
|
||||
#include <freerdp/codec/color.h>
|
||||
#include <freerdp/codec/rfx.h>
|
||||
#include <freerdp/codec/nsc.h>
|
||||
#include <freerdp/locale/keyboard.h>
|
||||
#include <freerdp/channels/wtsvc.h>
|
||||
#include <freerdp/server/cliprdr.h>
|
||||
|
||||
#include <libweston/libweston.h>
|
||||
#include <libweston/backend-rdp.h>
|
||||
#include <libweston/weston-log.h>
|
||||
|
||||
#include "backend.h"
|
||||
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
#define MAX_FREERDP_FDS 32
|
||||
#define DEFAULT_AXIS_STEP_DISTANCE 10
|
||||
#define DEFAULT_PIXEL_FORMAT PIXEL_FORMAT_BGRA32
|
||||
|
||||
/* https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getkeyboardtype
|
||||
* defines a keyboard type that isn't currently defined in FreeRDP, but is
|
||||
* available for RDP connections */
|
||||
#ifndef KBD_TYPE_KOREAN
|
||||
#define KBD_TYPE_KOREAN 8
|
||||
#endif
|
||||
|
||||
/* WinPR's GetVirtualKeyCodeFromVirtualScanCode() can't handle hangul/hanja keys */
|
||||
/* 0x1f1 and 0x1f2 keys are only exists on Korean 103 keyboard (Type 8:SubType 6) */
|
||||
|
||||
/* From Linux's keyboard driver at drivers/input/keyboard/atkbd.c */
|
||||
#define ATKBD_RET_HANJA 0xf1
|
||||
#define ATKBD_RET_HANGEUL 0xf2
|
||||
|
||||
struct rdp_output;
|
||||
|
||||
struct rdp_backend {
|
||||
struct weston_backend base;
|
||||
struct weston_compositor *compositor;
|
||||
|
||||
freerdp_listener *listener;
|
||||
struct wl_event_source *listener_events[MAX_FREERDP_FDS];
|
||||
struct rdp_output *output;
|
||||
struct weston_log_scope *debug;
|
||||
struct weston_log_scope *verbose;
|
||||
|
||||
struct weston_log_scope *clipboard_debug;
|
||||
struct weston_log_scope *clipboard_verbose;
|
||||
|
||||
struct wl_list peers;
|
||||
|
||||
char *server_cert;
|
||||
char *server_key;
|
||||
char *rdp_key;
|
||||
int tls_enabled;
|
||||
int no_clients_resize;
|
||||
int force_no_compression;
|
||||
bool remotefx_codec;
|
||||
int external_listener_fd;
|
||||
int rdp_monitor_refresh_rate;
|
||||
pid_t compositor_tid;
|
||||
|
||||
rdp_audio_in_setup audio_in_setup;
|
||||
rdp_audio_in_teardown audio_in_teardown;
|
||||
rdp_audio_out_setup audio_out_setup;
|
||||
rdp_audio_out_teardown audio_out_teardown;
|
||||
};
|
||||
|
||||
enum peer_item_flags {
|
||||
RDP_PEER_ACTIVATED = (1 << 0),
|
||||
RDP_PEER_OUTPUT_ENABLED = (1 << 1),
|
||||
};
|
||||
|
||||
struct rdp_peers_item {
|
||||
int flags;
|
||||
freerdp_peer *peer;
|
||||
struct weston_seat *seat;
|
||||
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
struct rdp_head {
|
||||
struct weston_head base;
|
||||
};
|
||||
|
||||
struct rdp_output {
|
||||
struct weston_output base;
|
||||
struct wl_event_source *finish_frame_timer;
|
||||
pixman_image_t *shadow_surface;
|
||||
};
|
||||
|
||||
struct rdp_peer_context {
|
||||
rdpContext _p;
|
||||
|
||||
struct rdp_backend *rdpBackend;
|
||||
struct wl_event_source *events[MAX_FREERDP_FDS + 1]; /* +1 for WTSVirtualChannelManagerGetFileDescriptor */
|
||||
RFX_CONTEXT *rfx_context;
|
||||
wStream *encode_stream;
|
||||
RFX_RECT *rfx_rects;
|
||||
NSC_CONTEXT *nsc_context;
|
||||
|
||||
struct rdp_peers_item item;
|
||||
|
||||
bool button_state[5];
|
||||
|
||||
int verticalAccumWheelRotationPrecise;
|
||||
int verticalAccumWheelRotationDiscrete;
|
||||
int horizontalAccumWheelRotationPrecise;
|
||||
int horizontalAccumWheelRotationDiscrete;
|
||||
|
||||
HANDLE vcm;
|
||||
|
||||
/* list of outstanding event_source sent from FreeRDP thread to display loop.*/
|
||||
int loop_task_event_source_fd;
|
||||
struct wl_event_source *loop_task_event_source;
|
||||
pthread_mutex_t loop_task_list_mutex;
|
||||
struct wl_list loop_task_list; /* struct rdp_loop_task::link */
|
||||
|
||||
/* Clipboard support */
|
||||
CliprdrServerContext *clipboard_server_context;
|
||||
|
||||
void *audio_in_private;
|
||||
void *audio_out_private;
|
||||
|
||||
struct rdp_clipboard_data_source *clipboard_client_data_source;
|
||||
struct rdp_clipboard_data_source *clipboard_inflight_client_data_source;
|
||||
|
||||
struct wl_listener clipboard_selection_listener;
|
||||
};
|
||||
|
||||
typedef struct rdp_peer_context RdpPeerContext;
|
||||
|
||||
typedef void (*rdp_loop_task_func_t)(bool freeOnly, void *data);
|
||||
|
||||
struct rdp_loop_task {
|
||||
struct wl_list link;
|
||||
RdpPeerContext *peerCtx;
|
||||
rdp_loop_task_func_t func;
|
||||
};
|
||||
|
||||
#define rdp_debug_verbose(b, ...) \
|
||||
rdp_debug_print(b->verbose, false, __VA_ARGS__)
|
||||
#define rdp_debug_verbose_continue(b, ...) \
|
||||
rdp_debug_print(b->verbose, true, __VA_ARGS__)
|
||||
#define rdp_debug(b, ...) \
|
||||
rdp_debug_print(b->debug, false, __VA_ARGS__)
|
||||
#define rdp_debug_continue(b, ...) \
|
||||
rdp_debug_print(b->debug, true, __VA_ARGS__)
|
||||
|
||||
#define rdp_debug_clipboard_verbose(b, ...) \
|
||||
rdp_debug_print(b->clipboard_verbose, false, __VA_ARGS__)
|
||||
#define rdp_debug_clipboard_verbose_continue(b, ...) \
|
||||
rdp_debug_print(b->clipboard_verbose, true, __VA_ARGS__)
|
||||
#define rdp_debug_clipboard(b, ...) \
|
||||
rdp_debug_print(b->clipboard_debug, false, __VA_ARGS__)
|
||||
#define rdp_debug_clipboard_continue(b, ...) \
|
||||
rdp_debug_print(b->clipboard_debug, true, __VA_ARGS__)
|
||||
|
||||
/* rdputil.c */
|
||||
void
|
||||
rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...);
|
||||
|
||||
int
|
||||
rdp_wl_array_read_fd(struct wl_array *array, int fd);
|
||||
|
||||
void
|
||||
convert_rdp_keyboard_to_xkb_rule_names(UINT32 KeyboardType, UINT32 KeyboardSubType, UINT32 KeyboardLayout, struct xkb_rule_names *xkbRuleNames);
|
||||
|
||||
void
|
||||
assert_compositor_thread(struct rdp_backend *b);
|
||||
|
||||
void
|
||||
assert_not_compositor_thread(struct rdp_backend *b);
|
||||
|
||||
bool
|
||||
rdp_event_loop_add_fd(struct wl_event_loop *loop,
|
||||
int fd, uint32_t mask,
|
||||
wl_event_loop_fd_func_t func,
|
||||
void *data,
|
||||
struct wl_event_source **event_source);
|
||||
|
||||
void
|
||||
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx,
|
||||
rdp_loop_task_func_t func,
|
||||
struct rdp_loop_task *task);
|
||||
|
||||
bool
|
||||
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx);
|
||||
|
||||
void
|
||||
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx);
|
||||
|
||||
/* rdpclip.c */
|
||||
int
|
||||
rdp_clipboard_init(freerdp_peer *client);
|
||||
|
||||
void
|
||||
rdp_clipboard_destroy(RdpPeerContext *peerCtx);
|
||||
|
||||
void
|
||||
rdp_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct rdp_head *
|
||||
to_rdp_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != rdp_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct rdp_head, base);
|
||||
}
|
||||
|
||||
void
|
||||
rdp_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct rdp_output *
|
||||
to_rdp_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != rdp_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct rdp_output, base);
|
||||
}
|
||||
|
||||
static inline struct rdp_backend *
|
||||
to_rdp_backend(struct weston_compositor *base)
|
||||
{
|
||||
return container_of(base->backend, struct rdp_backend, base);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright © 2020 Microsoft
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "rdp.h"
|
||||
|
||||
static int cached_tm_mday = -1;
|
||||
|
||||
void rdp_debug_print(struct weston_log_scope *log_scope, bool cont, char *fmt, ...)
|
||||
{
|
||||
char timestr[128];
|
||||
int len_va;
|
||||
char *str;
|
||||
|
||||
if (!log_scope || !weston_log_scope_is_enabled(log_scope))
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
|
||||
if (cont) {
|
||||
weston_log_scope_vprintf(log_scope, fmt, ap);
|
||||
goto end;
|
||||
}
|
||||
|
||||
weston_log_timestamp(timestr, sizeof(timestr), &cached_tm_mday);
|
||||
len_va = vasprintf(&str, fmt, ap);
|
||||
if (len_va >= 0) {
|
||||
weston_log_scope_printf(log_scope, "%s %s",
|
||||
timestr, str);
|
||||
free(str);
|
||||
} else {
|
||||
const char *oom = "Out of memory";
|
||||
|
||||
weston_log_scope_printf(log_scope, "%s %s",
|
||||
timestr, oom);
|
||||
}
|
||||
end:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
assert_compositor_thread(struct rdp_backend *b)
|
||||
{
|
||||
assert(b->compositor_tid == gettid());
|
||||
}
|
||||
|
||||
void
|
||||
assert_not_compositor_thread(struct rdp_backend *b)
|
||||
{
|
||||
assert(b->compositor_tid != gettid());
|
||||
}
|
||||
|
||||
bool
|
||||
rdp_event_loop_add_fd(struct wl_event_loop *loop,
|
||||
int fd, uint32_t mask,
|
||||
wl_event_loop_fd_func_t func,
|
||||
void *data, struct wl_event_source **event_source)
|
||||
{
|
||||
*event_source = wl_event_loop_add_fd(loop, fd, 0, func, data);
|
||||
if (!*event_source) {
|
||||
weston_log("%s: wl_event_loop_add_fd failed.\n", __func__);
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_event_source_fd_update(*event_source, mask);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
rdp_dispatch_task_to_display_loop(RdpPeerContext *peerCtx,
|
||||
rdp_loop_task_func_t func,
|
||||
struct rdp_loop_task *task)
|
||||
{
|
||||
/* this function is ONLY used to queue the task from FreeRDP thread,
|
||||
* and the task to be processed at wayland display loop thread. */
|
||||
assert_not_compositor_thread(peerCtx->rdpBackend);
|
||||
|
||||
task->peerCtx = peerCtx;
|
||||
task->func = func;
|
||||
|
||||
pthread_mutex_lock(&peerCtx->loop_task_list_mutex);
|
||||
/* this inserts at head */
|
||||
wl_list_insert(&peerCtx->loop_task_list, &task->link);
|
||||
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex);
|
||||
|
||||
eventfd_write(peerCtx->loop_task_event_source_fd, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
rdp_dispatch_task(int fd, uint32_t mask, void *arg)
|
||||
{
|
||||
RdpPeerContext *peerCtx = (RdpPeerContext *)arg;
|
||||
struct rdp_loop_task *task, *tmp;
|
||||
eventfd_t dummy;
|
||||
|
||||
/* this must be called back at wayland display loop thread */
|
||||
assert_compositor_thread(peerCtx->rdpBackend);
|
||||
|
||||
eventfd_read(peerCtx->loop_task_event_source_fd, &dummy);
|
||||
|
||||
pthread_mutex_lock(&peerCtx->loop_task_list_mutex);
|
||||
/* dequeue the first task which is at last, so use reverse. */
|
||||
assert(!wl_list_empty(&peerCtx->loop_task_list));
|
||||
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) {
|
||||
wl_list_remove(&task->link);
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&peerCtx->loop_task_list_mutex);
|
||||
|
||||
/* Dispatch and task will be freed by caller. */
|
||||
task->func(false, task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
rdp_initialize_dispatch_task_event_source(RdpPeerContext *peerCtx)
|
||||
{
|
||||
struct rdp_backend *b = peerCtx->rdpBackend;
|
||||
struct wl_event_loop *loop;
|
||||
bool ret;
|
||||
|
||||
if (pthread_mutex_init(&peerCtx->loop_task_list_mutex, NULL) == -1) {
|
||||
weston_log("%s: pthread_mutex_init failed. %s\n", __func__, strerror(errno));
|
||||
goto error_mutex;
|
||||
}
|
||||
|
||||
assert(peerCtx->loop_task_event_source_fd == -1);
|
||||
peerCtx->loop_task_event_source_fd = eventfd(0, EFD_SEMAPHORE | EFD_CLOEXEC);
|
||||
if (peerCtx->loop_task_event_source_fd == -1) {
|
||||
weston_log("%s: eventfd(EFD_SEMAPHORE) failed. %s\n", __func__, strerror(errno));
|
||||
goto error_event_source_fd;
|
||||
}
|
||||
|
||||
assert(wl_list_empty(&peerCtx->loop_task_list));
|
||||
|
||||
loop = wl_display_get_event_loop(b->compositor->wl_display);
|
||||
assert(peerCtx->loop_task_event_source == NULL);
|
||||
|
||||
ret = rdp_event_loop_add_fd(loop,
|
||||
peerCtx->loop_task_event_source_fd,
|
||||
WL_EVENT_READABLE, rdp_dispatch_task,
|
||||
peerCtx,
|
||||
&peerCtx->loop_task_event_source);
|
||||
if (!ret)
|
||||
goto error_event_loop_add_fd;
|
||||
|
||||
return true;
|
||||
|
||||
error_event_loop_add_fd:
|
||||
close(peerCtx->loop_task_event_source_fd);
|
||||
peerCtx->loop_task_event_source_fd = -1;
|
||||
|
||||
error_event_source_fd:
|
||||
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex);
|
||||
|
||||
error_mutex:
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
rdp_destroy_dispatch_task_event_source(RdpPeerContext *peerCtx)
|
||||
{
|
||||
struct rdp_loop_task *task, *tmp;
|
||||
|
||||
/* This function must be called all virtual channel thread at FreeRDP is terminated,
|
||||
* that ensures no more incoming tasks. */
|
||||
|
||||
if (peerCtx->loop_task_event_source) {
|
||||
wl_event_source_remove(peerCtx->loop_task_event_source);
|
||||
peerCtx->loop_task_event_source = NULL;
|
||||
}
|
||||
|
||||
wl_list_for_each_reverse_safe(task, tmp, &peerCtx->loop_task_list, link) {
|
||||
wl_list_remove(&task->link);
|
||||
/* inform caller task is not really scheduled prior to context destruction,
|
||||
* inform them to clean them up. */
|
||||
task->func(true /* freeOnly */, task);
|
||||
}
|
||||
assert(wl_list_empty(&peerCtx->loop_task_list));
|
||||
|
||||
if (peerCtx->loop_task_event_source_fd != -1) {
|
||||
close(peerCtx->loop_task_event_source_fd);
|
||||
peerCtx->loop_task_event_source_fd = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&peerCtx->loop_task_list_mutex);
|
||||
}
|
||||
|
||||
/* This is a little tricky - it makes sure there's always at least
|
||||
* one spare byte in the array in case the caller needs to add a
|
||||
* null terminator to it. We can't just null terminate the array
|
||||
* here, because some callers won't want that - and some won't
|
||||
* like having an odd number of bytes.
|
||||
*/
|
||||
int
|
||||
rdp_wl_array_read_fd(struct wl_array *array, int fd)
|
||||
{
|
||||
int len, size;
|
||||
char *data;
|
||||
|
||||
/* Make sure we have at least 1024 bytes of space left */
|
||||
if (array->alloc - array->size < 1024) {
|
||||
if (!wl_array_add(array, 1024)) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
array->size -= 1024;
|
||||
}
|
||||
data = (char *)array->data + array->size;
|
||||
/* Leave one char in case the caller needs space for a
|
||||
* null terminator */
|
||||
size = array->alloc - array->size - 1;
|
||||
do {
|
||||
len = read(fd, data, size);
|
||||
} while (len == -1 && errno == EINTR);
|
||||
|
||||
if (len == -1)
|
||||
return -1;
|
||||
|
||||
array->size += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
+125
-150
@@ -76,7 +76,6 @@ struct wayland_backend {
|
||||
struct wl_display *wl_display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shell *shell;
|
||||
struct xdg_wm_base *xdg_wm_base;
|
||||
struct zwp_fullscreen_shell_v1 *fshell;
|
||||
struct wl_shm *shm;
|
||||
@@ -112,7 +111,6 @@ struct wayland_output {
|
||||
struct wl_output *output;
|
||||
uint32_t global_id;
|
||||
|
||||
struct wl_shell_surface *shell_surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
int configure_width, configure_height;
|
||||
@@ -233,15 +231,25 @@ struct wayland_input {
|
||||
|
||||
struct gl_renderer_interface *gl_renderer;
|
||||
|
||||
static void
|
||||
wayland_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct wayland_head *
|
||||
to_wayland_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != wayland_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct wayland_head, base);
|
||||
}
|
||||
|
||||
static void
|
||||
wayland_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct wayland_output *
|
||||
to_wayland_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != wayland_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct wayland_output, base);
|
||||
}
|
||||
|
||||
@@ -293,7 +301,7 @@ wayland_output_get_shm_buffer(struct wayland_output *output)
|
||||
|
||||
struct wl_shm_pool *pool;
|
||||
int width, height, stride;
|
||||
int32_t fx, fy;
|
||||
struct weston_geometry area;
|
||||
int fd;
|
||||
unsigned char *data;
|
||||
|
||||
@@ -369,13 +377,20 @@ wayland_output_get_shm_buffer(struct wayland_output *output)
|
||||
cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32,
|
||||
width, height, stride);
|
||||
|
||||
fx = 0;
|
||||
fy = 0;
|
||||
if (output->frame)
|
||||
frame_interior(output->frame, &fx, &fy, 0, 0);
|
||||
if (output->frame) {
|
||||
frame_interior(output->frame, &area.x, &area.y,
|
||||
&area.width, &area.height);
|
||||
} else {
|
||||
area.x = 0;
|
||||
area.y = 0;
|
||||
area.width = output->base.current_mode->width;
|
||||
area.height = output->base.current_mode->height;
|
||||
}
|
||||
|
||||
/* Address only the interior, excluding output decorations */
|
||||
sb->pm_image =
|
||||
pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height,
|
||||
(uint32_t *)(data + fy * stride) + fx,
|
||||
pixman_image_create_bits(PIXMAN_a8r8g8b8, area.width, area.height,
|
||||
(uint32_t *)(data + area.y * stride) + area.x,
|
||||
stride);
|
||||
|
||||
return sb;
|
||||
@@ -501,8 +516,11 @@ static int
|
||||
wayland_output_start_repaint_loop(struct weston_output *output_base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_backend *wb =
|
||||
to_wayland_backend(output->base.compositor);
|
||||
struct wayland_backend *wb;
|
||||
|
||||
assert(output);
|
||||
|
||||
wb = to_wayland_backend(output->base.compositor);
|
||||
|
||||
/* If this is the initial frame, we need to attach a buffer so that
|
||||
* the compositor can map the surface and include it in its render
|
||||
@@ -526,11 +544,14 @@ wayland_output_start_repaint_loop(struct weston_output *output_base)
|
||||
#ifdef ENABLE_EGL
|
||||
static int
|
||||
wayland_output_repaint_gl(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct weston_compositor *ec;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
|
||||
output->frame_cb = wl_surface_frame(output->parent.surface);
|
||||
wl_callback_add_listener(output->frame_cb, &frame_listener, output);
|
||||
@@ -638,14 +659,16 @@ wayland_shm_buffer_attach(struct wayland_shm_buffer *sb)
|
||||
|
||||
static int
|
||||
wayland_output_repaint_pixman(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_backend *b =
|
||||
to_wayland_backend(output->base.compositor);
|
||||
struct wayland_backend *b;
|
||||
struct wayland_shm_buffer *sb;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_wayland_backend(output->base.compositor);
|
||||
|
||||
if (output->frame) {
|
||||
if (frame_status(output->frame) & FRAME_STATUS_REPAINT)
|
||||
wl_list_for_each(sb, &output->shm.buffers, link)
|
||||
@@ -692,11 +715,6 @@ wayland_backend_destroy_output_surface(struct wayland_output *output)
|
||||
output->parent.xdg_surface = NULL;
|
||||
}
|
||||
|
||||
if (output->parent.shell_surface) {
|
||||
wl_shell_surface_destroy(output->parent.shell_surface);
|
||||
output->parent.shell_surface = NULL;
|
||||
}
|
||||
|
||||
wl_surface_destroy(output->parent.surface);
|
||||
output->parent.surface = NULL;
|
||||
}
|
||||
@@ -718,7 +736,11 @@ static int
|
||||
wayland_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(base);
|
||||
struct wayland_backend *b = to_wayland_backend(base->compositor);
|
||||
struct wayland_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_wayland_backend(base->compositor);
|
||||
|
||||
if (!output->base.enabled)
|
||||
return 0;
|
||||
@@ -752,6 +774,8 @@ wayland_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(base);
|
||||
|
||||
assert(output);
|
||||
|
||||
wayland_output_disable(&output->base);
|
||||
|
||||
weston_output_release(&output->base);
|
||||
@@ -763,8 +787,6 @@ wayland_output_destroy(struct weston_output *base)
|
||||
free(output);
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener;
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
static int
|
||||
wayland_output_init_gl_renderer(struct wayland_output *output)
|
||||
@@ -816,64 +838,54 @@ wayland_output_init_pixman_renderer(struct wayland_output *output)
|
||||
static void
|
||||
wayland_output_resize_surface(struct wayland_output *output)
|
||||
{
|
||||
struct wayland_backend *b =
|
||||
to_wayland_backend(output->base.compositor);
|
||||
int32_t ix, iy, iwidth, iheight;
|
||||
int32_t width, height;
|
||||
struct wayland_backend *b = to_wayland_backend(output->base.compositor);
|
||||
/* Defaults for without frame: */
|
||||
struct weston_size fb_size = {
|
||||
.width = output->base.current_mode->width,
|
||||
.height = output->base.current_mode->height
|
||||
};
|
||||
struct weston_geometry area = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = fb_size.width,
|
||||
.height = fb_size.height
|
||||
};
|
||||
struct weston_geometry inp = area;
|
||||
struct weston_geometry opa = area;
|
||||
struct wl_region *region;
|
||||
|
||||
width = output->base.current_mode->width;
|
||||
height = output->base.current_mode->height;
|
||||
|
||||
if (output->frame) {
|
||||
frame_resize_inside(output->frame, width, height);
|
||||
frame_resize_inside(output->frame, area.width, area.height);
|
||||
frame_interior(output->frame, &area.x, &area.y, NULL, NULL);
|
||||
fb_size.width = frame_width(output->frame);
|
||||
fb_size.height = frame_height(output->frame);
|
||||
|
||||
frame_input_rect(output->frame, &inp.x, &inp.y,
|
||||
&inp.width, &inp.height);
|
||||
frame_opaque_rect(output->frame, &opa.x, &opa.y,
|
||||
&opa.width, &opa.height);
|
||||
}
|
||||
|
||||
frame_input_rect(output->frame, &ix, &iy, &iwidth, &iheight);
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, ix, iy, iwidth, iheight);
|
||||
wl_region_add(region, inp.x, inp.y, inp.width, inp.height);
|
||||
wl_surface_set_input_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
if (output->parent.xdg_surface) {
|
||||
xdg_surface_set_window_geometry(output->parent.xdg_surface,
|
||||
ix,
|
||||
iy,
|
||||
iwidth,
|
||||
iheight);
|
||||
inp.x, inp.y,
|
||||
inp.width, inp.height);
|
||||
}
|
||||
|
||||
frame_opaque_rect(output->frame, &ix, &iy, &iwidth, &iheight);
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, ix, iy, iwidth, iheight);
|
||||
wl_region_add(region, opa.x, opa.y, opa.width, opa.height);
|
||||
wl_surface_set_opaque_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
width = frame_width(output->frame);
|
||||
height = frame_height(output->frame);
|
||||
} else {
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, 0, 0, width, height);
|
||||
wl_surface_set_input_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
region = wl_compositor_create_region(b->parent.compositor);
|
||||
wl_region_add(region, 0, 0, width, height);
|
||||
wl_surface_set_opaque_region(output->parent.surface, region);
|
||||
wl_region_destroy(region);
|
||||
|
||||
if (output->parent.xdg_surface) {
|
||||
xdg_surface_set_window_geometry(output->parent.xdg_surface,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_EGL
|
||||
if (output->gl.egl_window) {
|
||||
wl_egl_window_resize(output->gl.egl_window,
|
||||
width, height, 0, 0);
|
||||
fb_size.width, fb_size.height, 0, 0);
|
||||
|
||||
/* These will need to be re-created due to the resize */
|
||||
gl_renderer->output_set_border(&output->base,
|
||||
@@ -928,8 +940,6 @@ wayland_output_set_windowed(struct wayland_output *output)
|
||||
|
||||
if (output->parent.xdg_toplevel) {
|
||||
xdg_toplevel_unset_fullscreen(output->parent.xdg_toplevel);
|
||||
} else if (output->parent.shell_surface) {
|
||||
wl_shell_surface_set_toplevel(output->parent.shell_surface);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
@@ -939,7 +949,6 @@ wayland_output_set_windowed(struct wayland_output *output)
|
||||
|
||||
static void
|
||||
wayland_output_set_fullscreen(struct wayland_output *output,
|
||||
enum wl_shell_surface_fullscreen_method method,
|
||||
uint32_t framerate, struct wl_output *target)
|
||||
{
|
||||
if (output->frame) {
|
||||
@@ -951,9 +960,6 @@ wayland_output_set_fullscreen(struct wayland_output *output,
|
||||
|
||||
if (output->parent.xdg_toplevel) {
|
||||
xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel, target);
|
||||
} else if (output->parent.shell_surface) {
|
||||
wl_shell_surface_set_fullscreen(output->parent.shell_surface,
|
||||
method, framerate, target);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
@@ -1062,7 +1068,7 @@ static int
|
||||
wayland_output_switch_mode(struct weston_output *output_base,
|
||||
struct weston_mode *mode)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_output *output;
|
||||
struct wayland_backend *b;
|
||||
struct wl_surface *old_surface;
|
||||
struct weston_mode *old_mode;
|
||||
@@ -1073,6 +1079,9 @@ wayland_output_switch_mode(struct weston_output *output_base,
|
||||
return -1;
|
||||
}
|
||||
|
||||
output = to_wayland_output(output_base);
|
||||
assert(output);
|
||||
|
||||
if (mode == NULL) {
|
||||
weston_log("mode is NULL.\n");
|
||||
return -1;
|
||||
@@ -1080,7 +1089,7 @@ wayland_output_switch_mode(struct weston_output *output_base,
|
||||
|
||||
b = to_wayland_backend(output_base->compositor);
|
||||
|
||||
if (output->parent.xdg_surface || output->parent.shell_surface || !b->parent.fshell)
|
||||
if (output->parent.xdg_surface || !b->parent.fshell)
|
||||
return -1;
|
||||
|
||||
mode = wayland_output_choose_mode(output, mode);
|
||||
@@ -1221,20 +1230,6 @@ wayland_backend_create_output_surface(struct wayland_output *output)
|
||||
|
||||
weston_log("wayland-backend: Using xdg_wm_base\n");
|
||||
}
|
||||
else if (b->parent.shell) {
|
||||
output->parent.shell_surface =
|
||||
wl_shell_get_shell_surface(b->parent.shell,
|
||||
output->parent.surface);
|
||||
if (!output->parent.shell_surface) {
|
||||
wl_surface_destroy(output->parent.surface);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wl_shell_surface_add_listener(output->parent.shell_surface,
|
||||
&shell_surface_listener, output);
|
||||
|
||||
weston_log("wayland-backend: Using wl_shell\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1243,10 +1238,14 @@ static int
|
||||
wayland_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(base);
|
||||
struct wayland_backend *b = to_wayland_backend(base->compositor);
|
||||
struct wayland_backend *b;
|
||||
enum mode_status mode_status;
|
||||
int ret = 0;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_wayland_backend(base->compositor);
|
||||
|
||||
wl_list_init(&output->shm.buffers);
|
||||
wl_list_init(&output->shm.free_buffers);
|
||||
|
||||
@@ -1295,13 +1294,9 @@ wayland_output_enable(struct weston_output *base)
|
||||
|
||||
output->parent.draw_initial_frame = true;
|
||||
}
|
||||
} else {
|
||||
wayland_output_set_fullscreen(output,
|
||||
WL_SHELL_SURFACE_FULLSCREEN_METHOD_DRIVER,
|
||||
output->mode.refresh, output->parent.output);
|
||||
}
|
||||
} else if (b->fullscreen) {
|
||||
wayland_output_set_fullscreen(output, 0, 0, NULL);
|
||||
wayland_output_set_fullscreen(output, 0, NULL);
|
||||
} else {
|
||||
wayland_output_set_windowed(output);
|
||||
}
|
||||
@@ -1326,9 +1321,16 @@ static int
|
||||
wayland_output_attach_head(struct weston_output *output_base,
|
||||
struct weston_head *head_base)
|
||||
{
|
||||
struct wayland_backend *b = to_wayland_backend(output_base->compositor);
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
struct wayland_head *head = to_wayland_head(head_base);
|
||||
struct wayland_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
if (!head)
|
||||
return -1;
|
||||
|
||||
b = to_wayland_backend(output_base->compositor);
|
||||
|
||||
if (!wl_list_empty(&output->base.head_list))
|
||||
return -1;
|
||||
@@ -1353,6 +1355,8 @@ wayland_output_detach_head(struct weston_output *output_base,
|
||||
{
|
||||
struct wayland_output *output = to_wayland_output(output_base);
|
||||
|
||||
assert(output);
|
||||
|
||||
/* Rely on the disable hook if the output was enabled. We do not
|
||||
* support cloned heads, so detaching is guaranteed to disable the
|
||||
* output.
|
||||
@@ -1411,6 +1415,9 @@ wayland_head_create(struct weston_compositor *compositor, const char *name)
|
||||
return NULL;
|
||||
|
||||
weston_head_init(&head->base, name);
|
||||
|
||||
head->base.backend_id = wayland_head_destroy;
|
||||
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_compositor_add_head(compositor, &head->base);
|
||||
|
||||
@@ -1458,8 +1465,12 @@ wayland_head_create_for_parent_output(struct weston_compositor *compositor,
|
||||
}
|
||||
|
||||
static void
|
||||
wayland_head_destroy(struct wayland_head *head)
|
||||
wayland_head_destroy(struct weston_head *base)
|
||||
{
|
||||
struct wayland_head *head = to_wayland_head(base);
|
||||
|
||||
assert(head);
|
||||
|
||||
if (head->parent_output)
|
||||
head->parent_output->head = NULL;
|
||||
|
||||
@@ -1474,6 +1485,9 @@ wayland_output_set_size(struct weston_output *base, int width, int height)
|
||||
struct weston_head *head;
|
||||
int output_width, output_height;
|
||||
|
||||
if (!output)
|
||||
return -1;
|
||||
|
||||
/* We can only be called once. */
|
||||
assert(!output->base.current_mode);
|
||||
|
||||
@@ -1564,13 +1578,10 @@ wayland_output_setup_fullscreen(struct wayland_output *output,
|
||||
return -1;
|
||||
|
||||
/* What should size be set if conditional is false? */
|
||||
if (b->parent.xdg_wm_base || b->parent.shell) {
|
||||
if (b->parent.xdg_wm_base) {
|
||||
if (output->parent.xdg_toplevel)
|
||||
xdg_toplevel_set_fullscreen(output->parent.xdg_toplevel,
|
||||
output->parent.output);
|
||||
else if (output->parent.shell_surface)
|
||||
wl_shell_surface_set_fullscreen(output->parent.shell_surface,
|
||||
0, 0, NULL);
|
||||
|
||||
wl_display_roundtrip(b->parent.wl_display);
|
||||
|
||||
@@ -1594,36 +1605,6 @@ err_set_size:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_ping(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
wl_shell_surface_pong(shell_surface, serial);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_configure(void *data, struct wl_shell_surface *shell_surface,
|
||||
uint32_t edges, int32_t width, int32_t height)
|
||||
{
|
||||
struct wayland_output *output = data;
|
||||
|
||||
output->parent.configure_width = width;
|
||||
output->parent.configure_height = height;
|
||||
|
||||
/* FIXME: implement resizing */
|
||||
}
|
||||
|
||||
static void
|
||||
shell_surface_popup_done(void *data, struct wl_shell_surface *shell_surface)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct wl_shell_surface_listener shell_surface_listener = {
|
||||
shell_surface_ping,
|
||||
shell_surface_configure,
|
||||
shell_surface_popup_done
|
||||
};
|
||||
|
||||
/* Events received from the wayland-server this compositor is client of: */
|
||||
|
||||
/* parent input interface */
|
||||
@@ -1801,9 +1782,6 @@ input_handle_button(void *data, struct wl_pointer *pointer,
|
||||
if (input->output->parent.xdg_toplevel)
|
||||
xdg_toplevel_move(input->output->parent.xdg_toplevel,
|
||||
input->parent.seat, serial);
|
||||
else if (input->output->parent.shell_surface)
|
||||
wl_shell_surface_move(input->output->parent.shell_surface,
|
||||
input->parent.seat, serial);
|
||||
frame_status_clear(input->output->frame,
|
||||
FRAME_STATUS_MOVE);
|
||||
return;
|
||||
@@ -2157,9 +2135,6 @@ input_handle_touch_down(void *data, struct wl_touch *wl_touch,
|
||||
if (output->parent.xdg_toplevel)
|
||||
xdg_toplevel_move(output->parent.xdg_toplevel,
|
||||
input->parent.seat, serial);
|
||||
else if (output->parent.shell_surface)
|
||||
wl_shell_surface_move(output->parent.shell_surface,
|
||||
input->parent.seat, serial);
|
||||
frame_status_clear(output->frame,
|
||||
FRAME_STATUS_MOVE);
|
||||
return;
|
||||
@@ -2187,10 +2162,6 @@ input_handle_touch_up(void *data, struct wl_touch *wl_touch,
|
||||
timespec_from_msec(&ts, time);
|
||||
|
||||
input->touch_points--;
|
||||
if (input->touch_points == 0) {
|
||||
input->touch_focus = NULL;
|
||||
input->touch_active = false;
|
||||
}
|
||||
|
||||
if (!output)
|
||||
return;
|
||||
@@ -2253,6 +2224,11 @@ input_handle_touch_frame(void *data, struct wl_touch *wl_touch)
|
||||
return;
|
||||
|
||||
notify_touch_frame(input->touch_device);
|
||||
|
||||
if (input->touch_points == 0) {
|
||||
input->touch_focus = NULL;
|
||||
input->touch_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2629,7 +2605,7 @@ wayland_parent_output_destroy(struct wayland_parent_output *output)
|
||||
wl_callback_destroy(output->sync_cb);
|
||||
|
||||
if (output->head)
|
||||
wayland_head_destroy(output->head);
|
||||
wayland_head_destroy(&output->head->base);
|
||||
|
||||
wl_output_destroy(output->global);
|
||||
free(output->physical.make);
|
||||
@@ -2671,10 +2647,6 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t name,
|
||||
&xdg_wm_base_interface, 1);
|
||||
xdg_wm_base_add_listener(b->parent.xdg_wm_base,
|
||||
&wm_base_listener, b);
|
||||
} else if (strcmp(interface, "wl_shell") == 0) {
|
||||
b->parent.shell =
|
||||
wl_registry_bind(registry, name,
|
||||
&wl_shell_interface, 1);
|
||||
} else if (strcmp(interface, "zwp_fullscreen_shell_v1") == 0) {
|
||||
b->parent.fshell =
|
||||
wl_registry_bind(registry, name,
|
||||
@@ -2742,14 +2714,20 @@ wayland_destroy(struct weston_compositor *ec)
|
||||
{
|
||||
struct wayland_backend *b = to_wayland_backend(ec);
|
||||
struct weston_head *base, *next;
|
||||
struct wayland_parent_output *output, *next_output;
|
||||
struct wayland_input *input, *next_input;
|
||||
|
||||
wl_event_source_remove(b->parent.wl_source);
|
||||
|
||||
weston_compositor_shutdown(ec);
|
||||
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
|
||||
wayland_head_destroy(to_wayland_head(base));
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
|
||||
if (to_wayland_head(base))
|
||||
wayland_head_destroy(base);
|
||||
}
|
||||
|
||||
wl_list_for_each_safe(output, next_output, &b->parent.output_list, link)
|
||||
wayland_parent_output_destroy(output);
|
||||
|
||||
wl_list_for_each_safe(input, next_input, &b->input_list, link)
|
||||
wayland_input_destroy(input);
|
||||
@@ -2763,9 +2741,6 @@ wayland_destroy(struct weston_compositor *ec)
|
||||
if (b->parent.xdg_wm_base)
|
||||
xdg_wm_base_destroy(b->parent.xdg_wm_base);
|
||||
|
||||
if (b->parent.shell)
|
||||
wl_shell_destroy(b->parent.shell);
|
||||
|
||||
if (b->parent.fshell)
|
||||
zwp_fullscreen_shell_v1_release(b->parent.fshell);
|
||||
|
||||
@@ -2833,7 +2808,7 @@ fullscreen_binding(struct weston_keyboard *keyboard,
|
||||
return;
|
||||
|
||||
if (input->output->frame)
|
||||
wayland_output_set_fullscreen(input->output, 0, 0, NULL);
|
||||
wayland_output_set_fullscreen(input->output, 0, NULL);
|
||||
else
|
||||
wayland_output_set_windowed(input->output);
|
||||
|
||||
|
||||
+89
-43
@@ -119,6 +119,7 @@ struct x11_backend {
|
||||
xcb_atom_t cardinal;
|
||||
xcb_atom_t xkb_names;
|
||||
} atom;
|
||||
xcb_generic_event_t *prev_event;
|
||||
};
|
||||
|
||||
struct x11_head {
|
||||
@@ -151,15 +152,25 @@ struct window_delete_data {
|
||||
|
||||
struct gl_renderer_interface *gl_renderer;
|
||||
|
||||
static void
|
||||
x11_head_destroy(struct weston_head *base);
|
||||
|
||||
static inline struct x11_head *
|
||||
to_x11_head(struct weston_head *base)
|
||||
{
|
||||
if (base->backend_id != x11_head_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct x11_head, base);
|
||||
}
|
||||
|
||||
static void
|
||||
x11_output_destroy(struct weston_output *base);
|
||||
|
||||
static inline struct x11_output *
|
||||
to_x11_output(struct weston_output *base)
|
||||
{
|
||||
if (base->destroy != x11_output_destroy)
|
||||
return NULL;
|
||||
return container_of(base, struct x11_output, base);
|
||||
}
|
||||
|
||||
@@ -417,11 +428,14 @@ x11_output_start_repaint_loop(struct weston_output *output)
|
||||
|
||||
static int
|
||||
x11_output_repaint_gl(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct weston_compositor *ec;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
|
||||
ec->renderer->repaint_output(output_base, damage);
|
||||
|
||||
@@ -436,8 +450,8 @@ static void
|
||||
set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct x11_backend *b = to_x11_backend(ec);
|
||||
struct weston_compositor *ec;
|
||||
struct x11_backend *b;
|
||||
pixman_region32_t transformed_region;
|
||||
pixman_box32_t *rects;
|
||||
xcb_rectangle_t *output_rects;
|
||||
@@ -445,6 +459,12 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
|
||||
int nrects, i;
|
||||
xcb_generic_error_t *err;
|
||||
|
||||
if (!output)
|
||||
return;
|
||||
|
||||
ec = output->base.compositor;
|
||||
b = to_x11_backend(ec);
|
||||
|
||||
pixman_region32_init(&transformed_region);
|
||||
pixman_region32_copy(&transformed_region, region);
|
||||
pixman_region32_translate(&transformed_region,
|
||||
@@ -486,15 +506,19 @@ set_clip_for_output(struct weston_output *output_base, pixman_region32_t *region
|
||||
|
||||
static int
|
||||
x11_output_repaint_shm(struct weston_output *output_base,
|
||||
pixman_region32_t *damage,
|
||||
void *repaint_data)
|
||||
pixman_region32_t *damage)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(output_base);
|
||||
struct weston_compositor *ec = output->base.compositor;
|
||||
struct x11_backend *b = to_x11_backend(ec);
|
||||
struct weston_compositor *ec;
|
||||
struct x11_backend *b;
|
||||
xcb_void_cookie_t cookie;
|
||||
xcb_generic_error_t *err;
|
||||
|
||||
assert(output);
|
||||
|
||||
ec = output->base.compositor;
|
||||
b = to_x11_backend(ec);
|
||||
|
||||
pixman_renderer_output_set_buffer(output_base, output->hw_surface);
|
||||
ec->renderer->repaint_output(output_base, damage);
|
||||
|
||||
@@ -803,12 +827,13 @@ static int
|
||||
x11_output_switch_mode(struct weston_output *base, struct weston_mode *mode)
|
||||
{
|
||||
struct x11_backend *b;
|
||||
struct x11_output *output;
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
static uint32_t values[2];
|
||||
int ret;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_x11_backend(base->compositor);
|
||||
output = to_x11_output(base);
|
||||
|
||||
if (mode->width == output->mode.width &&
|
||||
mode->height == output->mode.height)
|
||||
@@ -880,7 +905,11 @@ static int
|
||||
x11_output_disable(struct weston_output *base)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
struct x11_backend *backend = to_x11_backend(base->compositor);
|
||||
struct x11_backend *backend;
|
||||
|
||||
assert(output);
|
||||
|
||||
backend = to_x11_backend(base->compositor);
|
||||
|
||||
if (!output->base.enabled)
|
||||
return 0;
|
||||
@@ -905,6 +934,8 @@ x11_output_destroy(struct weston_output *base)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
|
||||
assert(output);
|
||||
|
||||
x11_output_disable(&output->base);
|
||||
weston_output_release(&output->base);
|
||||
|
||||
@@ -915,7 +946,12 @@ static int
|
||||
x11_output_enable(struct weston_output *base)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
struct x11_backend *b = to_x11_backend(base->compositor);
|
||||
const struct weston_mode *mode = output->base.current_mode;
|
||||
struct x11_backend *b;
|
||||
|
||||
assert(output);
|
||||
|
||||
b = to_x11_backend(base->compositor);
|
||||
|
||||
static const char name[] = "Weston Compositor";
|
||||
static const char class[] = "weston-1\0Weston Compositor";
|
||||
@@ -954,8 +990,7 @@ x11_output_enable(struct weston_output *base)
|
||||
output->window,
|
||||
screen->root,
|
||||
0, 0,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
mode->width, mode->height,
|
||||
0,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||
screen->root_visual,
|
||||
@@ -1020,8 +1055,7 @@ x11_output_enable(struct weston_output *base)
|
||||
.use_shadow = true,
|
||||
};
|
||||
if (x11_output_init_shm(b, output,
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height) < 0) {
|
||||
mode->width, mode->height) < 0) {
|
||||
weston_log("Failed to initialize SHM for the X11 output\n");
|
||||
goto err;
|
||||
}
|
||||
@@ -1062,9 +1096,7 @@ x11_output_enable(struct weston_output *base)
|
||||
wl_event_loop_add_timer(loop, finish_frame_handler, output);
|
||||
|
||||
weston_log("x11 output %dx%d, window id %d\n",
|
||||
output->base.current_mode->width,
|
||||
output->base.current_mode->height,
|
||||
output->window);
|
||||
mode->width, mode->height, output->window);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1079,11 +1111,17 @@ static int
|
||||
x11_output_set_size(struct weston_output *base, int width, int height)
|
||||
{
|
||||
struct x11_output *output = to_x11_output(base);
|
||||
struct x11_backend *b = to_x11_backend(base->compositor);
|
||||
struct x11_backend *b;
|
||||
struct weston_head *head;
|
||||
xcb_screen_t *scrn = b->screen;
|
||||
xcb_screen_t *scrn;
|
||||
int output_width, output_height;
|
||||
|
||||
if (!output)
|
||||
return -1;
|
||||
|
||||
b = to_x11_backend(base->compositor);
|
||||
scrn = b->screen;
|
||||
|
||||
/* We can only be called once. */
|
||||
assert(!output->base.current_mode);
|
||||
|
||||
@@ -1165,6 +1203,9 @@ x11_head_create(struct weston_compositor *compositor, const char *name)
|
||||
return -1;
|
||||
|
||||
weston_head_init(&head->base, name);
|
||||
|
||||
head->base.backend_id = x11_head_destroy;
|
||||
|
||||
weston_head_set_connection_status(&head->base, true);
|
||||
weston_compositor_add_head(compositor, &head->base);
|
||||
|
||||
@@ -1172,8 +1213,12 @@ x11_head_create(struct weston_compositor *compositor, const char *name)
|
||||
}
|
||||
|
||||
static void
|
||||
x11_head_destroy(struct x11_head *head)
|
||||
x11_head_destroy(struct weston_head *base)
|
||||
{
|
||||
struct x11_head *head = to_x11_head(base);
|
||||
|
||||
assert(head);
|
||||
|
||||
weston_head_release(&head->base);
|
||||
free(head);
|
||||
}
|
||||
@@ -1450,7 +1495,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
{
|
||||
struct x11_backend *b = data;
|
||||
struct x11_output *output;
|
||||
xcb_generic_event_t *event, *prev;
|
||||
xcb_generic_event_t *event;
|
||||
xcb_client_message_event_t *client_message;
|
||||
xcb_enter_notify_event_t *enter_notify;
|
||||
xcb_key_press_event_t *key_press, *key_release;
|
||||
@@ -1466,16 +1511,15 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
int count;
|
||||
struct timespec time;
|
||||
|
||||
prev = NULL;
|
||||
count = 0;
|
||||
while (x11_backend_next_event(b, &event, mask)) {
|
||||
response_type = event->response_type & ~0x80;
|
||||
|
||||
switch (prev ? prev->response_type & ~0x80 : 0x80) {
|
||||
switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) {
|
||||
case XCB_KEY_RELEASE:
|
||||
/* Suppress key repeat events; this is only used if we
|
||||
* don't have XCB XKB support. */
|
||||
key_release = (xcb_key_press_event_t *) prev;
|
||||
key_release = (xcb_key_press_event_t *) b->prev_event;
|
||||
key_press = (xcb_key_press_event_t *) event;
|
||||
if (response_type == XCB_KEY_PRESS &&
|
||||
key_release->time == key_press->time &&
|
||||
@@ -1483,8 +1527,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
/* Don't deliver the held key release
|
||||
* event or the new key press event. */
|
||||
free(event);
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
continue;
|
||||
} else {
|
||||
/* Deliver the held key release now
|
||||
@@ -1497,8 +1541,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
key_release->detail - 8,
|
||||
WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
STATE_UPDATE_AUTOMATIC);
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1522,8 +1566,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
notify_keyboard_focus_in(&b->core_seat, &b->keys,
|
||||
STATE_UPDATE_AUTOMATIC);
|
||||
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1548,7 +1592,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
/* If we don't have XKB, we need to use the lame
|
||||
* autorepeat detection above. */
|
||||
if (!b->has_xkb) {
|
||||
prev = event;
|
||||
b->prev_event = event;
|
||||
break;
|
||||
}
|
||||
key_release = (xcb_key_press_event_t *) event;
|
||||
@@ -1643,7 +1687,7 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
if (focus_in->mode == XCB_NOTIFY_MODE_WHILE_GRABBED)
|
||||
break;
|
||||
|
||||
prev = event;
|
||||
b->prev_event = event;
|
||||
break;
|
||||
|
||||
case XCB_FOCUS_OUT:
|
||||
@@ -1677,13 +1721,13 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
#endif
|
||||
|
||||
count++;
|
||||
if (prev != event)
|
||||
free (event);
|
||||
if (b->prev_event != event)
|
||||
free(event);
|
||||
}
|
||||
|
||||
switch (prev ? prev->response_type & ~0x80 : 0x80) {
|
||||
switch (b->prev_event ? b->prev_event->response_type & ~0x80 : 0x80) {
|
||||
case XCB_KEY_RELEASE:
|
||||
key_release = (xcb_key_press_event_t *) prev;
|
||||
key_release = (xcb_key_press_event_t *) b->prev_event;
|
||||
update_xkb_state_from_core(b, key_release->state);
|
||||
weston_compositor_get_time(&time);
|
||||
notify_key(&b->core_seat,
|
||||
@@ -1691,8 +1735,8 @@ x11_backend_handle_event(int fd, uint32_t mask, void *data)
|
||||
key_release->detail - 8,
|
||||
WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
STATE_UPDATE_AUTOMATIC);
|
||||
free(prev);
|
||||
prev = NULL;
|
||||
free(b->prev_event);
|
||||
b->prev_event = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -1792,8 +1836,10 @@ x11_destroy(struct weston_compositor *ec)
|
||||
|
||||
weston_compositor_shutdown(ec); /* destroys outputs, too */
|
||||
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link)
|
||||
x11_head_destroy(to_x11_head(base));
|
||||
wl_list_for_each_safe(base, next, &ec->head_list, compositor_link) {
|
||||
if (to_x11_head(base))
|
||||
x11_head_destroy(base);
|
||||
}
|
||||
|
||||
XCloseDisplay(backend->dpy);
|
||||
free(backend);
|
||||
|
||||
+12
-12
@@ -32,6 +32,8 @@
|
||||
#ifndef LIBWESTON_BACKEND_INTERNAL_H
|
||||
#define LIBWESTON_BACKEND_INTERNAL_H
|
||||
|
||||
struct weston_hdr_metadata_type1;
|
||||
|
||||
struct weston_backend {
|
||||
void (*destroy)(struct weston_compositor *compositor);
|
||||
|
||||
@@ -46,27 +48,21 @@ struct weston_backend {
|
||||
* Returns an opaque pointer, which the backend may use as private
|
||||
* data referring to the repaint cycle.
|
||||
*/
|
||||
void * (*repaint_begin)(struct weston_compositor *compositor);
|
||||
void (*repaint_begin)(struct weston_compositor *compositor);
|
||||
|
||||
/** Cancel a repaint sequence
|
||||
*
|
||||
* Cancels a repaint sequence, when an error has occurred during
|
||||
* one output's repaint; see repaint_begin.
|
||||
*
|
||||
* @param repaint_data Data returned by repaint_begin
|
||||
*/
|
||||
void (*repaint_cancel)(struct weston_compositor *compositor,
|
||||
void *repaint_data);
|
||||
void (*repaint_cancel)(struct weston_compositor *compositor);
|
||||
|
||||
/** Conclude a repaint sequence
|
||||
*
|
||||
* Called on successful completion of a repaint sequence; see
|
||||
* repaint_begin.
|
||||
*
|
||||
* @param repaint_data Data returned by repaint_begin
|
||||
*/
|
||||
int (*repaint_flush)(struct weston_compositor *compositor,
|
||||
void *repaint_data);
|
||||
int (*repaint_flush)(struct weston_compositor *compositor);
|
||||
|
||||
/** Allocate a new output
|
||||
*
|
||||
@@ -142,6 +138,10 @@ weston_head_set_subpixel(struct weston_head *head,
|
||||
void
|
||||
weston_head_set_transform(struct weston_head *head, uint32_t transform);
|
||||
|
||||
void
|
||||
weston_head_set_supported_eotf_mask(struct weston_head *head,
|
||||
uint32_t eotf_mask);
|
||||
|
||||
/* weston_output */
|
||||
|
||||
void
|
||||
@@ -154,9 +154,6 @@ weston_output_damage(struct weston_output *output);
|
||||
void
|
||||
weston_output_release(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_init_zoom(struct weston_output *output);
|
||||
|
||||
void
|
||||
weston_output_finish_frame(struct weston_output *output,
|
||||
const struct timespec *stamp,
|
||||
@@ -178,6 +175,9 @@ void
|
||||
weston_output_region_from_global(struct weston_output *output,
|
||||
pixman_region32_t *region);
|
||||
|
||||
const struct weston_hdr_metadata_type1 *
|
||||
weston_output_get_hdr_metadata_type1(const struct weston_output *output);
|
||||
|
||||
/* weston_seat */
|
||||
|
||||
void
|
||||
|
||||
@@ -310,8 +310,8 @@ weston_compositor_run_key_binding(struct weston_compositor *compositor,
|
||||
/* If this was a key binding and it didn't
|
||||
* install a keyboard grab, install one now to
|
||||
* swallow the key press. */
|
||||
if (keyboard->grab ==
|
||||
&keyboard->default_grab)
|
||||
if (keyboard->grab == &keyboard->default_grab ||
|
||||
keyboard->grab == &keyboard->input_method_grab)
|
||||
install_binding_grab(keyboard,
|
||||
time,
|
||||
key,
|
||||
|
||||
@@ -110,7 +110,7 @@ clipboard_source_data(int fd, uint32_t mask, void *data)
|
||||
|
||||
static void
|
||||
clipboard_source_accept(struct weston_data_source *source,
|
||||
uint32_t time, const char *mime_type)
|
||||
uint32_t serial, const char *mime_type)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -27,12 +27,37 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <libweston/libweston.h>
|
||||
|
||||
#include "color.h"
|
||||
#include "color-lcms.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
static cmsUInt32Number
|
||||
cmlcms_get_render_intent(enum cmlcms_category cat,
|
||||
struct weston_surface *surface,
|
||||
struct weston_output *output)
|
||||
{
|
||||
/*
|
||||
* TODO: Take into account client provided content profile,
|
||||
* output profile, and the category of the wanted color
|
||||
* transformation.
|
||||
*/
|
||||
cmsUInt32Number intent = INTENT_RELATIVE_COLORIMETRIC;
|
||||
return intent;
|
||||
}
|
||||
|
||||
static struct cmlcms_color_profile *
|
||||
get_cprof_or_stock_sRGB(struct weston_color_manager_lcms *cm,
|
||||
struct weston_color_profile *cprof_base)
|
||||
{
|
||||
if (cprof_base)
|
||||
return get_cprof(cprof_base);
|
||||
else
|
||||
return cm->sRGB_profile;
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_destroy_color_transform(struct weston_color_transform *xform_base)
|
||||
{
|
||||
@@ -48,47 +73,52 @@ cmlcms_get_surface_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_surface_color_transform *surf_xform)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
/*
|
||||
* Assumes both content and output color spaces are sRGB SDR.
|
||||
* This defines the blending space as optical sRGB SDR.
|
||||
*/
|
||||
.type = CMLCMS_TYPE_EOTF_sRGB,
|
||||
};
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
|
||||
.input_profile = get_cprof_or_stock_sRGB(cm, NULL /* TODO: surface->color_profile */),
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
surface, output);
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
return false;
|
||||
|
||||
surf_xform->transform = &xform->base;
|
||||
surf_xform->identity_pipeline = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
/*
|
||||
* Assumes blending space is optical sRGB SDR and
|
||||
* output color space is sRGB SDR.
|
||||
* When we introduce LCMS plug-in we can precisely answer this question
|
||||
* by examining the color pipeline using precision parameters. For now
|
||||
* we just compare if it is same pointer or not.
|
||||
*/
|
||||
.type = CMLCMS_TYPE_EOTF_sRGB_INV,
|
||||
};
|
||||
if (xform->search_key.input_profile == xform->search_key.output_profile)
|
||||
surf_xform->identity_pipeline = true;
|
||||
else
|
||||
surf_xform->identity_pipeline = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
|
||||
.input_profile = NULL,
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
NULL, output);
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
@@ -99,44 +129,200 @@ cmlcms_get_output_color_transform(struct weston_color_manager *cm_base,
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
|
||||
cmlcms_get_sRGB_to_output_color_transform(struct weston_color_manager_lcms *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
/* Assumes output color space is sRGB SDR */
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
|
||||
.input_profile = cm->sRGB_profile,
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
NULL, output);
|
||||
|
||||
/*
|
||||
* Create a color transformation when output profile is not stock
|
||||
* sRGB profile.
|
||||
*/
|
||||
if (param.output_profile != cm->sRGB_profile) {
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
return false;
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
*xform_out = &xform->base;
|
||||
} else {
|
||||
*xform_out = NULL; /* Identity transform */
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
|
||||
cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager_lcms *cm,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
struct cmlcms_color_transform *xform;
|
||||
|
||||
/* TODO: take weston_output::eotf_mode into account */
|
||||
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
.category = CMLCMS_CATEGORY_INPUT_TO_BLEND,
|
||||
.input_profile = cm->sRGB_profile,
|
||||
.output_profile = get_cprof_or_stock_sRGB(cm, output->color_profile),
|
||||
};
|
||||
param.intent_output = cmlcms_get_render_intent(param.category,
|
||||
NULL, output);
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
return false;
|
||||
|
||||
*xform_out = &xform->base;
|
||||
return true;
|
||||
}
|
||||
|
||||
static float
|
||||
meta_clamp(float value, const char *valname, float min, float max,
|
||||
struct weston_output *output)
|
||||
{
|
||||
float ret = value;
|
||||
|
||||
/* Paranoia against NaN */
|
||||
if (!(ret >= min))
|
||||
ret = min;
|
||||
|
||||
if (!(ret <= max))
|
||||
ret = max;
|
||||
|
||||
if (ret != value) {
|
||||
weston_log("output '%s' clamping %s value from %f to %f.\n",
|
||||
output->name, valname, value, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmlcms_get_hdr_meta(struct weston_output *output,
|
||||
struct weston_hdr_metadata_type1 *hdr_meta)
|
||||
{
|
||||
const struct weston_color_characteristics *cc;
|
||||
|
||||
hdr_meta->group_mask = 0;
|
||||
|
||||
/* Only SMPTE ST 2084 mode uses HDR Static Metadata Type 1 */
|
||||
if (weston_output_get_eotf_mode(output) != WESTON_EOTF_MODE_ST2084)
|
||||
return true;
|
||||
|
||||
/* ICC profile overrides color characteristics */
|
||||
if (output->color_profile) {
|
||||
/*
|
||||
* TODO: extract characteristics from profile?
|
||||
* Get dynamic range from weston_color_characteristics?
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cc = weston_output_get_color_characteristics(output);
|
||||
|
||||
/* Target content chromaticity */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_PRIMARIES) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
hdr_meta->primary[i].x = meta_clamp(cc->primary[i].x,
|
||||
"primary", 0.0, 1.0,
|
||||
output);
|
||||
hdr_meta->primary[i].y = meta_clamp(cc->primary[i].y,
|
||||
"primary", 0.0, 1.0,
|
||||
output);
|
||||
}
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_PRIMARIES;
|
||||
}
|
||||
|
||||
/* Target content white point */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_WHITE) {
|
||||
hdr_meta->white.x = meta_clamp(cc->white.x, "white",
|
||||
0.0, 1.0, output);
|
||||
hdr_meta->white.y = meta_clamp(cc->white.y, "white",
|
||||
0.0, 1.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_WHITE;
|
||||
}
|
||||
|
||||
/* Target content peak and max mastering luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXL) {
|
||||
hdr_meta->maxDML = meta_clamp(cc->max_luminance, "maxDML",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->maxCLL = meta_clamp(cc->max_luminance, "maxCLL",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXDML;
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXCLL;
|
||||
}
|
||||
|
||||
/* Target content min mastering luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MINL) {
|
||||
hdr_meta->minDML = meta_clamp(cc->min_luminance, "minDML",
|
||||
0.0001, 6.5535, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MINDML;
|
||||
}
|
||||
|
||||
/* Target content max frame-average luminance */
|
||||
if (cc->group_mask & WESTON_COLOR_CHARACTERISTICS_GROUP_MAXFALL) {
|
||||
hdr_meta->maxFALL = meta_clamp(cc->maxFALL, "maxFALL",
|
||||
1.0, 65535.0, output);
|
||||
hdr_meta->group_mask |= WESTON_HDR_METADATA_TYPE1_GROUP_MAXFALL;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct weston_output_color_outcome *
|
||||
cmlcms_create_output_color_outcome(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
struct cmlcms_color_transform_search_param param = {
|
||||
/* Assumes blending space is optical sRGB SDR */
|
||||
.type = CMLCMS_TYPE_EOTF_sRGB,
|
||||
};
|
||||
struct cmlcms_color_transform *xform;
|
||||
struct weston_output_color_outcome *co;
|
||||
|
||||
/* TODO: use output color profile */
|
||||
if (output->color_profile)
|
||||
return false;
|
||||
co = zalloc(sizeof *co);
|
||||
if (!co)
|
||||
return NULL;
|
||||
|
||||
xform = cmlcms_color_transform_get(cm, ¶m);
|
||||
if (!xform)
|
||||
return false;
|
||||
if (!cmlcms_get_hdr_meta(output, &co->hdr_meta))
|
||||
goto out_fail;
|
||||
|
||||
*xform_out = &xform->base;
|
||||
return true;
|
||||
/*
|
||||
* TODO: if output->color_profile is NULL, maybe manufacture a
|
||||
* profile from weston_color_characteristics if it has enough
|
||||
* information?
|
||||
* Or let the frontend decide to call a "create a profile from
|
||||
* characteristics" API?
|
||||
*/
|
||||
|
||||
/* TODO: take container color space into account */
|
||||
|
||||
if (!cmlcms_get_blend_to_output_color_transform(cm, output,
|
||||
&co->from_blend_to_output))
|
||||
goto out_fail;
|
||||
|
||||
if (!cmlcms_get_sRGB_to_blend_color_transform(cm, output,
|
||||
&co->from_sRGB_to_blend))
|
||||
goto out_fail;
|
||||
|
||||
if (!cmlcms_get_sRGB_to_output_color_transform(cm, output,
|
||||
&co->from_sRGB_to_output))
|
||||
goto out_fail;
|
||||
|
||||
return co;
|
||||
|
||||
out_fail:
|
||||
weston_output_color_outcome_destroy(&co);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -165,6 +351,10 @@ cmlcms_init(struct weston_color_manager *cm_base)
|
||||
|
||||
cmsSetLogErrorHandlerTHR(cm->lcms_ctx, lcms_error_logger);
|
||||
|
||||
if (!cmlcms_create_stock_profile(cm)) {
|
||||
weston_log("color-lcms: error: cmlcms_create_stock_profile failed\n");
|
||||
return false;
|
||||
}
|
||||
weston_log("LittleCMS %d initialized.\n", cmsGetEncodedCMMversion());
|
||||
|
||||
return true;
|
||||
@@ -175,6 +365,8 @@ cmlcms_destroy(struct weston_color_manager *cm_base)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(cm_base);
|
||||
|
||||
if (cm->sRGB_profile)
|
||||
cmlcms_color_profile_destroy(cm->sRGB_profile);
|
||||
assert(wl_list_empty(&cm->color_transform_list));
|
||||
assert(wl_list_empty(&cm->color_profile_list));
|
||||
|
||||
@@ -199,13 +391,8 @@ weston_color_manager_create(struct weston_compositor *compositor)
|
||||
cm->base.destroy_color_profile = cmlcms_destroy_color_profile;
|
||||
cm->base.get_color_profile_from_icc = cmlcms_get_color_profile_from_icc;
|
||||
cm->base.destroy_color_transform = cmlcms_destroy_color_transform;
|
||||
cm->base.get_surface_color_transform =
|
||||
cmlcms_get_surface_color_transform;
|
||||
cm->base.get_output_color_transform = cmlcms_get_output_color_transform;
|
||||
cm->base.get_sRGB_to_output_color_transform =
|
||||
cmlcms_get_sRGB_to_output_color_transform;
|
||||
cm->base.get_sRGB_to_blend_color_transform =
|
||||
cmlcms_get_sRGB_to_blend_color_transform;
|
||||
cm->base.get_surface_color_transform = cmlcms_get_surface_color_transform;
|
||||
cm->base.create_output_color_outcome = cmlcms_create_output_color_outcome;
|
||||
|
||||
wl_list_init(&cm->color_transform_list);
|
||||
wl_list_init(&cm->color_profile_list);
|
||||
|
||||
@@ -39,6 +39,7 @@ struct weston_color_manager_lcms {
|
||||
|
||||
struct wl_list color_transform_list; /* cmlcms_color_transform::link */
|
||||
struct wl_list color_profile_list; /* cmlcms_color_profile::link */
|
||||
struct cmlcms_color_profile *sRGB_profile; /* stock profile */
|
||||
};
|
||||
|
||||
static inline struct weston_color_manager_lcms *
|
||||
@@ -59,6 +60,58 @@ struct cmlcms_color_profile {
|
||||
|
||||
cmsHPROFILE profile;
|
||||
struct cmlcms_md5_sum md5sum;
|
||||
|
||||
/**
|
||||
* If the profile does support being an output profile and it is used as an
|
||||
* output then this field represents a light linearizing transfer function
|
||||
* and it can not be null. The field is null only if the profile is not
|
||||
* usable as an output profile. The field is set when cmlcms_color_profile
|
||||
* is created.
|
||||
*/
|
||||
cmsToneCurve *output_eotf[3];
|
||||
|
||||
/**
|
||||
* If the profile does support being an output profile and it is used as an
|
||||
* output then this field represents a concatenation of inverse EOTF + VCGT,
|
||||
* if the tag exists and it can not be null.
|
||||
* VCGT is part of monitor calibration which means: even though we must
|
||||
* apply VCGT in the compositor, we pretend that it happens inside the
|
||||
* monitor. This is how the classic color management and ICC profiles work.
|
||||
* The ICC profile (ignoring the VCGT tag) characterizes the output which
|
||||
* is VCGT + monitor behavior. The field is null only if the profile is not
|
||||
* usable as an output profile. The field is set when cmlcms_color_profile
|
||||
* is created.
|
||||
*/
|
||||
cmsToneCurve *output_inv_eotf_vcgt[3];
|
||||
|
||||
/**
|
||||
* VCGT tag cached from output profile, it could be null if not exist
|
||||
*/
|
||||
cmsToneCurve *vcgt[3];
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of LCMS transforms
|
||||
*/
|
||||
enum cmlcms_category {
|
||||
/**
|
||||
* Uses combination of input profile with output profile, but
|
||||
* without INV EOTF or with additional EOTF in the transform pipeline
|
||||
* input→blend = input profile + output profile + output EOTF
|
||||
*/
|
||||
CMLCMS_CATEGORY_INPUT_TO_BLEND = 0,
|
||||
|
||||
/**
|
||||
* Uses INV EOTF only concatenated with VCGT tag if present
|
||||
* blend→output = output inverse EOTF + VCGT
|
||||
*/
|
||||
CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
|
||||
|
||||
/**
|
||||
* Transform uses input profile and output profile as is
|
||||
* input→output = input profile + output profile + VCGT
|
||||
*/
|
||||
CMLCMS_CATEGORY_INPUT_TO_OUTPUT,
|
||||
};
|
||||
|
||||
static inline struct cmlcms_color_profile *
|
||||
@@ -78,18 +131,12 @@ cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm,
|
||||
void
|
||||
cmlcms_destroy_color_profile(struct weston_color_profile *cprof_base);
|
||||
|
||||
/*
|
||||
* Perhaps a placeholder, until we get actual color spaces involved and
|
||||
* see how this would work better.
|
||||
*/
|
||||
enum cmlcms_color_transform_type {
|
||||
CMLCMS_TYPE_EOTF_sRGB = 0,
|
||||
CMLCMS_TYPE_EOTF_sRGB_INV,
|
||||
CMLCMS_TYPE__END,
|
||||
};
|
||||
|
||||
struct cmlcms_color_transform_search_param {
|
||||
enum cmlcms_color_transform_type type;
|
||||
enum cmlcms_category category;
|
||||
struct cmlcms_color_profile *input_profile;
|
||||
struct cmlcms_color_profile *output_profile;
|
||||
cmsUInt32Number intent_output; /* selected intent from output profile */
|
||||
};
|
||||
|
||||
struct cmlcms_color_transform {
|
||||
@@ -100,8 +147,15 @@ struct cmlcms_color_transform {
|
||||
|
||||
struct cmlcms_color_transform_search_param search_key;
|
||||
|
||||
/* for EOTF types */
|
||||
cmsToneCurve *curve;
|
||||
/**
|
||||
* 3D LUT color mapping part of the transformation, if needed.
|
||||
* For category CMLCMS_CATEGORY_INPUT_TO_OUTPUT it includes pre-curve and
|
||||
* post-curve.
|
||||
* For category CMLCMS_CATEGORY_INPUT_TO_BLEND it includes pre-curve.
|
||||
* For category CMLCMS_CATEGORY_BLEND_TO_OUTPUT and when identity it is
|
||||
* not used
|
||||
*/
|
||||
cmsHTRANSFORM cmap_3dlut;
|
||||
};
|
||||
|
||||
static inline struct cmlcms_color_transform *
|
||||
@@ -117,4 +171,27 @@ cmlcms_color_transform_get(struct weston_color_manager_lcms *cm,
|
||||
void
|
||||
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform);
|
||||
|
||||
struct cmlcms_color_profile *
|
||||
ref_cprof(struct cmlcms_color_profile *cprof);
|
||||
|
||||
void
|
||||
unref_cprof(struct cmlcms_color_profile *cprof);
|
||||
|
||||
bool
|
||||
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm);
|
||||
|
||||
void
|
||||
cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof);
|
||||
|
||||
bool
|
||||
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
|
||||
cmsHPROFILE hProfile,
|
||||
cmsToneCurve *output_eotf[3],
|
||||
cmsToneCurve *output_inv_eotf_vcgt[3],
|
||||
cmsToneCurve *vcgt[3],
|
||||
unsigned int num_points);
|
||||
|
||||
unsigned int
|
||||
cmlcms_reasonable_1D_points(void);
|
||||
|
||||
#endif /* WESTON_COLOR_LCMS_H */
|
||||
|
||||
@@ -36,6 +36,223 @@
|
||||
#include "shared/helpers.h"
|
||||
#include "shared/string-helpers.h"
|
||||
|
||||
struct xyz_arr_flt {
|
||||
float v[3];
|
||||
};
|
||||
|
||||
static double
|
||||
xyz_dot_prod(const struct xyz_arr_flt a, const struct xyz_arr_flt b)
|
||||
{
|
||||
return (double)a.v[0] * b.v[0] +
|
||||
(double)a.v[1] * b.v[1] +
|
||||
(double)a.v[2] * b.v[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Graeme sketched a linearization method there:
|
||||
* https://lists.freedesktop.org/archives/wayland-devel/2019-March/040171.html
|
||||
*/
|
||||
static bool
|
||||
build_eotf_from_clut_profile(cmsContext lcms_ctx,
|
||||
cmsHPROFILE profile,
|
||||
cmsToneCurve *output_eotf[3],
|
||||
int num_points)
|
||||
{
|
||||
int ch, point;
|
||||
float *curve_array[3];
|
||||
float *red = NULL;
|
||||
cmsHPROFILE xyz_profile = NULL;
|
||||
cmsHTRANSFORM transform_rgb_to_xyz = NULL;
|
||||
bool ret = false;
|
||||
const float div = num_points - 1;
|
||||
|
||||
red = malloc(sizeof(float) * num_points * 3);
|
||||
if (!red)
|
||||
goto release;
|
||||
|
||||
curve_array[0] = red;
|
||||
curve_array[1] = red + num_points;
|
||||
curve_array[2] = red + 2 * num_points;
|
||||
|
||||
xyz_profile = cmsCreateXYZProfileTHR(lcms_ctx);
|
||||
if (!xyz_profile)
|
||||
goto release;
|
||||
|
||||
transform_rgb_to_xyz = cmsCreateTransformTHR(lcms_ctx, profile,
|
||||
TYPE_RGB_FLT, xyz_profile,
|
||||
TYPE_XYZ_FLT,
|
||||
INTENT_ABSOLUTE_COLORIMETRIC,
|
||||
0);
|
||||
if (!transform_rgb_to_xyz)
|
||||
goto release;
|
||||
|
||||
for (ch = 0; ch < 3; ch++) {
|
||||
struct xyz_arr_flt prim_xyz_max;
|
||||
struct xyz_arr_flt prim_xyz;
|
||||
double xyz_square_magnitude;
|
||||
float rgb[3] = { 0.0f, 0.0f, 0.0f };
|
||||
|
||||
rgb[ch] = 1.0f;
|
||||
cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz_max.v, 1);
|
||||
|
||||
/**
|
||||
* Calculate xyz square of magnitude uses single channel 100% and
|
||||
* others are zero.
|
||||
*/
|
||||
xyz_square_magnitude = xyz_dot_prod(prim_xyz_max, prim_xyz_max);
|
||||
/**
|
||||
* Build rgb tone curves
|
||||
*/
|
||||
for (point = 0; point < num_points; point++) {
|
||||
rgb[ch] = (float)point / div;
|
||||
cmsDoTransform(transform_rgb_to_xyz, rgb, prim_xyz.v, 1);
|
||||
curve_array[ch][point] = xyz_dot_prod(prim_xyz,
|
||||
prim_xyz_max) /
|
||||
xyz_square_magnitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create LCMS object of rgb tone curves and validate whether
|
||||
* monotonic
|
||||
*/
|
||||
output_eotf[ch] = cmsBuildTabulatedToneCurveFloat(lcms_ctx,
|
||||
num_points,
|
||||
curve_array[ch]);
|
||||
if (!output_eotf[ch])
|
||||
goto release;
|
||||
if (!cmsIsToneCurveMonotonic(output_eotf[ch])) {
|
||||
/**
|
||||
* It is interesting to see how this profile was created.
|
||||
* We assume that such a curve could not be used for linearization
|
||||
* of arbitrary profile.
|
||||
*/
|
||||
goto release;
|
||||
}
|
||||
}
|
||||
ret = true;
|
||||
|
||||
release:
|
||||
if (transform_rgb_to_xyz)
|
||||
cmsDeleteTransform(transform_rgb_to_xyz);
|
||||
if (xyz_profile)
|
||||
cmsCloseProfile(xyz_profile);
|
||||
free(red);
|
||||
if (ret == false)
|
||||
cmsFreeToneCurveTriple(output_eotf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenation of two monotonic tone curves.
|
||||
* LCMS API cmsJoinToneCurve does y = Y^-1(X(t)),
|
||||
* but want to have y = Y^(X(t))
|
||||
*/
|
||||
static cmsToneCurve *
|
||||
lcmsJoinToneCurve(cmsContext context_id, const cmsToneCurve *X,
|
||||
const cmsToneCurve *Y, unsigned int resulting_points)
|
||||
{
|
||||
cmsToneCurve *out = NULL;
|
||||
float t, x;
|
||||
float *res = NULL;
|
||||
unsigned int i;
|
||||
|
||||
res = zalloc(resulting_points * sizeof(float));
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < resulting_points; i++) {
|
||||
t = (float)i / (resulting_points - 1);
|
||||
x = cmsEvalToneCurveFloat(X, t);
|
||||
res[i] = cmsEvalToneCurveFloat(Y, x);
|
||||
}
|
||||
|
||||
out = cmsBuildTabulatedToneCurveFloat(context_id, resulting_points, res);
|
||||
|
||||
error:
|
||||
if (res != NULL)
|
||||
free(res);
|
||||
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Extract EOTF from matrix-shaper and cLUT profiles,
|
||||
* then invert and concatenate with 'vcgt' curve if it
|
||||
* is available.
|
||||
*/
|
||||
bool
|
||||
retrieve_eotf_and_output_inv_eotf(cmsContext lcms_ctx,
|
||||
cmsHPROFILE hProfile,
|
||||
cmsToneCurve *output_eotf[3],
|
||||
cmsToneCurve *output_inv_eotf_vcgt[3],
|
||||
cmsToneCurve *vcgt[3],
|
||||
unsigned int num_points)
|
||||
{
|
||||
cmsToneCurve *curve = NULL;
|
||||
const cmsToneCurve * const *vcgt_curves;
|
||||
unsigned i;
|
||||
cmsTagSignature tags[] = {
|
||||
cmsSigRedTRCTag, cmsSigGreenTRCTag, cmsSigBlueTRCTag
|
||||
};
|
||||
|
||||
if (cmsIsMatrixShaper(hProfile)) {
|
||||
/**
|
||||
* Optimization for matrix-shaper profile
|
||||
* May have 1DLUT->3x3->3x3->1DLUT, 1DLUT->3x3->1DLUT
|
||||
*/
|
||||
for (i = 0 ; i < 3; i++) {
|
||||
curve = cmsReadTag(hProfile, tags[i]);
|
||||
if (!curve)
|
||||
goto fail;
|
||||
output_eotf[i] = cmsDupToneCurve(curve);
|
||||
if (!output_eotf[i])
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Linearization of cLUT profile may have 1DLUT->3DLUT->1DLUT,
|
||||
* 1DLUT->3DLUT, 3DLUT
|
||||
*/
|
||||
if (!build_eotf_from_clut_profile(lcms_ctx, hProfile,
|
||||
output_eotf, num_points))
|
||||
goto fail;
|
||||
}
|
||||
/**
|
||||
* If the caller looking for eotf only then return early.
|
||||
* It could be used for input profile when identity case: EOTF + INV_EOTF
|
||||
* in pipeline only.
|
||||
*/
|
||||
if (output_inv_eotf_vcgt == NULL)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
curve = cmsReverseToneCurve(output_eotf[i]);
|
||||
if (!curve)
|
||||
goto fail;
|
||||
output_inv_eotf_vcgt[i] = curve;
|
||||
}
|
||||
vcgt_curves = cmsReadTag(hProfile, cmsSigVcgtTag);
|
||||
if (vcgt_curves && vcgt_curves[0] && vcgt_curves[1] && vcgt_curves[2]) {
|
||||
for (i = 0; i < 3; i++) {
|
||||
curve = lcmsJoinToneCurve(lcms_ctx,
|
||||
output_inv_eotf_vcgt[i],
|
||||
vcgt_curves[i], num_points);
|
||||
if (!curve)
|
||||
goto fail;
|
||||
cmsFreeToneCurve(output_inv_eotf_vcgt[i]);
|
||||
output_inv_eotf_vcgt[i] = curve;
|
||||
if (vcgt)
|
||||
vcgt[i] = cmsDupToneCurve(vcgt_curves[i]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
fail:
|
||||
cmsFreeToneCurveTriple(output_eotf);
|
||||
cmsFreeToneCurveTriple(output_inv_eotf_vcgt);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* FIXME: sync with spec! */
|
||||
static bool
|
||||
validate_icc_profile(cmsHPROFILE profile, char **errmsg)
|
||||
@@ -102,15 +319,37 @@ cmlcms_color_profile_create(struct weston_color_manager_lcms *cm,
|
||||
return cprof;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
cmlcms_color_profile_destroy(struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
wl_list_remove(&cprof->link);
|
||||
cmsFreeToneCurveTriple(cprof->vcgt);
|
||||
cmsFreeToneCurveTriple(cprof->output_eotf);
|
||||
cmsFreeToneCurveTriple(cprof->output_inv_eotf_vcgt);
|
||||
cmsCloseProfile(cprof->profile);
|
||||
free(cprof->base.description);
|
||||
free(cprof);
|
||||
}
|
||||
|
||||
struct cmlcms_color_profile *
|
||||
ref_cprof(struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
if (!cprof)
|
||||
return NULL;
|
||||
|
||||
weston_color_profile_ref(&cprof->base);
|
||||
return cprof;
|
||||
}
|
||||
|
||||
void
|
||||
unref_cprof(struct cmlcms_color_profile *cprof)
|
||||
{
|
||||
if (!cprof)
|
||||
return;
|
||||
|
||||
weston_color_profile_unref(&cprof->base);
|
||||
}
|
||||
|
||||
static char *
|
||||
make_icc_file_description(cmsHPROFILE profile,
|
||||
const struct cmlcms_md5_sum *md5sum,
|
||||
@@ -131,6 +370,52 @@ make_icc_file_description(cmsHPROFILE profile,
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Build stock profile which available for clients unaware of color management
|
||||
*/
|
||||
bool
|
||||
cmlcms_create_stock_profile(struct weston_color_manager_lcms *cm)
|
||||
{
|
||||
cmsHPROFILE profile;
|
||||
struct cmlcms_md5_sum md5sum;
|
||||
char *desc = NULL;
|
||||
|
||||
profile = cmsCreate_sRGBProfileTHR(cm->lcms_ctx);
|
||||
if (!profile) {
|
||||
weston_log("color-lcms: error: cmsCreate_sRGBProfileTHR failed\n");
|
||||
return false;
|
||||
}
|
||||
if (!cmsMD5computeID(profile)) {
|
||||
weston_log("Failed to compute MD5 for ICC profile\n");
|
||||
goto err_close;
|
||||
}
|
||||
|
||||
cmsGetHeaderProfileID(profile, md5sum.bytes);
|
||||
desc = make_icc_file_description(profile, &md5sum, "sRGB stock");
|
||||
if (!desc)
|
||||
goto err_close;
|
||||
|
||||
cm->sRGB_profile = cmlcms_color_profile_create(cm, profile, desc, NULL);
|
||||
if (!cm->sRGB_profile)
|
||||
goto err_close;
|
||||
|
||||
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
|
||||
cm->sRGB_profile->profile,
|
||||
cm->sRGB_profile->output_eotf,
|
||||
cm->sRGB_profile->output_inv_eotf_vcgt,
|
||||
cm->sRGB_profile->vcgt,
|
||||
cmlcms_reasonable_1D_points()))
|
||||
goto err_close;
|
||||
|
||||
return true;
|
||||
|
||||
err_close:
|
||||
free(desc);
|
||||
cmsCloseProfile(profile);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
cmlcms_get_color_profile_from_icc(struct weston_color_manager *cm_base,
|
||||
const void *icc_data,
|
||||
|
||||
@@ -33,48 +33,98 @@
|
||||
#include "color-lcms.h"
|
||||
#include "shared/helpers.h"
|
||||
|
||||
/* Arguments to cmsBuildParametricToneCurve() */
|
||||
struct tone_curve_def {
|
||||
cmsInt32Number cmstype;
|
||||
cmsFloat64Number params[5];
|
||||
};
|
||||
|
||||
/*
|
||||
* LCMS uses the required number of 'params' based on 'cmstype', the parametric
|
||||
* tone curve number. LCMS honors negative 'cmstype' as inverse function.
|
||||
* These are LCMS built-in parametric tone curves.
|
||||
/**
|
||||
* The method is used in linearization of an arbitrary color profile
|
||||
* when EOTF is retrieved we want to know a generic way to decide the number
|
||||
* of points
|
||||
*/
|
||||
static const struct tone_curve_def predefined_eotf_curves[] = {
|
||||
[CMLCMS_TYPE_EOTF_sRGB] = {
|
||||
.cmstype = 4,
|
||||
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
|
||||
},
|
||||
[CMLCMS_TYPE_EOTF_sRGB_INV] = {
|
||||
.cmstype = -4,
|
||||
.params = { 2.4, 1. / 1.055, 0.055 / 1.055, 1. / 12.92, 0.04045 },
|
||||
},
|
||||
};
|
||||
unsigned int
|
||||
cmlcms_reasonable_1D_points(void)
|
||||
{
|
||||
return 1024;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
cmlcms_reasonable_3D_points(void)
|
||||
{
|
||||
return 33;
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_fill_in_tone_curve(struct weston_color_transform *xform_base,
|
||||
float *values, unsigned len)
|
||||
fill_in_curves(cmsToneCurve *curves[3], float *values, unsigned len)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
||||
float *R_lut = values;
|
||||
float *G_lut = R_lut + len;
|
||||
float *B_lut = G_lut + len;
|
||||
unsigned i;
|
||||
cmsFloat32Number x, y;
|
||||
|
||||
assert(xform->curve != NULL);
|
||||
assert(len > 1);
|
||||
cmsFloat32Number x;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
x = (double)i / (len - 1);
|
||||
y = cmsEvalToneCurveFloat(xform->curve, x);
|
||||
R_lut[i] = y;
|
||||
G_lut[i] = y;
|
||||
B_lut[i] = y;
|
||||
R_lut[i] = cmsEvalToneCurveFloat(curves[0], x);
|
||||
G_lut[i] = cmsEvalToneCurveFloat(curves[1], x);
|
||||
B_lut[i] = cmsEvalToneCurveFloat(curves[2], x);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_fill_in_pre_curve(struct weston_color_transform *xform_base,
|
||||
float *values, unsigned len)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
||||
|
||||
assert(xform->search_key.category == CMLCMS_CATEGORY_BLEND_TO_OUTPUT);
|
||||
|
||||
assert(len > 1);
|
||||
|
||||
fill_in_curves(xform->search_key.output_profile->output_inv_eotf_vcgt,
|
||||
values, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp value to [0.0, 1.0], except pass NaN through.
|
||||
*
|
||||
* This function is not intended for hiding NaN.
|
||||
*/
|
||||
static float
|
||||
ensure_unorm(float v)
|
||||
{
|
||||
if (v <= 0.0f)
|
||||
return 0.0f;
|
||||
if (v > 1.0f)
|
||||
return 1.0f;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void
|
||||
cmlcms_fill_in_3dlut(struct weston_color_transform *xform_base,
|
||||
float *lut, unsigned int len)
|
||||
{
|
||||
struct cmlcms_color_transform *xform = get_xform(xform_base);
|
||||
float rgb_in[3];
|
||||
float rgb_out[3];
|
||||
unsigned int index;
|
||||
unsigned int value_b, value_r, value_g;
|
||||
float divider = len - 1;
|
||||
|
||||
assert(xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_BLEND ||
|
||||
xform->search_key.category == CMLCMS_CATEGORY_INPUT_TO_OUTPUT);
|
||||
|
||||
for (value_b = 0; value_b < len; value_b++) {
|
||||
for (value_g = 0; value_g < len; value_g++) {
|
||||
for (value_r = 0; value_r < len; value_r++) {
|
||||
rgb_in[0] = (float)value_r / divider;
|
||||
rgb_in[1] = (float)value_g / divider;
|
||||
rgb_in[2] = (float)value_b / divider;
|
||||
|
||||
cmsDoTransform(xform->cmap_3dlut, rgb_in, rgb_out, 1);
|
||||
|
||||
index = 3 * (value_r + len * (value_g + len * value_b));
|
||||
lut[index ] = ensure_unorm(rgb_out[0]);
|
||||
lut[index + 1] = ensure_unorm(rgb_out[1]);
|
||||
lut[index + 2] = ensure_unorm(rgb_out[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,55 +132,135 @@ void
|
||||
cmlcms_color_transform_destroy(struct cmlcms_color_transform *xform)
|
||||
{
|
||||
wl_list_remove(&xform->link);
|
||||
if (xform->curve)
|
||||
cmsFreeToneCurve(xform->curve);
|
||||
|
||||
if (xform->cmap_3dlut)
|
||||
cmsDeleteTransform(xform->cmap_3dlut);
|
||||
|
||||
unref_cprof(xform->search_key.input_profile);
|
||||
unref_cprof(xform->search_key.output_profile);
|
||||
free(xform);
|
||||
}
|
||||
|
||||
static bool
|
||||
xform_set_cmap_3dlut(struct cmlcms_color_transform *xform,
|
||||
cmsHPROFILE input_profile,
|
||||
cmsHPROFILE output_profile,
|
||||
cmsToneCurve *curves[3],
|
||||
cmsUInt32Number intent)
|
||||
{
|
||||
struct weston_color_manager_lcms *cm = get_cmlcms(xform->base.cm);
|
||||
cmsHPROFILE arr_prof[3] = { input_profile, output_profile, NULL };
|
||||
int num_profiles = 2;
|
||||
|
||||
if (curves[0]) {
|
||||
arr_prof[2] = cmsCreateLinearizationDeviceLinkTHR(cm->lcms_ctx,
|
||||
cmsSigRgbData,
|
||||
curves);
|
||||
if (!arr_prof[2])
|
||||
return false;
|
||||
|
||||
num_profiles = 3;
|
||||
}
|
||||
|
||||
xform->cmap_3dlut = cmsCreateMultiprofileTransformTHR(cm->lcms_ctx,
|
||||
arr_prof,
|
||||
num_profiles,
|
||||
TYPE_RGB_FLT,
|
||||
TYPE_RGB_FLT,
|
||||
intent,
|
||||
0);
|
||||
if (!xform->cmap_3dlut) {
|
||||
cmsCloseProfile(arr_prof[2]);
|
||||
weston_log("color-lcms error: fail cmsCreateMultiprofileTransformTHR.\n");
|
||||
return false;
|
||||
}
|
||||
xform->base.mapping.type = WESTON_COLOR_MAPPING_TYPE_3D_LUT;
|
||||
xform->base.mapping.u.lut3d.fill_in = cmlcms_fill_in_3dlut;
|
||||
xform->base.mapping.u.lut3d.optimal_len =
|
||||
cmlcms_reasonable_3D_points();
|
||||
cmsCloseProfile(arr_prof[2]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct cmlcms_color_transform *
|
||||
cmlcms_color_transform_create(struct weston_color_manager_lcms *cm,
|
||||
const struct cmlcms_color_transform_search_param *param)
|
||||
const struct cmlcms_color_transform_search_param *search_param)
|
||||
{
|
||||
struct cmlcms_color_profile *input_profile = search_param->input_profile;
|
||||
struct cmlcms_color_profile *output_profile = search_param->output_profile;
|
||||
struct cmlcms_color_transform *xform;
|
||||
const struct tone_curve_def *tonedef;
|
||||
|
||||
if (param->type < 0 || param->type >= CMLCMS_TYPE__END) {
|
||||
weston_log("color-lcms error: bad color transform type in %s.\n",
|
||||
__func__);
|
||||
return NULL;
|
||||
}
|
||||
tonedef = &predefined_eotf_curves[param->type];
|
||||
bool ok = false;
|
||||
|
||||
xform = zalloc(sizeof *xform);
|
||||
if (!xform)
|
||||
return NULL;
|
||||
|
||||
xform->curve = cmsBuildParametricToneCurve(cm->lcms_ctx,
|
||||
tonedef->cmstype,
|
||||
tonedef->params);
|
||||
if (xform->curve == NULL) {
|
||||
weston_log("color-lcms error: failed to build parametric tone curve.\n");
|
||||
free(xform);
|
||||
return NULL;
|
||||
weston_color_transform_init(&xform->base, &cm->base);
|
||||
wl_list_init(&xform->link);
|
||||
xform->search_key = *search_param;
|
||||
xform->search_key.input_profile = ref_cprof(input_profile);
|
||||
xform->search_key.output_profile = ref_cprof(output_profile);
|
||||
|
||||
/* Ensure the linearization etc. have been extracted. */
|
||||
if (!output_profile->output_eotf[0]) {
|
||||
if (!retrieve_eotf_and_output_inv_eotf(cm->lcms_ctx,
|
||||
output_profile->profile,
|
||||
output_profile->output_eotf,
|
||||
output_profile->output_inv_eotf_vcgt,
|
||||
output_profile->vcgt,
|
||||
cmlcms_reasonable_1D_points()))
|
||||
goto error;
|
||||
}
|
||||
|
||||
weston_color_transform_init(&xform->base, &cm->base);
|
||||
xform->search_key = *param;
|
||||
switch (search_param->category) {
|
||||
case CMLCMS_CATEGORY_INPUT_TO_BLEND:
|
||||
/* Use EOTF to linearize the result. */
|
||||
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
|
||||
output_profile->profile,
|
||||
output_profile->output_eotf,
|
||||
search_param->intent_output);
|
||||
break;
|
||||
|
||||
case CMLCMS_CATEGORY_INPUT_TO_OUTPUT:
|
||||
/* Apply also VCGT if it exists. */
|
||||
ok = xform_set_cmap_3dlut(xform, input_profile->profile,
|
||||
output_profile->profile,
|
||||
output_profile->vcgt,
|
||||
search_param->intent_output);
|
||||
break;
|
||||
|
||||
case CMLCMS_CATEGORY_BLEND_TO_OUTPUT:
|
||||
xform->base.pre_curve.type = WESTON_COLOR_CURVE_TYPE_LUT_3x1D;
|
||||
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_tone_curve;
|
||||
xform->base.pre_curve.u.lut_3x1d.optimal_len = 256;
|
||||
xform->base.pre_curve.u.lut_3x1d.fill_in = cmlcms_fill_in_pre_curve;
|
||||
xform->base.pre_curve.u.lut_3x1d.optimal_len =
|
||||
cmlcms_reasonable_1D_points();
|
||||
ok = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
goto error;
|
||||
|
||||
wl_list_insert(&cm->color_transform_list, &xform->link);
|
||||
|
||||
return xform;
|
||||
|
||||
error:
|
||||
cmlcms_color_transform_destroy(xform);
|
||||
weston_log("CM cmlcms_color_transform_create failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
transform_matches_params(const struct cmlcms_color_transform *xform,
|
||||
const struct cmlcms_color_transform_search_param *param)
|
||||
{
|
||||
if (xform->search_key.type != param->type)
|
||||
if (xform->search_key.category != param->category)
|
||||
return false;
|
||||
|
||||
if (xform->search_key.intent_output != param->intent_output ||
|
||||
xform->search_key.output_profile != param->output_profile ||
|
||||
xform->search_key.input_profile != param->input_profile)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -2,7 +2,6 @@ if not get_option('color-management-lcms')
|
||||
subdir_done()
|
||||
endif
|
||||
|
||||
dep_lcms2 = dependency('lcms2', version: '>= 2.9', required: false)
|
||||
if not dep_lcms2.found()
|
||||
error('color-lcms plugin requires lcms2 which was not found. Or, you can use \'-Dcolor-management-lcms=false\'.')
|
||||
endif
|
||||
|
||||
+33
-37
@@ -35,6 +35,18 @@ struct weston_color_manager_noop {
|
||||
struct weston_color_manager base;
|
||||
};
|
||||
|
||||
static bool
|
||||
check_output_eotf_mode(struct weston_output *output)
|
||||
{
|
||||
if (output->eotf_mode == WESTON_EOTF_MODE_SDR)
|
||||
return true;
|
||||
|
||||
weston_log("Error: color manager no-op does not support EOTF mode %s of output %s.\n",
|
||||
weston_eotf_mode_to_str(output->eotf_mode),
|
||||
output->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct weston_color_manager_noop *
|
||||
get_cmnoop(struct weston_color_manager *cm_base)
|
||||
{
|
||||
@@ -74,6 +86,9 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
|
||||
/* TODO: Assert surface has no colorspace set */
|
||||
assert(output->color_profile == NULL);
|
||||
|
||||
if (!check_output_eotf_mode(output))
|
||||
return false;
|
||||
|
||||
/* Identity transform */
|
||||
surf_xform->transform = NULL;
|
||||
surf_xform->identity_pipeline = true;
|
||||
@@ -81,43 +96,29 @@ cmnoop_get_surface_color_transform(struct weston_color_manager *cm_base,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmnoop_get_output_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
static struct weston_output_color_outcome *
|
||||
cmnoop_create_output_color_outcome(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output)
|
||||
{
|
||||
struct weston_output_color_outcome *co;
|
||||
|
||||
assert(output->color_profile == NULL);
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
if (!check_output_eotf_mode(output))
|
||||
return NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
co = zalloc(sizeof *co);
|
||||
if (!co)
|
||||
return NULL;
|
||||
|
||||
static bool
|
||||
cmnoop_get_sRGB_to_output_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
assert(output->color_profile == NULL);
|
||||
/* Identity transform on everything */
|
||||
co->from_blend_to_output = NULL;
|
||||
co->from_sRGB_to_blend = NULL;
|
||||
co->from_sRGB_to_output = NULL;
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
co->hdr_meta.group_mask = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
cmnoop_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_base,
|
||||
struct weston_output *output,
|
||||
struct weston_color_transform **xform_out)
|
||||
{
|
||||
assert(output->color_profile == NULL);
|
||||
|
||||
/* Identity transform */
|
||||
*xform_out = NULL;
|
||||
|
||||
return true;
|
||||
return co;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -153,13 +154,8 @@ weston_color_manager_noop_create(struct weston_compositor *compositor)
|
||||
cm->base.destroy_color_profile = cmnoop_destroy_color_profile;
|
||||
cm->base.get_color_profile_from_icc = cmnoop_get_color_profile_from_icc;
|
||||
cm->base.destroy_color_transform = cmnoop_destroy_color_transform;
|
||||
cm->base.get_surface_color_transform =
|
||||
cmnoop_get_surface_color_transform;
|
||||
cm->base.get_output_color_transform = cmnoop_get_output_color_transform;
|
||||
cm->base.get_sRGB_to_output_color_transform =
|
||||
cmnoop_get_sRGB_to_output_color_transform;
|
||||
cm->base.get_sRGB_to_blend_color_transform =
|
||||
cmnoop_get_sRGB_to_blend_color_transform;
|
||||
cm->base.get_surface_color_transform = cmnoop_get_surface_color_transform;
|
||||
cm->base.create_output_color_outcome = cmnoop_create_output_color_outcome;
|
||||
|
||||
return &cm->base;
|
||||
}
|
||||
|
||||
@@ -297,3 +297,55 @@ out_close:
|
||||
close(fd);
|
||||
return cprof;
|
||||
}
|
||||
|
||||
/** Get a string naming the EOTF mode
|
||||
*
|
||||
* \internal
|
||||
*/
|
||||
WL_EXPORT const char *
|
||||
weston_eotf_mode_to_str(enum weston_eotf_mode e)
|
||||
{
|
||||
switch (e) {
|
||||
case WESTON_EOTF_MODE_NONE: return "(none)";
|
||||
case WESTON_EOTF_MODE_SDR: return "SDR";
|
||||
case WESTON_EOTF_MODE_TRADITIONAL_HDR: return "traditional gamma HDR";
|
||||
case WESTON_EOTF_MODE_ST2084: return "ST2084";
|
||||
case WESTON_EOTF_MODE_HLG: return "HLG";
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
/** A list of EOTF modes as a string
|
||||
*
|
||||
* \param eotf_mask Bitwise-or'd enum weston_eotf_mode values.
|
||||
* \return Comma separated names of the listed EOTF modes. Must be free()'d by
|
||||
* the caller.
|
||||
*/
|
||||
WL_EXPORT char *
|
||||
weston_eotf_mask_to_str(uint32_t eotf_mask)
|
||||
{
|
||||
FILE *fp;
|
||||
char *str = NULL;
|
||||
size_t size = 0;
|
||||
unsigned i;
|
||||
const char *sep = "";
|
||||
|
||||
fp = open_memstream(&str, &size);
|
||||
if (!fp)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; eotf_mask; i++) {
|
||||
uint32_t bitmask = 1u << i;
|
||||
|
||||
if (eotf_mask & bitmask) {
|
||||
fprintf(fp, "%s%s", sep,
|
||||
weston_eotf_mode_to_str(bitmask));
|
||||
sep = ", ";
|
||||
}
|
||||
|
||||
eotf_mask &= ~bitmask;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user