-----BEGIN PGP SIGNATURE-----
 
 iQJOBAABCAA4FiEEcDKHej6x6uPk3J379jQS5glH1u8FAmTKfCgaHG1hcml1cy52
 bGFkQGNvbGxhYm9yYS5jb20ACgkQ9jQS5glH1u+ihA//e09DRmit8rw2Gglk5N5G
 P/O4m0BukEcXZy4dEo9WEkhrJ/hGTyZ3iqUMtfdbWouEpEMR25ck8n3jTEtUj2QN
 T4Gm/Ljcr8zzmtljbjEBDfBOaE07tISa7aX+IPTg+imf/t54kg80WwuKrW4FbNz2
 azr2L+UAIiFZ4fJ5jA0uiGRiBo2gEwxylujZlt609SQiQCYND+XrFY3fWkHC8vyW
 JfR3IfrYER7KXlxkdJiFE36eIEpgYGIguVp6Uj6B35gxr0+npIrcjY38v/KJILGl
 5mbxnwblGQpLgnBRMb2JmffetK+puFrTSOzEQDSIaOTjuQ0hHX1Ocjg3NfiEoxfi
 eG25SNaqMMUE2U2+cdojP97vCscoU71rwTpuklY1MG37O50jWsjgcUpVKYwcoljA
 TBOhwIJ2jLIzs5Ax37V81Bh7zj95nGYHupHnZOcQ/+SPnjnQ5Je6RE4Me4d0JJdN
 aR/hj/mF+DU80/ySfQwN9/bFp9hbCsozjFClIKiJxnWi71wQZl2o8ZfA8oJZyG6K
 VwYkVTQMoQtTuyDdyD6lbNbbC8/uq8ZnX9qKVHuEZlHWigvGSvuDQLGExuK7Ksjy
 sQPYBdGGWoIJGbqwuJX+FQ/hJgSnY1/VBp82N4//OUYs65LW01ACxF7J8SIIp+nE
 EJhNfCTDnO1hrN5xhmhk+qs=
 =GzNE
 -----END PGP SIGNATURE-----

Merge tag '11.0.3' into dev

11.0.3
dev
Nikita Tokarchuk 4 months ago
commit 0cc7369023
  1. 9
      .gitlab-ci.yml
  2. 23
      .gitlab-ci/build-deps.sh
  3. 2
      .gitlab-ci/debian-install.sh
  4. 6
      .gitlab-ci/leak-sanitizer.supp
  5. 20
      .gitlab-ci/virtme-scripts/per-test-asan.sh
  6. 17
      .gitlab-ci/virtme-scripts/run-weston-tests.sh
  7. 4
      CONTRIBUTING.md
  8. 26
      README.md
  9. 92
      clients/desktop-shell.c
  10. 2
      clients/eventdemo.c
  11. 504
      clients/gears.c
  12. 28
      clients/meson.build
  13. 374
      clients/nested-client.c
  14. 1139
      clients/nested.c
  15. 2
      clients/presentation-shm.c
  16. 10
      clients/simple-dmabuf-egl.c
  17. 112
      clients/simple-dmabuf-feedback.c
  18. 426
      clients/simple-egl.c
  19. 6
      clients/simple-touch.c
  20. 23
      clients/terminal.c
  21. 1890
      clients/weston-info.c
  22. 409
      clients/window.c
  23. 20
      clients/window.h
  24. 616
      compositor/main.c
  25. 24
      compositor/meson.build
  26. 60
      compositor/screen-share.c
  27. 18
      compositor/text-backend.c
  28. 32
      compositor/weston-private.h
  29. 3
      compositor/weston-screenshooter.c
  30. 224
      compositor/xwayland.c
  31. 737
      desktop-shell/exposay.c
  32. 2
      desktop-shell/input-panel.c
  33. 4
      desktop-shell/meson.build
  34. 1195
      desktop-shell/shell.c
  35. 75
      desktop-shell/shell.h
  36. 299
      doc/sphinx/doxygen.ini.in
  37. 1
      doc/sphinx/index.rst
  38. 2
      doc/sphinx/meson.build
  39. BIN
      doc/sphinx/toc/images/ivi-shell.png
  40. 9
      doc/sphinx/toc/images/meson.build
  41. 111
      doc/sphinx/toc/ivi-shell.rst
  42. 2
      doc/sphinx/toc/libweston/images/create_output.msc
  43. 2
      doc/sphinx/toc/meson.build
  44. 31
      doc/sphinx/toc/running-weston.rst
  45. 17
      doc/sphinx/toc/test-suite.rst
  46. 90
      fullscreen-shell/fullscreen-shell.c
  47. 3
      fullscreen-shell/meson.build
  48. 27
      include/libweston-desktop/libweston-desktop.h
  49. 30
      include/libweston/backend-drm.h
  50. 70
      include/libweston/backend-fbdev.h
  51. 13
      include/libweston/backend-rdp.h
  52. 25
      include/libweston/config-parser.h
  53. 339
      include/libweston/libweston.h
  54. 13
      include/libweston/matrix.h
  55. 1
      include/libweston/meson.build
  56. 2
      include/libweston/weston-log.h
  57. 78
      ivi-shell/README
  58. 37
      ivi-shell/hmi-controller.c
  59. 4
      ivi-shell/ivi-layout-export.h
  60. 3
      ivi-shell/ivi-layout-shell.h
  61. 27
      ivi-shell/ivi-layout.c
  62. 14
      ivi-shell/ivi-shell.c
  63. 1
      ivi-shell/meson.build
  64. 72
      kiosk-shell/kiosk-shell.c
  65. 2
      kiosk-shell/kiosk-shell.h
  66. 5
      kiosk-shell/meson.build
  67. 36
      libweston-desktop/meson.build
  68. 497
      libweston-desktop/wl-shell.c
  69. 89
      libweston/backend-drm/drm-gbm.c
  70. 180
      libweston/backend-drm/drm-internal.h
  71. 64
      libweston/backend-drm/drm-virtual.c
  72. 687
      libweston/backend-drm/drm.c
  73. 118
      libweston/backend-drm/fb.c
  74. 178
      libweston/backend-drm/kms-color.c
  75. 295
      libweston/backend-drm/kms.c
  76. 27
      libweston/backend-drm/libbacklight.c
  77. 2
      libweston/backend-drm/meson.build
  78. 72
      libweston/backend-drm/modes.c
  79. 29
      libweston/backend-drm/state-helpers.c
  80. 662
      libweston/backend-drm/state-propose.c
  81. 998
      libweston/backend-fbdev/fbdev.c
  82. 33
      libweston/backend-fbdev/meson.build
  83. 73
      libweston/backend-headless/headless.c
  84. 22
      libweston/backend-rdp/meson.build
  85. 817
      libweston/backend-rdp/rdp.c
  86. 263
      libweston/backend-rdp/rdp.h
  87. 1754
      libweston/backend-rdp/rdpclip.c
  88. 262
      libweston/backend-rdp/rdputil.c
  89. 275
      libweston/backend-wayland/wayland.c
  90. 132
      libweston/backend-x11/x11.c
  91. 24
      libweston/backend.h
  92. 4
      libweston/bindings.c
  93. 2
      libweston/clipboard.c
  94. 277
      libweston/color-lcms/color-lcms.c
  95. 101
      libweston/color-lcms/color-lcms.h
  96. 287
      libweston/color-lcms/color-profile.c
  97. 240
      libweston/color-lcms/color-transform.c
  98. 1
      libweston/color-lcms/meson.build
  99. 68
      libweston/color-noop.c
  100. 52
      libweston/color.c
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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

@ -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)

@ -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

@ -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;
}

@ -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,
&registry_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;
}

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);

@ -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;

@ -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

@ -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
window_close(struct window *window)
close_task_run(struct task *task, uint32_t events)
{
if (window->close_handler)
struct window *window = container_of(task, struct window, close_task);
window->close_handler(window->user_data);
else
}
static void
window_close(struct window *window)
{
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,
};

@ -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,78 +368,90 @@ sigchld_handler(int signal_number, void *data)
return 1;
}
static void
child_client_exec(int sockfd, const char *path)
{
int clientfd;
char s[32];
sigset_t allsigs;
/* do not give our signal mask to the new process */
sigfillset(&allsigs);
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
/* Launch clients as the user. Do not launch clients with wrong euid. */
if (seteuid(getuid()) == -1) {
weston_log("compositor: failed seteuid\n");
return;
}
/* 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;
}
snprintf(s, sizeof s, "%d", clientfd);
setenv("WAYLAND_SOCKET", s, 1);
if (execl(path, path, NULL) < 0)
weston_log("compositor: executing '%s' failed: %s\n",
path, strerror(errno));
}
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];
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;
struct wl_client *client;
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);
if (os_socketpair_cloexec(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
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();
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;
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);
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
/* Launch clients as the user. Do not launch clients with wrong euid. */
if (seteuid(getuid()) == -1) {
written = write(STDERR_FILENO, fail_seteuid,
strlen(fail_seteuid));
_exit(EXIT_FAILURE);
}
if (pid == 0) {
child_client_exec(sv[1], path);
_exit(-1);
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
if (!ret) {
written = write(STDERR_FILENO, fail_cloexec,
strlen(fail_cloexec));
_exit(EXIT_FAILURE);
}
close(sv[1]);
execve(argp[0], argp, envp);
if (fail_exec)
written = write(STDERR_FILENO, fail_exec,
strlen(fail_exec));
_exit(EXIT_FAILURE);
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",
&section_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;

@ -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')

@ -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);

@ -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);
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);
section = weston_config_get_section(config,
"xwayland", NULL, NULL);
weston_config_section_get_string(section, "path",
&xserver, XSERVER_PATH);
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);
/* 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);
execve(xserver, argp, envp);
/* execve does not return on success, so it failed */
if (exec_failure_msg) {
written = write(STDERR_FILENO, exec_failure_msg,
strlen(exec_failure_msg));
}
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(x11_wm_socket.fds[1]);
wxw->wm_fd = x11_wm_socket.fds[0];
close(wm[1]);
wxw->wm_fd = wm[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',

File diff suppressed because it is too large Load Diff

@ -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

@ -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;
surface = weston_surface_create(ec);
if (surface == NULL) {
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;
curtain = weston_curtain_create(ec, &curtain_params);
if (!curtain) {
weston_log("no memory\n");
return NULL;
}
view = weston_view_create(surface);
if (!view) {
weston_surface_destroy(surface);
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(&region, 0, 0,
surface->width,
surface->height);
wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) {
pixman_region32_union_rect(&region, &region,
subsurface->position.x,
subsurface->position.y,
subsurface->surface->width,
subsurface->surface->height);
}
box = pixman_region32_extents(&region);
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(&region);
}
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 */

@ -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

@ -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,

@ -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

@ -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: '',

@ -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;
curtain_params.x = output->x;
curtain_params.y = output->y;
curtain_params.width = output->width;
curtain_params.height = output->height;
solid_surface.get_label = kiosk_shell_background_surface_get_label;
solid_surface.surface_committed = NULL;
solid_surface.surface_private = NULL;
curtain_params.capture_input = true;
shoutput->background_view =
create_solid_color_surface(shoutput->shell->compositor,
&solid_surface,
output->x, output->y,
output->width,
output->height);
curtain_params.get_label = kiosk_shell_background_surface_get_label;
curtain_params.surface_committed = NULL;
curtain_params.surface_private = NULL;
weston_surface_set_role(shoutput->background_view->surface,
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,15 +260,8 @@ enum actions_needed_dmabuf_feedback {
ACTION_NEEDED_REMOVE_SCANOUT_TRANCHE = (1 << 1),
};
struct drm_backend {
struct weston_backend base;
struct weston_compositor *compositor;
struct udev *udev;
struct wl_event_source *drm_source;
struct udev_monitor *udev_monitor;
struct wl_event_source *udev_drm_source;
struct drm_device {
struct drm_backend *backend;
struct {
int id;
@ -265,51 +269,63 @@ struct drm_backend {
char *filename;
dev_t devnum;
} 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;
/* drm_crtc::link */
struct wl_list crtc_list;
struct wl_list plane_list;
uint32_t next_plane_idx;
void *repaint_data;
/* drm_writeback::link */
struct wl_list writeback_connector_list;
bool state_invalid;
/* drm_crtc::link */
struct wl_list crtc_list;
bool atomic_modeset;
/* drm_writeback::link */
struct wl_list writeback_connector_list;
bool aspect_ratio_supported;
int32_t cursor_width;
int32_t cursor_height;
bool sprites_are_broken;
bool cursors_are_broken;
bool sprites_are_broken;
bool atomic_modeset;
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;
struct udev *udev;
struct wl_event_source *drm_source;
struct udev_monitor *udev_monitor;
struct wl_event_source *udev_drm_source;
struct drm_device *drm;
struct gbm_device *gbm;
struct wl_listener session_listener;
uint32_t gbm_format;
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;

File diff suppressed because it is too large Load Diff

@ -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;
}

@ -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;
fmt_prev = drm_iter.fmt;
}
ret = weston_drm_format_add_modifier(fmt, mod->modifier);
ret = weston_drm_format_add_modifier(fmt, drm_iter.mod);
if (ret < 0)
goto out;
}
if (fmt->modifiers.size == 0)
weston_drm_format_array_remove_latest_format(&plane->formats);
}
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);
out:
return state;
out:
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: '',

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;
}

@ -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);

@ -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);

@ -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, &param);
if (!xform)
return false;
surf_xform->transform = &xform->base;
/*
* 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.
*/
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_output_color_transform(struct weston_color_manager *cm_base,
cmlcms_get_blend_to_output_color_transform(struct weston_color_manager_lcms *cm,
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.
*/
.type = CMLCMS_TYPE_EOTF_sRGB_INV,
};
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, &param);
if (!xform)
@ -99,37 +129,54 @@ 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)
return false;
/* TODO: take weston_output::eotf_mode into account */
/* Identity transform */
*xform_out = NULL;
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, &param);
if (!xform)
return false;
*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 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;
/* 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 = 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, &param);
if (!xform)
@ -139,6 +186,145 @@ cmlcms_get_sRGB_to_blend_color_transform(struct weston_color_manager *cm_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 weston_output_color_outcome *co;
co = zalloc(sizeof *co);
if (!co)
return NULL;
if (!cmlcms_get_hdr_meta(output, &co->hdr_meta))
goto out_fail;
/*
* 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
lcms_error_logger(cmsContext context_id,
cmsUInt32Number error_code,
@ -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
* inputblend = input profile + output profile + output EOTF
*/
CMLCMS_CATEGORY_INPUT_TO_BLEND = 0,
/**
* Uses INV EOTF only concatenated with VCGT tag if present
* blendoutput = output inverse EOTF + VCGT
*/
CMLCMS_CATEGORY_BLEND_TO_OUTPUT,
/**
* Transform uses input profile and output profile as is
* inputoutput = 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;
}
wl_list_insert(&cm->color_transform_list, &xform->link);
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

@ -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)
{
assert(output->color_profile == NULL);
struct weston_output_color_outcome *co;
/* Identity transform */
*xform_out = NULL;
return true;
}
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 */
*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_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 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;
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

Loading…
Cancel
Save