-----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 8 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. 114
      clients/simple-dmabuf-feedback.c
  18. 426
      clients/simple-egl.c
  19. 6
      clients/simple-touch.c
  20. 25
      clients/terminal.c
  21. 1890
      clients/weston-info.c
  22. 409
      clients/window.c
  23. 20
      clients/window.h
  24. 634
      compositor/main.c
  25. 24
      compositor/meson.build
  26. 62
      compositor/screen-share.c
  27. 18
      compositor/text-backend.c
  28. 32
      compositor/weston-private.h
  29. 3
      compositor/weston-screenshooter.c
  30. 226
      compositor/xwayland.c
  31. 737
      desktop-shell/exposay.c
  32. 2
      desktop-shell/input-panel.c
  33. 4
      desktop-shell/meson.build
  34. 1201
      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. 94
      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. 91
      libweston/backend-drm/drm-gbm.c
  70. 180
      libweston/backend-drm/drm-internal.h
  71. 64
      libweston/backend-drm/drm-virtual.c
  72. 703
      libweston/backend-drm/drm.c
  73. 118
      libweston/backend-drm/fb.c
  74. 178
      libweston/backend-drm/kms-color.c
  75. 311
      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. 682
      libweston/backend-drm/state-propose.c
  81. 998
      libweston/backend-fbdev/fbdev.c
  82. 33
      libweston/backend-fbdev/meson.build
  83. 77
      libweston/backend-headless/headless.c
  84. 22
      libweston/backend-rdp/meson.build
  85. 829
      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. 293
      libweston/backend-wayland/wayland.c
  90. 140
      libweston/backend-x11/x11.c
  91. 24
      libweston/backend.h
  92. 4
      libweston/bindings.c
  93. 2
      libweston/clipboard.c
  94. 283
      libweston/color-lcms/color-lcms.c
  95. 101
      libweston/color-lcms/color-lcms.h
  96. 287
      libweston/color-lcms/color-profile.c
  97. 242
      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,12 +1183,12 @@ 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",
format_str, mod_name);
fprintf(stderr, L_LINE L_VAL " format %s, modifier %s\n",
format_str, mod_name);
free(format_str);
free(mod_name);
@ -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,10 +3057,12 @@ 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
terminal_data(terminal, buffer, len);
return;
}
terminal_data(terminal, buffer, len);
}
static int
@ -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
close_task_run(struct task *task, uint32_t events)
{
struct window *window = container_of(task, struct window, close_task);
window->close_handler(window->user_data);
}
static void
window_close(struct window *window)
{
if (window->close_handler)
window->close_handler(window->user_data);
else
if (window->close_handler && !window->close_task.run) {
window->close_task.run = close_task_run;
display_defer(window->display, &window->close_task);
} else {
display_exit(window->display);
}
}
struct display *
@ -1453,15 +1255,6 @@ surface_create_surface(struct surface *surface, uint32_t flags)
struct display *display = surface->window->display;
struct rectangle allocation = surface->allocation;
if (!surface->toysurface && display->dpy &&
surface->buffer_type == WINDOW_BUFFER_TYPE_EGL_WINDOW) {
surface->toysurface =
egl_window_surface_create(display,
surface->surface,
flags,
&allocation);
}
if (!surface->toysurface)
surface->toysurface = shm_surface_create(display,
surface->surface,
@ -4439,9 +4232,24 @@ xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_surface)
window_close(window);
}
static void
xdg_toplevel_handle_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height)
{
}
static void
xdg_toplevel_handle_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel,
struct wl_array *caps)
{
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
xdg_toplevel_handle_configure,
xdg_toplevel_handle_close,
xdg_toplevel_handle_configure_bounds,
xdg_toplevel_handle_wm_capabilities,
};
static void
@ -5272,11 +5080,6 @@ surface_create(struct window *window)
static enum window_buffer_type
get_preferred_buffer_type(struct display *display)
{
#ifdef HAVE_CAIRO_EGL
if (display->argb_device && !getenv("TOYTOOLKIT_NO_EGL"))
return WINDOW_BUFFER_TYPE_EGL_WINDOW;
#endif
return WINDOW_BUFFER_TYPE_SHM;
}
@ -5319,14 +5122,14 @@ window_create(struct display *display)
window->xdg_surface =
xdg_wm_base_get_xdg_surface(window->display->xdg_shell,
window->main_surface->surface);
fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
window->xdg_toplevel =
xdg_surface_get_toplevel(window->xdg_surface);
fail_on_null(window->xdg_toplevel, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_toplevel);
xdg_toplevel_add_listener(window->xdg_toplevel,
&xdg_toplevel_listener, window);
@ -5531,7 +5334,7 @@ create_menu(struct display *display,
menu->widget = window_add_widget(menu->window, menu);
menu->frame = frame_create(window->display->theme, 0, 0,
FRAME_BUTTON_NONE, NULL, NULL);
fail_on_null(menu->frame, 0, __FILE__, __LINE__);
abort_oom_if_null(menu->frame);
menu->entries = entries;
menu->count = count;
menu->release_count = 0;
@ -5565,7 +5368,7 @@ create_simple_positioner(struct display *display,
struct xdg_positioner *positioner;
positioner = xdg_wm_base_create_positioner(display->xdg_shell);
fail_on_null(positioner, 0, __FILE__, __LINE__);
abort_oom_if_null(positioner);
xdg_positioner_set_anchor_rect(positioner, x, y, 1, 1);
xdg_positioner_set_size(positioner, w, h);
xdg_positioner_set_anchor(positioner,
@ -5610,7 +5413,7 @@ window_show_menu(struct display *display,
window->xdg_surface =
xdg_wm_base_get_xdg_surface(display->xdg_shell,
window->main_surface->surface);
fail_on_null(window->xdg_surface, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_surface);
xdg_surface_add_listener(window->xdg_surface,
&xdg_surface_listener, window);
@ -5623,7 +5426,7 @@ window_show_menu(struct display *display,
window->xdg_popup = xdg_surface_get_popup(window->xdg_surface,
parent->xdg_surface,
positioner);
fail_on_null(window->xdg_popup, 0, __FILE__, __LINE__);
abort_oom_if_null(window->xdg_popup);
xdg_positioner_destroy(positioner);
xdg_popup_grab(window->xdg_popup, input->seat,
display_get_serial(window->display));
@ -6089,7 +5892,8 @@ registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
display_add_data_device(d, id, version);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
d->xdg_shell = wl_registry_bind(registry, id,
&xdg_wm_base_interface, 1);
&xdg_wm_base_interface,
MIN(version, 5));
xdg_wm_base_add_listener(d->xdg_shell, &wm_base_listener, d);
} else if (strcmp(interface, "text_cursor_position") == 0) {
d->text_cursor_position =
@ -6140,90 +5944,6 @@ static const struct wl_registry_listener registry_listener = {
registry_handle_global_remove
};
#ifdef HAVE_CAIRO_EGL
static int
init_egl(struct display *d)
{
EGLint major, minor;
EGLint n;
#ifdef USE_CAIRO_GLESV2
# define GL_BIT EGL_OPENGL_ES2_BIT
#else
# define GL_BIT EGL_OPENGL_BIT
#endif
static const EGLint argb_cfg_attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 1,
EGL_DEPTH_SIZE, 1,
EGL_RENDERABLE_TYPE, GL_BIT,
EGL_NONE
};
#ifdef USE_CAIRO_GLESV2
static const EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLint api = EGL_OPENGL_ES_API;
#else
EGLint *context_attribs = NULL;
EGLint api = EGL_OPENGL_API;
#endif
d->dpy =
weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR,
d->display, NULL);
if (!eglInitialize(d->dpy, &major, &minor)) {
fprintf(stderr, "failed to initialize EGL\n");
return -1;
}
if (!eglBindAPI(api)) {
fprintf(stderr, "failed to bind EGL client API\n");
return -1;
}
if (!eglChooseConfig(d->dpy, argb_cfg_attribs,
&d->argb_config, 1, &n) || n != 1) {
fprintf(stderr, "failed to choose argb EGL config\n");
return -1;
}
d->argb_ctx = eglCreateContext(d->dpy, d->argb_config,
EGL_NO_CONTEXT, context_attribs);
if (d->argb_ctx == NULL) {
fprintf(stderr, "failed to create EGL context\n");
return -1;
}
d->argb_device = cairo_egl_device_create(d->dpy, d->argb_ctx);
if (cairo_device_status(d->argb_device) != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "failed to get cairo EGL argb device\n");
return -1;
}
return 0;
}
static void
fini_egl(struct display *display)
{
cairo_device_destroy(display->argb_device);
eglMakeCurrent(display->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglTerminate(display->dpy);
eglReleaseThread();
}
#endif
static void
init_dummy_surface(struct display *display)
{
@ -6329,12 +6049,6 @@ display_create(int *argc, char *argv[])
return NULL;
}
#ifdef HAVE_CAIRO_EGL
if (init_egl(d) < 0)
fprintf(stderr, "EGL does not seem to work, "
"falling back to software rendering and wl_shm.\n");
#endif
create_cursors(d);
d->theme = theme_create();
@ -6393,10 +6107,7 @@ display_destroy(struct display *display)
theme_destroy(display->theme);
destroy_cursors(display);
#ifdef HAVE_CAIRO_EGL
if (display->argb_device)
fini_egl(display);
#endif
cleanup_after_cairo();
if (display->relative_pointer_manager)
zwp_relative_pointer_manager_v1_destroy(display->relative_pointer_manager);
@ -6462,12 +6173,6 @@ display_has_subcompositor(struct display *display)
return display->subcompositor != NULL;
}
cairo_device_t *
display_get_cairo_device(struct display *display)
{
return display->argb_device;
}
struct output *
display_get_output(struct display *display)
{
@ -6489,12 +6194,6 @@ display_get_serial(struct display *display)
return display->serial;
}
EGLDisplay
display_get_egl_display(struct display *d)
{
return d->dpy;
}
struct wl_data_source *
display_create_data_source(struct display *display)
{
@ -6504,38 +6203,6 @@ display_create_data_source(struct display *display)
return NULL;
}
EGLConfig
display_get_argb_egl_config(struct display *d)
{
return d->argb_config;
}
int
display_acquire_window_surface(struct display *display,
struct window *window,
EGLContext ctx)
{
struct surface *surface = window->main_surface;
if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
return -1;
widget_get_cairo_surface(window->main_surface->widget);
return surface->toysurface->acquire(surface->toysurface, ctx);
}
void
display_release_window_surface(struct display *display,
struct window *window)
{
struct surface *surface = window->main_surface;
if (surface->buffer_type != WINDOW_BUFFER_TYPE_EGL_WINDOW)
return;
surface->toysurface->release(surface->toysurface);
}
void
display_defer(struct display *display, struct task *task)
{

@ -71,9 +71,6 @@ display_get_display(struct display *display);
int
display_has_subcompositor(struct display *display);
cairo_device_t *
display_get_cairo_device(struct display *display);
struct wl_compositor *
display_get_compositor(struct display *display);
@ -114,22 +111,6 @@ display_set_output_configure_handler(struct display *display,
struct wl_data_source *
display_create_data_source(struct display *display);
#ifdef EGL_NO_DISPLAY
EGLDisplay
display_get_egl_display(struct display *d);
EGLConfig
display_get_argb_egl_config(struct display *d);
int
display_acquire_window_surface(struct display *display,
struct window *window,
EGLContext ctx);
void
display_release_window_surface(struct display *display,
struct window *window);
#endif
#define SURFACE_OPAQUE 0x01
#define SURFACE_SHM 0x02
@ -416,7 +397,6 @@ struct wl_subsurface *
widget_get_wl_subsurface(struct widget *widget);
enum window_buffer_type {
WINDOW_BUFFER_TYPE_EGL_WINDOW,
WINDOW_BUFFER_TYPE_SHM,
};

@ -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,87 +368,111 @@ 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();
if (pid == 0) {
child_client_exec(sv[1], path);
_exit(-1);
}
/* do not give our signal mask to the new process */
sigfillset(&allsigs);
sigprocmask(SIG_UNBLOCK, &allsigs, NULL);
close(sv[1]);
/* 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);
}
ret = fdstr_clear_cloexec_fd1(&wayland_socket);
if (!ret) {
written = write(STDERR_FILENO, fail_cloexec,
strlen(fail_cloexec));
_exit(EXIT_FAILURE);
}
client = wl_client_create(compositor->wl_display, sv[0]);
if (!client) {
close(sv[0]);
execve(argp[0], argp, envp);
if (fail_exec)
written = write(STDERR_FILENO, fail_exec,
strlen(fail_exec));
_exit(EXIT_FAILURE);
default:
close(wayland_socket.fds[1]);
client = wl_client_create(compositor->wl_display,
wayland_socket.fds[0]);
if (!client) {
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);
return NULL;
}
proc->pid = pid;
proc->cleanup = cleanup;
wet_watch_process(compositor, proc);
break;
case -1:
fdstr_close_all(&wayland_socket);
weston_log("weston_client_launch: "
"wl_client_create failed while launching '%s'.\n",
path);
return NULL;
"fork failed while launching '%s': %s\n", path,
strerror(errno));
break;
}
proc->pid = pid;
proc->cleanup = cleanup;
wet_watch_process(compositor, proc);
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);
}
output = weston_output_find(pointer->seat->compositor,
wl_fixed_to_int(pointer->x),
wl_fixed_to_int(pointer->y));
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;
/* 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;
/* 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);
return 1;
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);
/* 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);
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);
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,
float x, float y, int w, int h)
static struct weston_curtain *
create_curtain(struct weston_compositor *ec, struct fs_output *fsout,
float x, float y, int w, int h)
{
struct weston_surface *surface = NULL;
struct weston_view *view;
struct weston_curtain_params curtain_params = {
.r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0,
.x = x, .y = y, .width = w, .height = h,
.surface_committed = black_surface_committed,
.get_label = NULL,
.surface_private = fsout,
.capture_input = true,
};
struct weston_curtain *curtain;
surface = weston_surface_create(ec);
if (surface == NULL) {
weston_log("no memory\n");
return NULL;
}
view = weston_view_create(surface);
if (!view) {
weston_surface_destroy(surface);
curtain = weston_curtain_create(ec, &curtain_params);
if (!curtain) {
weston_log("no memory\n");
return NULL;
}
surface->committed = black_surface_committed;
surface->committed_private = fsout;
weston_surface_set_color(surface, 0.0f, 0.0f, 0.0f, 1.0f);
pixman_region32_fini(&surface->opaque);
pixman_region32_init_rect(&surface->opaque, 0, 0, w, h);
pixman_region32_fini(&surface->input);
pixman_region32_init_rect(&surface->input, 0, 0, w, h);
weston_surface_set_size(surface, w, h);
weston_view_set_position(view, x, y);
return view;
return curtain;
}
static void
@ -333,13 +324,12 @@ fs_output_create(struct fullscreen_shell *shell, struct weston_output *output)
fsout->surface_destroyed.notify = surface_destroyed;
fsout->pending.surface_destroyed.notify = pending_surface_destroyed;
fsout->black_view = create_black_surface(shell->compositor, fsout,
output->x, output->y,
output->width, output->height);
fsout->black_view->surface->is_mapped = true;
fsout->black_view->is_mapped = true;
fsout->curtain = create_curtain(shell->compositor, fsout,
output->x, output->y,
output->width, output->height);
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;