add sommelier

This commit is contained in:
Bruce Leidl 2019-09-20 18:57:28 -04:00
parent d962bb4d46
commit 1dcad9f1f4
37 changed files with 13672 additions and 0 deletions

145
sommelier/BUILD.gn Normal file
View File

@ -0,0 +1,145 @@
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("wayland_protocol.gni")
group("all") {
deps = [
":sommelier",
":wayland_demo",
":x11_demo",
]
}
if (!defined(peer_cmd_prefix)) {
# if (use.amd64) {
peer_cmd_prefix = "\"/opt/google/cros-containers/lib/ld-linux-x86-64.so.2 --library-path /opt/google/cros-containers/lib --inhibit-rpath \\\"\\\"\""
# }
# if (use.arm) {
# peer_cmd_prefix = "\"/opt/google/cros-containers/lib/ld-linux-armhf.so.3 --library-path /opt/google/cros-containers/lib --inhibit-rpath \\\"\\\"\""
# }
}
# Set this to the Xwayland path.
if (!defined(xwayland_path)) {
xwayland_path = "\"/opt/google/cros-containers/bin/Xwayland\""
}
# Set this to the GL driver path to use for Xwayland.
if (!defined(xwayland_gl_driver_path)) {
xwayland_gl_driver_path = "\"/opt/google/cros-containers/lib\""
}
# Set this to the shm driver to use for Xwayland.
if (!defined(xwayland_shm_driver)) {
xwayland_shm_driver = "\"virtwl\""
}
# Set this to the shm driver to use for wayland clients.
if (!defined(shm_driver)) {
shm_driver = "\"virtwl-dmabuf\""
}
# Set this to the virtwl device.
if (!defined(virtwl_device)) {
virtwl_device = "\"/dev/wl0\""
}
# Set this to the frame color to use for Xwayland clients.
if (!defined(frame_color)) {
frame_color = "\"#f2f2f2\""
}
# Set this to the dark frame color to use for Xwayland clients.
if (!defined(dark_frame_color)) {
dark_frame_color = "\"#323639\""
}
wayland_protocol_library("sommelier-protocol") {
out_dir = "include"
sources = [
"protocol/aura-shell.xml",
"protocol/drm.xml",
"protocol/gtk-shell.xml",
"protocol/keyboard-extension-unstable-v1.xml",
"protocol/linux-dmabuf-unstable-v1.xml",
"protocol/relative-pointer-unstable-v1.xml",
"protocol/text-input-unstable-v1.xml",
"protocol/viewporter.xml",
"protocol/xdg-shell-unstable-v6.xml",
]
}
executable("sommelier") {
pkg_deps = [
"gbm",
"grpc++",
"libdrm",
"pixman-1",
"protobuf",
"vm_protos",
"wayland-client",
"wayland-server",
"xcb",
"xcb-composite",
"xcb-xfixes",
"xkbcommon",
]
libs = [ "m" ]
deps = [
":sommelier-protocol",
]
sources = [
"sommelier-compositor.c",
"sommelier-data-device-manager.c",
"sommelier-display.c",
"sommelier-drm.c",
"sommelier-gtk-shell.c",
"sommelier-output.c",
"sommelier-relative-pointer-manager.c",
"sommelier-seat.c",
"sommelier-shell.c",
"sommelier-shm.c",
"sommelier-subcompositor.c",
"sommelier-text-input.c",
"sommelier-viewporter.c",
"sommelier-xdg-shell.c",
"sommelier.c",
]
defines = [
"_GNU_SOURCE",
"WL_HIDE_DEPRECATED",
"XWAYLAND_PATH=${xwayland_path}",
"XWAYLAND_GL_DRIVER_PATH=${xwayland_gl_driver_path}",
"XWAYLAND_SHM_DRIVER=${xwayland_shm_driver}",
"SHM_DRIVER=${shm_driver}",
"VIRTWL_DEVICE=${virtwl_device}",
"PEER_CMD_PREFIX=${peer_cmd_prefix}",
"FRAME_COLOR=${frame_color}",
"DARK_FRAME_COLOR=${dark_frame_color}",
]
}
executable("wayland_demo") {
pkg_deps = [
"libbrillo-${libbase_ver}",
"libchrome-${libbase_ver}",
"wayland-client",
]
libs = [ "wayland-client" ]
sources = [
"demos/wayland_demo.cc",
]
}
executable("x11_demo") {
pkg_deps = [
"libbrillo-${libbase_ver}",
"libchrome-${libbase_ver}",
]
libs = [ "X11" ]
sources = [
"demos/x11_demo.cc",
]
}

129
sommelier/Makefile Normal file
View File

@ -0,0 +1,129 @@
CC=gcc
SED=sed
CLANG_FORMAT=clang-format-3.9
CLANG_TIDY=clang-tidy-3.9
PREFIX = /usr
SYSCONFDIR = /etc
BINDIR = $(PREFIX)/bin
SRCFILES := sommelier.c version.h
XMLFILES := protocol/aura-shell.xml protocol/viewporter.xml protocol/xdg-shell-unstable-v6.xml protocol/linux-dmabuf-unstable-v1.xml protocol/drm.xml protocol/keyboard-extension-unstable-v1.xml protocol/gtk-shell.xml protocol/relative-pointer-unstable-v1.xml protocol/text-input-unstable-v1.xml
AUXFILES := Makefile README LICENSE AUTHORS sommelier@.service.in sommelier-x@.service.in sommelierrc sommelier.sh
ALLFILES := $(SRCFILES) $(XMLFILES) $(AUXFILES)
#GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags)
#DIST_VERSION := $(shell git describe --abbrev=0 --tags)
DIST_VERSION_BITS := $(subst ., ,$(DIST_VERSION))
DIST_VERSION_MAJOR := $(word 1,$(DIST_VERSION_BITS))
DIST_VERSION_MINOR := $(word 2,$(DIST_VERSION_BITS))
DIST_VERSION_MINOR_NEXT := $(shell expr $(DIST_VERSION_MINOR) + 1)
CFLAGS=-g -Wall `pkg-config --cflags libdrm xcb xcb-composite xcb-xfixes wayland-server wayland-client gbm pixman-1` -I. -D_GNU_SOURCE=1 -DWL_HIDE_DEPRECATED=1 -DXWAYLAND_PATH=\"$(PREFIX)/bin/Xwayland\"
LDFLAGS=-lpthread -lm `pkg-config --libs libdrm xcb xcb-composite xcb-xfixes wayland-server wayland-client gbm pixman-1 xkbcommon`
DEPS = xdg-shell-unstable-v6-client-protocol.h xdg-shell-unstable-v6-server-protocol.h aura-shell-client-protocol.h viewporter-client-protocol.h viewporter-server-protocol.h linux-dmabuf-unstable-v1-client-protocol.h drm-server-protocol.h keyboard-extension-unstable-v1-client-protocol.h gtk-shell-server-protocol.h relative-pointer-unstable-v1-server-protocol.h relative-pointer-unstable-v1-client-protocol.h text-input-unstable-v1-client-protocol.h text-input-unstable-v1-server-protocol.h
OBJECTS = sommelier.o sommelier-compositor.o sommelier-data-device-manager.o sommelier-display.o sommelier-drm.o sommelier-gtk-shell.o sommelier-output.o sommelier-relative-pointer-manager.o sommelier-seat.o sommelier-shell.o sommelier-shm.o sommelier-subcompositor.o sommelier-text-input.o sommelier-viewporter.o sommelier-xdg-shell.o xdg-shell-unstable-v6-protocol.o aura-shell-protocol.o viewporter-protocol.o linux-dmabuf-unstable-v1-protocol.o drm-protocol.o keyboard-extension-unstable-v1-protocol.o gtk-shell-protocol.o relative-pointer-unstable-v1-protocol.o text-input-unstable-v1-protocol.o
#all: sommelier sommelier@.service sommelier-x@.service
all: sommelier
%.service: %.service.in
$(SED) \
-e 's|@bindir[@]|$(BINDIR)|g' \
-e 's|@sysconfdir[@]|$(SYSCONFDIR)|g' \
-e 's|@version[@]|$(DIST_VERSION)|g' \
$< > $@
sommelier: $(OBJECTS)
$(CC) $(OBJECTS) -o sommelier $(LDFLAGS)
strip -s sommelier
%-protocol.c: protocol/%.xml
wayland-scanner private-code < $< > $@
%-client-protocol.h: protocol/%.xml
wayland-scanner client-header < $< > $@
%-server-protocol.h: protocol/%.xml
wayland-scanner server-header < $< > $@
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
$(OBJECTS): $(DEPS)
.PHONY: all install uninstall update-version dist deb version-clean clean style check-style tidy
install: all
install -D sommelier \
$(DESTDIR)$(PREFIX)/bin/sommelier
install -D sommelierrc $(DESTDIR)$(SYSCONFDIR)/sommelierrc
install -m 644 -D sommelier@.service \
$(DESTDIR)$(PREFIX)/lib/systemd/user/sommelier@.service
install -m 644 -D sommelier-x@.service \
$(DESTDIR)$(PREFIX)/lib/systemd/user/sommelier-x@.service
install -m 644 -D sommelier.sh $(DESTDIR)$(SYSCONFDIR)/profile.d/sommelier.sh
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/sommelier
rm -f $(DESTDIR)$(SYSCONFDIR)/sommelierrc
rm -f $(DESTDIR)$(PREFIX)/lib/systemd/user/sommelier@.service
rm -f $(DESTDIR)$(PREFIX)/lib/systemd/user/sommelier-x@.service
rm -f $(DESTDIR)$(SYSCONFDIR)/profile.d/sommelier.sh
update-version:
dch -v $(DIST_VERSION_MAJOR).$(DIST_VERSION_MINOR_NEXT)-1
git commit -m 'debian/changelog: bump to version $(DIST_VERSION_MAJOR).$(DIST_VERSION_MINOR_NEXT)' debian/changelog
$(SED) -i -e 's/VERSION "[0-9.]*"/VERSION "$(DIST_VERSION_MAJOR).$(DIST_VERSION_MINOR_NEXT)"/g' version.h
git tag $(DIST_VERSION_MAJOR).$(DIST_VERSION_MINOR_NEXT)
dist: $(DEPS)
mkdir -p sommelier-$(DIST_VERSION)
cp -r $(ALLFILES) $(DEPS) debian sommelier-$(DIST_VERSION)
tar czf sommelier-$(DIST_VERSION).tar.gz sommelier-$(DIST_VERSION)
rm -rf sommelier-$(DIST_VERSION)
deb: dist
ln -sf sommelier-$(DIST_VERSION).tar.gz sommelier_$(DIST_VERSION).orig.tar.gz
tar xzf sommelier-$(DIST_VERSION).tar.gz
cd sommelier-$(DIST_VERSION) && debuild -i -us -uc -b
rm -rf sommelier-$(DIST_VERSION) sommelier_$(DIST_VERSION).orig.tar.gz
clean:
rm -f *~ *-protocol.c *-protocol.h *.o sommelier sommelier@.service \
sommelier-x@.service sommelier-*.tar.gz sommelier*.deb \
sommelier_*.build sommelier_*.buildinfo sommelier_*.changes
style: $(DEPS)
@for src in $(SRCFILES) ; do \
echo "Formatting $$src..."; \
$(CLANG_FORMAT) -i "$$src"; \
$(CLANG_TIDY) -checks='-*,readability-identifier-naming' \
-config="{CheckOptions: [ \
{ key: readability-identifier-naming.StructCase, value: lower_case }, \
{ key: readability-identifier-naming.FunctionCase, value: lower_case }, \
{ key: readability-identifier-naming.VariableCase, value: lower_case }, \
{ key: readability-identifier-naming.GlobalConstantCase, value: lower_case }, \
{ key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } \
]}" "$$src"; \
done
@echo "Done"
check-style:
@for src in $(SRCFILES) ; do \
var=`$(CLANG_FORMAT) "$$src" | diff "$$src" - | wc -l`; \
if [ $$var -ne 0 ] ; then \
echo "$$src does not respect the coding style (diff: $$var lines)"; \
exit 1; \
fi; \
done
@echo "Style check passed"
tidy: $(DEPS)
@for src in $(SRCFILES); do \
echo "Running tidy on $$src..."; \
$(CLANG_TIDY) -checks="-*,modernize-use-auto,modernize-use-nullptr, \
readability-else-after-return,readability-simplify-boolean-expr, \
readability-redundant-member-init,modernize-use-default-member-init, \
modernize-use-equals-default,modernize-use-equals-delete, \
modernize-use-using,modernize-loop-convert, \
cppcoreguidelines-no-malloc,misc-redundant-expression" \
"$$src"; \
done
@echo "Done"

5
sommelier/OWNERS Normal file
View File

@ -0,0 +1,5 @@
set noparent
reveman@chromium.org
hollingum@google.com
sidereal@google.com
davidriley@chromium.org

247
sommelier/README.md Normal file
View File

@ -0,0 +1,247 @@
# Sommelier - Nested Wayland compositor with support for X11 forwarding
Sommelier is an implementation of a Wayland compositor that delegates
compositing to a 'host' compositor. Sommelier includes a set of features that
allows it to run inside a tight jail or virtual machine.
Sommelier can run as service or as a wrapper around the execution of a
program. As a service, it spawns new processes as needed to service clients.
The parent process is called the master sommelier.
## Sommeliers
### Master Sommelier
The master sommelier instance will create a wayland socket in XDG_RUNTIME_DIR
and accept connections from regular wayland clients. Each connection will be
serviced by spawning a child sommelier process.
### X11 Sommelier
An X11 sommelier instance provides X11 forwarding. Xwayland is used to
accomplish this. A single X11 sommelier instance is typically shared across
all X11 clients as they often expect that they can use a shared X server for
communication. If the X11 sommelier instance crashes in this setup, it takes
all running X11 programs down with it. Multiple X11 sommelier instances
can be used for improved isolation or when per-client configuration is
needed, but it will be at the cost of losing the ability for programs to use
the X server for communication between each other.
### Peer Sommelier
Each Linux program that support the Wayland protocol can have its own sommelier.
This provides better use of multiple cores when servicing clients, and it
prevents errors in one client from causing other clients to crash.
## Host Compositor Channel
Sommelier needs a channel to the host compositor in order to serve Wayland
clients inside a container. If the container environment provides a socket
that can be used to establish a connection to the host compositor, then
pointing sommelier to this socket using the `--display=DISPLAY` flag is
sufficient.
### VirtWL
The VirtWL device can be used to establish a new connection when no socket
is available (typically when running inside a VM). If a VirtWL device has been
specified (e.g. `--virtwl-device=/dev/wl0`) then sommelier will use this
mechanism by default to establish new channels between the host compositor and
sommelier instances. Data is forwarded between the VirtWL device and the core
Wayland dispatch mechanism using non-blocking I/O multiplexing.
## Shared Memory Drivers
Shared memory allocated inside a container cannot always be shared with the
host compositor. Sommelier provides a shared memory driver option as a
solution for this. What's the most appropriate option depends on the host
compositor and device drivers available for allocating buffers.
### Noop
The `noop` shared memory driver simply forwards buffers to the host without
any special processing. This requires that the client inside the container is
using memory that can be shared with the host compositor.
### VirtWL
The `virtwl` driver creates a set of intermediate virtwl buffers for each
surface, and copies minimal damaged areas from the clients standard shared
memory buffers into the virtwl buffers that can be shared with the host
compositor.
### VirtWL-DMABuf
The `virtwl-dmabuf` works the same way as the `virtwl` driver but allocates
buffers that can be shared with the host compositor using the linux_dmabuf
protocol. The benefits of using this driver over the basic `virtwl` driver
are:
* Larger set of supported formats (E.g NV12).
* Host compositor can avoid expensive texture uploads.
* HW overlays can be used for presentation if support by the host compositor.
### DMABuf
The `dmabuf` driver is similar to the `virtwl-dmabuf` driver. It creates a set
of intermediate buffers for each surface and copies minimal damaged areas from
the clients standard shared memory buffer into the DMABuf buffer. However,
the buffer is allocated using a DRM device and a prime FD is used to access
buffer memory inside the container. Intermediate buffers are shared with the
host compositor using the linux_dmabuf protocol.
## Damage Tracking
Shared memory drivers that use intermediate buffers require some form of
damage tracking in order to update intermediate buffers.
### Surface Buffer Queue
Each client surface in sommelier is associated with a buffer queue. Each
buffer in the buffer queue has a region (list of rectangles) that describes
the part of the buffer that is damaged compared to the last frame submitted
by the client. This provides high precision damage tracking across multiple
frames. Each new frame from the client adds damage to existing buffers. When
submitting a frame to the host compositor, the next available buffer is
dequeued and updated to not contain any damage. This is done by copying
contents from the current client buffer into the dequeued buffer.
The client's buffer is released as soon as this copy operation described above
is complete and the client can then reuse the shared memory buffer for another
frame.
Note: It is important to release the buffer immediately as clients dont
expect it to be held by the compositor for long when using shared memory.
### Back Pressure
Sommelier doesnt provide any back pressure for when the client is producing
contents faster than the host compositor can consume it. The size of the
buffer queue can as a result grow large. This is not a problem as Xwayland
and other clients handle back pressure themselves using Wayland frame
callbacks or similar mechanism.
## Data Drivers
Socket pairs created inside a container cannot always be shared with the
host compositor. Sommelier provides a data driver option as a solution
for this.
### Noop
The `noop` driver simply forwards socket pair FDs to the host without any
special processing. This requires that the client inside the container is
using socket pairs that can be shared with the host compositor.
### VirtWL
The `virtwl` driver creates a special pipe that can be shared with the host
compositor and forwards all data received over this pipe to the client FD.
Forwarding is done using non-blocking I/O multiplexing.
## Flags and Settings
Sommelier has two forms of configuration. Command line flags and environment
variables. Standard practice is to expose each option both as a command line
flag and as an environment variable. Command line flags will always override
the configuration provided by environment variables. This makes it easy to
run sommelier as a systemd service and allow the system-wide configuration
to be overridden using a local user provided systemd override file.
## Density and Scaling
A protocol aware proxy compositor between the client and the host compositor
makes it easier to support Linux programs that lack good HiDPI support.
It can also be used to adjust the scale of contents to support the dynamic
density changes that Chrome OS UI provide, and it gives the user an option
override any density decisions made by the host compositor. For example,
HiDPI aware programs can run at native display resolution, while some older
programs can use half of that resolution.
### Contents Scaling
Contents scaling can be applied to both native wayland clients and X11
clients. It can be controlled using the `--scale=SCALE` flag or
`SOMMELIER_SCALE=SCALE` variable. Where `SCALE` is a display density
multiplier. For example, if the default density is 200 DPI, then using
`--scale=0.5` will result in contents produced for 100 DPI.
### Scale Factor
An optimal scale factor is calculated for Wayland clients based on contents
scale setting and the current host compositor scaling. This allows Wayland
clients to produce contents at an optimal resolution for all combinations of
scaling used by sommelier and the host compositor.
### DPI
An exact value for DPI is calculated by sommelier. However, many Linux
programs expect DPI to be one out of a well known set of values. Sommelier
solves this by adjusting DPI using a set of buckets. For example, given the
default set of buckets (72, 96, 160, 240), Sommelier will use 96 as DPI when
the exact value is 112, or 160 when exact value is 188. The DPI buckets that
sommelier should use can be specified with `--dpi=[DPI[,DPI...]]`. Where,
`--dpi=””` will result in sommelier exposing the exact DPI value to clients.
### XCursor
Sommelier will set `XCURSOR_SIZE` environment variable automatically based on
the contents scale and preferred host compositor scale factor.
## Accelerators
If the host compositor support dynamic handling of keyboard events, then
keyboard shortcuts are forwarded to the Linux program by default. A small set
of shortcuts are expected to be reserved by the host compositor. A list of
reserved shortcuts on Chrome OS can be found
[here](https://chromium.googlesource.com/chromium/src/+/master/ash/accelerators/accelerator_table.h#22).
Theres unfortunately no reliable way to detect if a Linux program handled a
key event or not. This means that all non-reserved shortcuts that the user
want the host compositor to handle needs to be explicitly listed as an
accelerator. For example, on Chrome OS, the launcher can be brought up using
the "launcher" button during normal usage. The "launcher" button event is
forwarded to Linux programs by default so it wont work when a Linux program
has keyboard focus unless this shortcut is explicitly listed as an accelerator.
Sommelier provides the `--accelerator=ACCELERATORS` flag for this purpose.
`ACCELERATORS` is a comma separated list of accelerators that shouldnt be
forwarded to the Linux program but instead handled by the host compositor.
Each accelerator can contain a list of modifiers (e.g. `<Control><Alt>`) and
must be followed by an XKB keysym. The `xev` utility can be used to determine
what the XKB keysym is for a specific key. Given the launcher button example
above (which happens to have XKB keysym `Super_L` on the Chromebook Pixel),
`--accelerators=Super_L` needs to be passed to sommelier for the this button to
bring up the application launcher when Linux programs have keyboard focus.
Consistent with other flags, `SOMMELIER_ACCELERATORS` environment variable can
be used as an alternative to the command line flag.
## Examples
Start master sommelier and use wayland-1 as name of socket to listen on:
```
sommelier --master --socket=wayland-1
```
Start sommelier that runs weston-terminal with density scale multiplier 1.5:
```
sommelier --scale=1.5 weston-terminal
```
Start sommelier that runs inkscape with density scale multiplier 0.75 and 120
dots per inch (note that -X is specified as inkscape is an X11 client and
requires X11 forwarding):
```
sommelier -X --scale=0.75 --dpi=120 inkscape
```
Start sommelier that runs gedit with some accelerators reserved to the host
compositor instead of being sent to gedit:
```
sommelier --accelerators="<Alt>Bracketright,<Alt>Bracketleft" gedit
```

8
sommelier/build/args.gn Normal file
View File

@ -0,0 +1,8 @@
# Set build arguments here. See `gn buildargs`.
pkg_config = "/usr/bin/pkg-config"
libdir = "/usr/lib/x86_64-linux-gnu/"
platform_subdir="vm_tools/sommelier"
cxx="g++"
cc="gcc"
ar="/usr/bin/ar"
#use.amd64=true

16
sommelier/config.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
/*
#ifndef XWAYLAND_PATH
#define XWAYLAND_PATH "/usr/bin/Xwayland"
#endif
*/
#define XWAYLAND_GL_DRIVER_PATH "/lib/x86_64-linux-gnu/dri"
#define XWAYLAND_SHM_DRIVER "virtwl"
#define SHM_DRIVER "virtwl"
#define VIRTWL_DEVICE "/dev/wl0"
#define PEER_CMD_PREFIX ""
#define FRAME_COLOR "#f2f2f2"
#define DARK_FRAME_COLOR "#323639"
#endif

View File

@ -0,0 +1,319 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "base/strings/string_number_conversions.h"
#include "brillo/syslog_logging.h"
constexpr char kBgColorFlag[] = "bgcolor";
constexpr char kWidthFlag[] = "width";
constexpr char kHeightFlag[] = "height";
constexpr char kTitleFlag[] = "title";
struct demo_data {
uint32_t bgcolor;
uint32_t width;
uint32_t height;
std::string title;
int scale;
struct wl_compositor* compositor;
struct wl_shell* shell;
struct wl_shm* shm;
struct wl_surface* surface;
struct wl_shell_surface* shell_surface;
struct wl_buffer* buffer;
struct wl_callback* callback;
struct wl_callback_listener* callback_listener;
struct wl_output* output;
struct wl_output_listener* output_listener;
struct wl_keyboard_listener* keyboard_listener;
void* shm_ptr;
bool done;
};
void keyboard_keymap(void* data,
struct wl_keyboard* keyboard,
uint32_t format,
int32_t fd,
uint32_t size) {}
void keyboard_enter(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
struct wl_surface* surface,
struct wl_array* keys) {}
void keyboard_leave(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
struct wl_surface* surface) {}
void keyboard_key(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
// Key pressed.
if (state == 1) {
LOG(INFO) << "wayland_demo application detected keypress";
data_ptr->done = true;
}
}
void keyboard_modifiers(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group) {}
void keyboard_repeat_info(void* data,
struct wl_keyboard* keyboard,
int32_t rate,
int32_t delay) {}
void demo_registry_listener(void* data,
struct wl_registry* registry,
uint32_t id,
const char* interface,
uint32_t version) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
if (!strcmp("wl_compositor", interface)) {
data_ptr->compositor = reinterpret_cast<struct wl_compositor*>(
wl_registry_bind(registry, id, &wl_compositor_interface, version));
} else if (!strcmp("wl_shell", interface)) {
data_ptr->shell = reinterpret_cast<struct wl_shell*>(
wl_registry_bind(registry, id, &wl_shell_interface, version));
} else if (!strcmp("wl_shm", interface)) {
data_ptr->shm = reinterpret_cast<struct wl_shm*>(
wl_registry_bind(registry, id, &wl_shm_interface, version));
} else if (!strcmp("wl_output", interface)) {
data_ptr->output = reinterpret_cast<struct wl_output*>(
wl_registry_bind(registry, id, &wl_output_interface, version));
wl_output_add_listener(data_ptr->output, data_ptr->output_listener,
data_ptr);
} else if (!strcmp("wl_seat", interface)) {
struct wl_seat* seat = reinterpret_cast<struct wl_seat*>(
wl_registry_bind(registry, id, &wl_seat_interface, version));
wl_keyboard_add_listener(wl_seat_get_keyboard(seat),
data_ptr->keyboard_listener, data_ptr);
}
}
void demo_registry_remover(void* data,
struct wl_registry* registry,
uint32_t id) {}
void shell_surface_ping(void* data,
struct wl_shell_surface* shell_surface,
uint32_t serial) {
wl_shell_surface_pong(shell_surface, serial);
}
void shell_surface_configure(void* data,
struct wl_shell_surface* shell_surface,
uint32_t edges,
int32_t width,
int32_t height) {}
void shell_surface_popup_done(void* data,
struct wl_shell_surface* shell_surface) {}
void demo_draw(void* data, struct wl_callback* callback, uint32_t time) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
wl_callback_destroy(data_ptr->callback);
wl_surface_damage(data_ptr->surface, 0, 0, data_ptr->width, data_ptr->height);
uint32_t* surface_data = reinterpret_cast<uint32_t*>(data_ptr->shm_ptr);
for (int i = 0; i < data_ptr->width * data_ptr->height; ++i) {
surface_data[i] = data_ptr->bgcolor;
}
data_ptr->callback = wl_surface_frame(data_ptr->surface);
wl_surface_attach(data_ptr->surface, data_ptr->buffer, 0, 0);
wl_callback_add_listener(data_ptr->callback, data_ptr->callback_listener,
data_ptr);
wl_surface_commit(data_ptr->surface);
}
void output_geometry(void* data,
struct wl_output* 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) {}
void output_mode(void* data,
struct wl_output* output,
uint32_t flags,
int32_t width,
int32_t height,
int32_t refresh) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
if (data_ptr->width == 0) {
data_ptr->width = width;
if (data_ptr->scale != 0) {
data_ptr->width /= data_ptr->scale;
}
}
if (data_ptr->height == 0) {
data_ptr->height = height;
if (data_ptr->scale != 0) {
data_ptr->height /= data_ptr->scale;
}
}
}
void output_done(void* data, struct wl_output* output) {}
void output_scale(void* data, struct wl_output* output, int32_t factor) {
struct demo_data* data_ptr = reinterpret_cast<struct demo_data*>(data);
data_ptr->scale = factor;
if (data_ptr->width != 0) {
data_ptr->width /= factor;
}
if (data_ptr->height != 0) {
data_ptr->height /= factor;
}
}
int main(int argc, char* argv[]) {
brillo::InitLog(brillo::kLogToSyslog);
LOG(INFO) << "Starting wayland_demo application";
base::CommandLine::Init(argc, argv);
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
struct demo_data data;
memset(&data, 0, sizeof(data));
data.done = false;
data.bgcolor = 0x3388DD;
if (cl->HasSwitch(kBgColorFlag)) {
data.bgcolor =
strtoul(cl->GetSwitchValueASCII(kBgColorFlag).c_str(), nullptr, 0);
}
if (cl->HasSwitch(kWidthFlag)) {
if (!base::StringToUint(cl->GetSwitchValueASCII(kWidthFlag), &data.width)) {
LOG(ERROR) << "Invalid width parameter passed";
return -1;
}
}
if (cl->HasSwitch(kHeightFlag)) {
if (!base::StringToUint(cl->GetSwitchValueASCII(kHeightFlag),
&data.height)) {
LOG(ERROR) << "Invalid height parameter passed";
return -1;
}
}
data.title = "wayland_demo";
if (cl->HasSwitch(kTitleFlag)) {
data.title = cl->GetSwitchValueASCII(kTitleFlag);
}
struct wl_display* display = wl_display_connect(nullptr);
if (!display) {
LOG(ERROR) << "Failed connecting to display";
return -1;
}
struct wl_output_listener output_listener = {output_geometry, output_mode,
output_done, output_scale};
data.output_listener = &output_listener;
struct wl_registry_listener registry_listener = {
demo_registry_listener, demo_registry_remover,
};
struct wl_keyboard_listener keyboard_listener = {
keyboard_keymap, keyboard_enter, keyboard_leave,
keyboard_key, keyboard_modifiers, keyboard_repeat_info};
data.keyboard_listener = &keyboard_listener;
struct wl_registry* registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, &data);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (!data.compositor) {
LOG(ERROR) << "Failed to find compositor";
return -1;
}
if (!data.output) {
LOG(ERROR) << "Failed to get output";
return -1;
}
// Do another roundtrip to ensure we get the wl_output callbacks.
wl_display_roundtrip(display);
data.surface = wl_compositor_create_surface(data.compositor);
if (!data.surface) {
LOG(ERROR) << "Failed creating surface";
return -1;
}
if (!data.shell) {
LOG(ERROR) << "Failed getting shell";
return -1;
}
data.shell_surface = wl_shell_get_shell_surface(data.shell, data.surface);
if (!data.shell_surface) {
LOG(ERROR) << "Failed getting shell surface";
return -1;
}
const struct wl_shell_surface_listener shell_surface_listener = {
shell_surface_ping, shell_surface_configure, shell_surface_popup_done};
wl_shell_surface_add_listener(data.shell_surface, &shell_surface_listener,
nullptr);
wl_shell_surface_set_toplevel(data.shell_surface);
wl_shell_surface_set_class(data.shell_surface, data.title.c_str());
wl_shell_surface_set_title(data.shell_surface, data.title.c_str());
data.callback = wl_surface_frame(data.surface);
struct wl_callback_listener callback_listener = {demo_draw};
data.callback_listener = &callback_listener;
wl_callback_add_listener(data.callback, data.callback_listener, &data);
if (!data.shm) {
LOG(ERROR) << "Failed getting shared memory";
return -1;
}
size_t stride = data.width * 4 /* 32bpp */;
size_t shm_size = stride * data.height;
base::SharedMemory shared_mem;
shared_mem.CreateAndMapAnonymous(shm_size);
data.shm_ptr = shared_mem.memory();
struct wl_shm_pool* pool =
wl_shm_create_pool(data.shm, shared_mem.handle().fd, shm_size);
data.buffer = wl_shm_pool_create_buffer(pool, 0, data.width, data.height,
stride, WL_SHM_FORMAT_XRGB8888);
wl_shm_pool_destroy(pool);
wl_surface_attach(data.surface, data.buffer, 0, 0);
wl_surface_commit(data.surface);
demo_draw(&data, nullptr, 0);
LOG(INFO) << "wayland_demo application displaying, waiting for keypress";
do {
} while (wl_display_dispatch(display) != -1 && !data.done);
wl_display_disconnect(display);
LOG(INFO) << "wayland_demo application exiting";
return 0;
}

View File

@ -0,0 +1,90 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "brillo/syslog_logging.h"
constexpr char kBgColorFlag[] = "bgcolor";
constexpr char kWidthFlag[] = "width";
constexpr char kHeightFlag[] = "height";
constexpr char kTitleFlag[] = "title";
// Creates an X window the same size as the display and fills its background
// with a solid color that can be specified as the only parameter (in hex or
// base 10). Closes on any keypress.
int main(int argc, char* argv[]) {
brillo::InitLog(brillo::kLogToSyslog);
LOG(INFO) << "Starting x11_demo application";
base::CommandLine::Init(argc, argv);
base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
uint32_t bgcolor = 0x99EE44;
if (cl->HasSwitch(kBgColorFlag)) {
bgcolor =
strtoul(cl->GetSwitchValueASCII(kBgColorFlag).c_str(), nullptr, 0);
}
std::string title = "x11_demo";
if (cl->HasSwitch(kTitleFlag)) {
title = cl->GetSwitchValueASCII(kTitleFlag);
}
Display* dpy = XOpenDisplay(nullptr);
if (!dpy) {
LOG(ERROR) << "Failed opening display";
return -1;
}
int screen = DefaultScreen(dpy);
Window win;
int x, y;
unsigned int width, height, border, depth;
if (XGetGeometry(dpy, RootWindow(dpy, screen), &win, &x, &y, &width, &height,
&border, &depth) == 0) {
LOG(ERROR) << "Failed getting screen geometry";
return -1;
}
if (cl->HasSwitch(kWidthFlag)) {
if (!base::StringToUint(cl->GetSwitchValueASCII(kWidthFlag), &width)) {
LOG(ERROR) << "Invalid width parameter passed";
return -1;
}
}
if (cl->HasSwitch(kHeightFlag)) {
if (!base::StringToUint(cl->GetSwitchValueASCII(kHeightFlag), &height)) {
LOG(ERROR) << "Invalid height parameter passed";
return -1;
}
}
win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), x, y, width, height,
0, 0 /* black */, bgcolor);
XClassHint* wmclass_hint = XAllocClassHint();
wmclass_hint->res_name = wmclass_hint->res_class = strdup(title.c_str());
XSetClassHint(dpy, win, wmclass_hint);
XSelectInput(dpy, win, KeyPressMask);
XMapWindow(dpy, win);
XStoreName(dpy, win, title.c_str());
LOG(INFO) << "x11_demo application displaying, waiting for keypress";
XEvent evt;
for (;;) {
XNextEvent(dpy, &evt);
if (evt.type == KeyPress) {
LOG(INFO) << "x11_demo application detected keypress";
break;
}
}
XCloseDisplay(dpy);
LOG(INFO) << "x11_demo application exiting";
return 0;
}

135
sommelier/linux/virtio_wl.h Normal file
View File

@ -0,0 +1,135 @@
#ifndef _LINUX_VIRTIO_WL_H
#define _LINUX_VIRTIO_WL_H
/*
* This header is BSD licensed so anyone can use the definitions to implement
* compatible drivers/servers.
*/
#include <linux/virtio_ids.h>
#include <linux/virtio_config.h>
#include <linux/virtwl.h>
#define VIRTWL_IN_BUFFER_SIZE 4096
#define VIRTWL_OUT_BUFFER_SIZE 4096
#define VIRTWL_VQ_IN 0
#define VIRTWL_VQ_OUT 1
#define VIRTWL_QUEUE_COUNT 2
#define VIRTWL_MAX_ALLOC 0x800
#define VIRTWL_PFN_SHIFT 12
/* Enables the transition to new flag semantics */
#define VIRTIO_WL_F_TRANS_FLAGS 1
struct virtio_wl_config {
};
/*
* The structure of each of these is virtio_wl_ctrl_hdr or one of its subclasses
* where noted.
*/
enum virtio_wl_ctrl_type {
VIRTIO_WL_CMD_VFD_NEW = 0x100, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_CMD_VFD_CLOSE, /* virtio_wl_ctrl_vfd */
VIRTIO_WL_CMD_VFD_SEND, /* virtio_wl_ctrl_vfd_send + data */
VIRTIO_WL_CMD_VFD_RECV, /* virtio_wl_ctrl_vfd_recv + data */
VIRTIO_WL_CMD_VFD_NEW_CTX, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_CMD_VFD_NEW_PIPE, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_CMD_VFD_HUP, /* virtio_wl_ctrl_vfd */
VIRTIO_WL_CMD_VFD_NEW_DMABUF, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_CMD_VFD_DMABUF_SYNC, /* virtio_wl_ctrl_vfd_dmabuf_sync */
VIRTIO_WL_CMD_VFD_SEND_FOREIGN_ID, /* virtio_wl_ctrl_vfd_send + data */
VIRTIO_WL_RESP_OK = 0x1000,
VIRTIO_WL_RESP_VFD_NEW = 0x1001, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_RESP_VFD_NEW_DMABUF = 0x1002, /* virtio_wl_ctrl_vfd_new */
VIRTIO_WL_RESP_ERR = 0x1100,
VIRTIO_WL_RESP_OUT_OF_MEMORY,
VIRTIO_WL_RESP_INVALID_ID,
VIRTIO_WL_RESP_INVALID_TYPE,
VIRTIO_WL_RESP_INVALID_FLAGS,
VIRTIO_WL_RESP_INVALID_CMD,
};
struct virtio_wl_ctrl_hdr {
__le32 type; /* one of virtio_wl_ctrl_type */
__le32 flags; /* always 0 */
};
enum virtio_wl_vfd_flags {
VIRTIO_WL_VFD_WRITE = 0x1, /* intended to be written by guest */
VIRTIO_WL_VFD_READ = 0x2, /* intended to be read by guest */
};
struct virtio_wl_ctrl_vfd {
struct virtio_wl_ctrl_hdr hdr;
__le32 vfd_id;
};
/*
* If this command is sent to the guest, it indicates that the VFD has been
* created and the fields indicate the properties of the VFD being offered.
*
* If this command is sent to the host, it represents a request to create a VFD
* of the given properties. The pfn field is ignored by the host.
*/
struct virtio_wl_ctrl_vfd_new {
struct virtio_wl_ctrl_hdr hdr;
__le32 vfd_id; /* MSB indicates device allocated vfd */
__le32 flags; /* virtio_wl_vfd_flags */
__le64 pfn; /* first guest physical page frame number if VFD_MAP */
__le32 size; /* size in bytes if VIRTIO_WL_CMD_VFD_NEW* */
/* buffer description if VIRTIO_WL_CMD_VFD_NEW_DMABUF */
struct {
__le32 width; /* width in pixels */
__le32 height; /* height in pixels */
__le32 format; /* fourcc format */
__le32 stride0; /* return stride0 */
__le32 stride1; /* return stride1 */
__le32 stride2; /* return stride2 */
__le32 offset0; /* return offset0 */
__le32 offset1; /* return offset1 */
__le32 offset2; /* return offset2 */
} dmabuf;
};
enum virtio_wl_ctrl_vfd_send_kind {
/* The id after this one indicates an ordinary vfd_id. */
VIRTIO_WL_CTRL_VFD_SEND_KIND_LOCAL,
/* The id after this one is a virtio-gpu resource id. */
VIRTIO_WL_CTRL_VFD_SEND_KIND_VIRTGPU,
};
struct virtio_wl_ctrl_vfd_send_vfd {
__le32 kind; /* virtio_wl_ctrl_vfd_send_kind */
__le32 id;
};
struct virtio_wl_ctrl_vfd_send {
struct virtio_wl_ctrl_hdr hdr;
__le32 vfd_id;
__le32 vfd_count; /* struct is followed by this many IDs */
/*
* If hdr.type == VIRTIO_WL_CMD_VFD_SEND_FOREIGN_ID, there is a
* vfd_count array of virtio_wl_ctrl_vfd_send_vfd. Otherwise, there is a
* vfd_count array of vfd_ids.
*/
/* the remainder is raw data */
};
struct virtio_wl_ctrl_vfd_recv {
struct virtio_wl_ctrl_hdr hdr;
__le32 vfd_id;
__le32 vfd_count; /* struct is followed by this many IDs */
/* the remainder is raw data */
};
struct virtio_wl_ctrl_vfd_dmabuf_sync {
struct virtio_wl_ctrl_hdr hdr;
__le32 vfd_id;
__le32 flags;
};
#endif /* _LINUX_VIRTIO_WL_H */

64
sommelier/linux/virtwl.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef _LINUX_VIRTWL_H
#define _LINUX_VIRTWL_H
#include <asm/ioctl.h>
#include <linux/types.h>
#define VIRTWL_SEND_MAX_ALLOCS 28
#define VIRTWL_IOCTL_BASE 'w'
#define VIRTWL_IO(nr) _IO(VIRTWL_IOCTL_BASE, nr)
#define VIRTWL_IOR(nr, type) _IOR(VIRTWL_IOCTL_BASE, nr, type)
#define VIRTWL_IOW(nr, type) _IOW(VIRTWL_IOCTL_BASE, nr, type)
#define VIRTWL_IOWR(nr, type) _IOWR(VIRTWL_IOCTL_BASE, nr, type)
enum virtwl_ioctl_new_type {
VIRTWL_IOCTL_NEW_CTX, /* open a new wayland connection context */
VIRTWL_IOCTL_NEW_ALLOC, /* create a new virtwl shm allocation */
/* create a new virtwl pipe that is readable via the returned fd */
VIRTWL_IOCTL_NEW_PIPE_READ,
/* create a new virtwl pipe that is writable via the returned fd */
VIRTWL_IOCTL_NEW_PIPE_WRITE,
/* create a new virtwl dmabuf that is writable via the returned fd */
VIRTWL_IOCTL_NEW_DMABUF,
};
struct virtwl_ioctl_new {
__u32 type; /* VIRTWL_IOCTL_NEW_* */
int fd; /* return fd */
__u32 flags; /* currently always 0 */
union {
/* size of allocation if type == VIRTWL_IOCTL_NEW_ALLOC */
__u32 size;
/* buffer description if type == VIRTWL_IOCTL_NEW_DMABUF */
struct {
__u32 width; /* width in pixels */
__u32 height; /* height in pixels */
__u32 format; /* fourcc format */
__u32 stride0; /* return stride0 */
__u32 stride1; /* return stride1 */
__u32 stride2; /* return stride2 */
__u32 offset0; /* return offset0 */
__u32 offset1; /* return offset1 */
__u32 offset2; /* return offset2 */
} dmabuf;
};
};
struct virtwl_ioctl_txn {
int fds[VIRTWL_SEND_MAX_ALLOCS];
__u32 len;
__u8 data[0];
};
struct virtwl_ioctl_dmabuf_sync {
__u32 flags; /* synchronization flags (see dma-buf.h) */
};
#define VIRTWL_IOCTL_NEW VIRTWL_IOWR(0x00, struct virtwl_ioctl_new)
#define VIRTWL_IOCTL_SEND VIRTWL_IOR(0x01, struct virtwl_ioctl_txn)
#define VIRTWL_IOCTL_RECV VIRTWL_IOW(0x02, struct virtwl_ioctl_txn)
#define VIRTWL_IOCTL_DMABUF_SYNC VIRTWL_IOR(0x03, \
struct virtwl_ioctl_dmabuf_sync)
#endif /* _LINUX_VIRTWL_H */

View File

@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="aura_shell">
<copyright>
Copyright 2017 The Chromium Authors.
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.
</copyright>
<interface name="zaura_shell" version="6">
<description summary="aura_shell">
The global interface exposing aura shell capabilities is used to
instantiate an interface extension for a wl_surface object.
This extended interface will then allow the client to use aura shell
specific functionality.
</description>
<enum name="error">
<entry name="aura_surface_exists" value="0"
summary="the surface already has an aura surface object associated"/>
<entry name="aura_output_exists" value="1"
summary="the output already has an aura output object associated"/>
</enum>
<request name="get_aura_surface">
<description summary="extend surface interface for aura shell">
Instantiate an interface extension for the given wl_surface to
provide aura shell functionality. If the given wl_surface is not
associated with a shell surface, the shell_surface_missing protocol
error is raised.
</description>
<arg name="id" type="new_id" interface="zaura_surface"
summary="the new aura surface interface id"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface"/>
</request>
<!-- Version 2 additions -->
<request name="get_aura_output" since="2">
<description summary="extend output interface for aura shell">
Instantiate an interface extension for the given wl_output to
provide aura shell functionality.
</description>
<arg name="id" type="new_id" interface="zaura_output"
summary="the new aura output interface id"/>
<arg name="output" type="object" interface="wl_output"
summary="the output"/>
</request>
</interface>
<interface name="zaura_surface" version="5">
<description summary="aura shell interface to a wl_surface">
An additional interface to a wl_surface object, which allows the
client to access aura shell specific functionality for surface.
</description>
<enum name="frame_type">
<description summary="different frame types">
Frame types that can be used to decorate a surface.
</description>
<entry name="none" value="0" summary="no frame"/>
<entry name="normal" value="1" summary="caption with shadow" />
<entry name="shadow" value="2" summary="shadow only"/>
</enum>
<request name="set_frame">
<description summary="request a frame for surface">
Suggests a surface should use a specific frame.
</description>
<arg name="type" type="uint" summary="the new frame type"/>
</request>
<!-- Version 2 additions -->
<request name="set_parent" since="2">
<description summary="set the parent of this surface">
Set the "parent" of this surface. "x" and "y" arguments specify the
initial position for surface relative to parent.
</description>
<arg name="parent" type="object" interface="zaura_surface" allow-null="true"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
</request>
<!-- Version 3 additions -->
<request name="set_frame_colors" since="3">
<description summary="set the frame colors of this surface">
Set the frame colors.
</description>
<arg name="active_color" type="uint" summary="32 bit ARGB color value, not premultiplied"/>
<arg name="inactive_color" type="uint" summary="32 bit ARGB color value, not premultiplied"/>
</request>
<!-- Version 4 additions -->
<request name="set_startup_id" since="4">
<description summary="set the startup ID of this surface">
Set the startup ID.
</description>
<arg name="startup_id" type="string" allow-null="true"/>
</request>
<!-- Version 5 additions -->
<request name="set_application_id" since="5">
<description summary="set the application ID of this surface">
Set the application ID.
</description>
<arg name="application_id" type="string" allow-null="true"/>
</request>
</interface>
<interface name="zaura_output" version="6">
<description summary="aura shell interface to a wl_output">
An additional interface to a wl_output object, which allows the
client to access aura shell specific functionality for output.
</description>
<!-- Version 2 additions -->
<enum name="scale_property" bitfield="true">
<description summary="scale information">
These flags describe properties of an output scale.
They are used in the flags bitfield of the scale event.
</description>
<entry name="current" value="0x1"
summary="indicates this is the current scale"/>
<entry name="preferred" value="0x2"
summary="indicates this is the preferred scale"/>
</enum>
<enum name="scale_factor">
<entry name="0400" value="400"/>
<entry name="0500" value="500"/>
<entry name="0550" value="550"/>
<entry name="0600" value="600"/>
<entry name="0625" value="625"/>
<entry name="0650" value="650"/>
<entry name="0700" value="700"/>
<entry name="0750" value="750"/>
<entry name="0800" value="800"/>
<entry name="0850" value="850"/>
<entry name="0900" value="900"/>
<entry name="0950" value="950"/>
<entry name="1000" value="1000"/>
<entry name="1050" value="1050"/>
<entry name="1100" value="1100"/>
<entry name="1150" value="1150"/>
<entry name="1125" value="1125"/>
<entry name="1200" value="1200"/>
<entry name="1250" value="1250"/>
<entry name="1300" value="1300"/>
<entry name="1400" value="1400"/>
<entry name="1450" value="1450"/>
<entry name="1500" value="1500"/>
<entry name="1600" value="1600"/>
<entry name="1750" value="1750"/>
<entry name="1800" value="1800"/>
<entry name="2000" value="2000"/>
<entry name="2200" value="2200"/>
<entry name="2250" value="2250"/>
<entry name="2500" value="2500"/>
<entry name="2750" value="2750"/>
<entry name="3000" value="3000"/>
<entry name="3500" value="3500"/>
<entry name="4000" value="4000"/>
<entry name="4500" value="4500"/>
<entry name="5000" value="5000"/>
</enum>
<event name="scale" since="2">
<description summary="advertise available scales for the output">
The scale event describes an available scale for the output.
The event is sent when binding to the output object and there
will always be one scale, the current scale. The event is sent
again if an output changes scale, for the scale that is now
current. In other words, the current scale is always the last
scale that was received with the current flag set.
</description>
<arg name="flags" type="uint" enum="scale_property" summary="bitfield of scale flags"/>
<arg name="scale" type="uint" enum="scale_factor" summary="output scale"/>
</event>
<!-- Version 5 additions -->
<enum name="connection_type">
<entry name="unknown" value="0"/>
<entry name="internal" value="1"/>
</enum>
<event name="connection" since="5">
<description summary="advertise connection for the output">
The connection event describes how the output is connected.
The event is sent when binding to the output object.
</description>
<arg name="connection" type="uint" enum="connection_type" summary="output connection"/>
</event>
<event name="device_scale_factor" since="5">
<description summary="advertise device scale factor for the output">
This event describes the device specific scale factor for the output.
The device specific scale factor is not expected the change during
the lifetime of the output. And it is not limited to an integer value
like the scale factor provided by wl_output interface. The exact
contents scale used by the compositor can be determined by combining
this device scale factor with the current output scale.
The event is sent when binding to the output object.
</description>
<arg name="scale" type="uint" enum="scale_factor" summary="output device scale factor"/>
</event>
</interface>
</protocol>

182
sommelier/protocol/drm.xml Normal file
View File

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="drm">
<copyright>
Copyright © 2008-2011 Kristian Høgsberg
Copyright © 2010-2011 Intel Corporation
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that\n the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<!-- drm support. This object is created by the server and published
using the display's global event. -->
<interface name="wl_drm" version="2">
<enum name="error">
<entry name="authenticate_fail" value="0"/>
<entry name="invalid_format" value="1"/>
<entry name="invalid_name" value="2"/>
</enum>
<enum name="format">
<!-- The drm format codes match the #defines in drm_fourcc.h.
The formats actually supported by the compositor will be
reported by the format event. -->
<entry name="c8" value="0x20203843"/>
<entry name="rgb332" value="0x38424752"/>
<entry name="bgr233" value="0x38524742"/>
<entry name="xrgb4444" value="0x32315258"/>
<entry name="xbgr4444" value="0x32314258"/>
<entry name="rgbx4444" value="0x32315852"/>
<entry name="bgrx4444" value="0x32315842"/>
<entry name="argb4444" value="0x32315241"/>
<entry name="abgr4444" value="0x32314241"/>
<entry name="rgba4444" value="0x32314152"/>
<entry name="bgra4444" value="0x32314142"/>
<entry name="xrgb1555" value="0x35315258"/>
<entry name="xbgr1555" value="0x35314258"/>
<entry name="rgbx5551" value="0x35315852"/>
<entry name="bgrx5551" value="0x35315842"/>
<entry name="argb1555" value="0x35315241"/>
<entry name="abgr1555" value="0x35314241"/>
<entry name="rgba5551" value="0x35314152"/>
<entry name="bgra5551" value="0x35314142"/>
<entry name="rgb565" value="0x36314752"/>
<entry name="bgr565" value="0x36314742"/>
<entry name="rgb888" value="0x34324752"/>
<entry name="bgr888" value="0x34324742"/>
<entry name="xrgb8888" value="0x34325258"/>
<entry name="xbgr8888" value="0x34324258"/>
<entry name="rgbx8888" value="0x34325852"/>
<entry name="bgrx8888" value="0x34325842"/>
<entry name="argb8888" value="0x34325241"/>
<entry name="abgr8888" value="0x34324241"/>
<entry name="rgba8888" value="0x34324152"/>
<entry name="bgra8888" value="0x34324142"/>
<entry name="xrgb2101010" value="0x30335258"/>
<entry name="xbgr2101010" value="0x30334258"/>
<entry name="rgbx1010102" value="0x30335852"/>
<entry name="bgrx1010102" value="0x30335842"/>
<entry name="argb2101010" value="0x30335241"/>
<entry name="abgr2101010" value="0x30334241"/>
<entry name="rgba1010102" value="0x30334152"/>
<entry name="bgra1010102" value="0x30334142"/>
<entry name="yuyv" value="0x56595559"/>
<entry name="yvyu" value="0x55595659"/>
<entry name="uyvy" value="0x59565955"/>
<entry name="vyuy" value="0x59555956"/>
<entry name="ayuv" value="0x56555941"/>
<entry name="nv12" value="0x3231564e"/>
<entry name="nv21" value="0x3132564e"/>
<entry name="nv16" value="0x3631564e"/>
<entry name="nv61" value="0x3136564e"/>
<entry name="yuv410" value="0x39565559"/>
<entry name="yvu410" value="0x39555659"/>
<entry name="yuv411" value="0x31315559"/>
<entry name="yvu411" value="0x31315659"/>
<entry name="yuv420" value="0x32315559"/>
<entry name="yvu420" value="0x32315659"/>
<entry name="yuv422" value="0x36315559"/>
<entry name="yvu422" value="0x36315659"/>
<entry name="yuv444" value="0x34325559"/>
<entry name="yvu444" value="0x34325659"/>
</enum>
<!-- Call this request with the magic received from drmGetMagic().
It will be passed on to the drmAuthMagic() or
DRIAuthConnection() call. This authentication must be
completed before create_buffer could be used. -->
<request name="authenticate">
<arg name="id" type="uint"/>
</request>
<!-- Create a wayland buffer for the named DRM buffer. The DRM
surface must have a name using the flink ioctl -->
<request name="create_buffer">
<arg name="id" type="new_id" interface="wl_buffer"/>
<arg name="name" type="uint"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
<arg name="stride" type="uint"/>
<arg name="format" type="uint"/>
</request>
<!-- Create a wayland buffer for the named DRM buffer. The DRM
surface must have a name using the flink ioctl -->
<request name="create_planar_buffer">
<arg name="id" type="new_id" interface="wl_buffer"/>
<arg name="name" type="uint"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
<arg name="format" type="uint"/>
<arg name="offset0" type="int"/>
<arg name="stride0" type="int"/>
<arg name="offset1" type="int"/>
<arg name="stride1" type="int"/>
<arg name="offset2" type="int"/>
<arg name="stride2" type="int"/>
</request>
<!-- Create a wayland buffer for the prime fd. Use for regular and planar
buffers. Pass 0 for offset and stride for unused planes. -->
<request name="create_prime_buffer" since="2">
<arg name="id" type="new_id" interface="wl_buffer"/>
<arg name="name" type="fd"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
<arg name="format" type="uint"/>
<arg name="offset0" type="int"/>
<arg name="stride0" type="int"/>
<arg name="offset1" type="int"/>
<arg name="stride1" type="int"/>
<arg name="offset2" type="int"/>
<arg name="stride2" type="int"/>
</request>
<!-- Notification of the path of the drm device which is used by
the server. The client should use this device for creating
local buffers. Only buffers created from this device should
be be passed to the server using this drm object's
create_buffer request. -->
<event name="device">
<arg name="name" type="string"/>
</event>
<event name="format">
<arg name="format" type="uint"/>
</event>
<!-- Raised if the authenticate request succeeded -->
<event name="authenticated"/>
<enum name="capability" since="2">
<description summary="wl_drm capability bitmask">
Bitmask of capabilities.
</description>
<entry name="prime" value="1" summary="wl_drm prime available"/>
</enum>
<event name="capabilities">
<arg name="value" type="uint"/>
</event>
</interface>
</protocol>

View File

@ -0,0 +1,61 @@
<protocol name="gtk">
<interface name="gtk_shell1" version="1">
<description summary="gtk specific extensions">
gtk_shell is a protocol extension providing additional features for
clients implementing it.
</description>
<enum name="capability">
<entry name="global_app_menu" value="1"/>
<entry name="global_menu_bar" value="2"/>
<entry name="desktop_icons" value="3"/>
</enum>
<event name="capabilities">
<arg name="capabilities" type="uint"/>
</event>
<request name="get_gtk_surface">
<arg name="gtk_surface" type="new_id" interface="gtk_surface1"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="set_startup_id">
<arg name="startup_id" type="string" allow-null="true"/>
</request>
<request name="system_bell">
<arg name="surface" type="object" interface="gtk_surface1" allow-null="true"/>
</request>
</interface>
<interface name="gtk_surface1" version="1">
<request name="set_dbus_properties">
<arg name="application_id" type="string" allow-null="true"/>
<arg name="app_menu_path" type="string" allow-null="true"/>
<arg name="menubar_path" type="string" allow-null="true"/>
<arg name="window_object_path" type="string" allow-null="true"/>
<arg name="application_object_path" type="string" allow-null="true"/>
<arg name="unique_bus_name" type="string" allow-null="true"/>
</request>
<request name="set_modal"/>
<request name="unset_modal"/>
<request name="present">
<arg name="time" type="uint"/>
</request>
<!-- Version 2 additions -->
<enum name="state">
<entry name="tiled" value="1"/>
</enum>
<event name="configure">
<arg name="states" type="array"/>
</event>
</interface>
</protocol>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="keyboard_extension_unstable_v1">
<copyright>
Copyright 2017 The Chromium Authors.
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.
</copyright>
<interface name="zcr_keyboard_extension_v1" version="1">
<description summary="extends wl_keyboard with ack_key events">
Allows a wl_keyboard to send ack_key requests for each key event of
the keyboard to the server.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding uinterface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and uinterface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<enum name="error">
<entry name="extended_keyboard_exists" value="0"
summary="the keyboard already has an extended_keyboard object associated"/>
</enum>
<request name="get_extended_keyboard">
<description summary="get extended_keyboard for a keyboard">
Create extended_keyboard object.
See zcr_extended_keyboard interface for details.
If the given wl_keyboard object already has a extended_keyboard object
associated, the extended_keyboard_exists protocol error is raised.
</description>
<arg name="id" type="new_id" interface="zcr_extended_keyboard_v1"/>
<arg name="keyboard" type="object" interface="wl_keyboard"/>
</request>
</interface>
<interface name="zcr_extended_keyboard_v1" version="1">
<description summary="extension of wl_keyboard protocol">
The zcr_extended_keyboard_v1 interface extends the wl_keyboard interface
with requests to notify whether sent key events are handled or not by
the client.
</description>
<request name="destroy" type="destructor">
<description summary="destroy extended_keyboard object"/>
</request>
<enum name="handled_state">
<description summary="whether a key event is handled by client or not"/>
<entry name="not_handled" value="0"/>
<entry name="handled" value="1"/>
</enum>
<request name="ack_key">
<description summary="acknowledge a key event"/>
<arg name="serial" type="uint"/>
<arg name="handled" type="uint" enum="handled_state"/>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,348 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="linux_dmabuf_unstable_v1">
<copyright>
Copyright © 2014, 2015 Collabora, Ltd.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="zwp_linux_dmabuf_v1" version="3">
<description summary="factory for creating dmabuf-based wl_buffers">
Following the interfaces from:
https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt
and the Linux DRM sub-system's AddFb2 ioctl.
This interface offers ways to create generic dmabuf-based
wl_buffers. Immediately after a client binds to this interface,
the set of supported formats and format modifiers is sent with
'format' and 'modifier' events.
The following are required from clients:
- Clients must ensure that either all data in the dma-buf is
coherent for all subsequent read access or that coherency is
correctly handled by the underlying kernel-side dma-buf
implementation.
- Don't make any more attachments after sending the buffer to the
compositor. Making more attachments later increases the risk of
the compositor not being able to use (re-import) an existing
dmabuf-based wl_buffer.
The underlying graphics stack must ensure the following:
- The dmabuf file descriptors relayed to the server will stay valid
for the whole lifetime of the wl_buffer. This means the server may
at any time use those fds to import the dmabuf into any kernel
sub-system that might accept it.
To create a wl_buffer from one or more dmabufs, a client creates a
zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params
request. All planes required by the intended format are added with
the 'add' request. Finally, a 'create' or 'create_immed' request is
issued, which has the following outcome depending on the import success.
The 'create' request,
- on success, triggers a 'created' event which provides the final
wl_buffer to the client.
- on failure, triggers a 'failed' event to convey that the server
cannot use the dmabufs received from the client.
For the 'create_immed' request,
- on success, the server immediately imports the added dmabufs to
create a wl_buffer. No event is sent from the server in this case.
- on failure, the server can choose to either:
- terminate the client by raising a fatal error.
- mark the wl_buffer as failed, and send a 'failed' event to the
client. If the client uses a failed wl_buffer as an argument to any
request, the behaviour is compositor implementation-defined.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<request name="destroy" type="destructor">
<description summary="unbind the factory">
Objects created through this interface, especially wl_buffers, will
remain valid.
</description>
</request>
<request name="create_params">
<description summary="create a temporary object for buffer parameters">
This temporary object is used to collect multiple dmabuf handles into
a single batch to create a wl_buffer. It can only be used once and
should be destroyed after a 'created' or 'failed' event has been
received.
</description>
<arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1"
summary="the new temporary"/>
</request>
<event name="format">
<description summary="supported buffer format">
This event advertises one buffer format that the server supports.
All the supported formats are advertised once when the client
binds to this interface. A roundtrip after binding guarantees
that the client has received all supported formats.
For the definition of the format codes, see the
zwp_linux_buffer_params_v1::create request.
Warning: the 'format' event is likely to be deprecated and replaced
with the 'modifier' event introduced in zwp_linux_dmabuf_v1
version 3, described below. Please refrain from using the information
received from this event.
</description>
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
</event>
<event name="modifier" since="3">
<description summary="supported buffer format modifier">
This event advertises the formats that the server supports, along with
the modifiers supported for each format. All the supported modifiers
for all the supported formats are advertised once when the client
binds to this interface. A roundtrip after binding guarantees that
the client has received all supported format-modifier pairs.
For the definition of the format and modifier codes, see the
zwp_linux_buffer_params_v1::create request.
</description>
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
<arg name="modifier_hi" type="uint"
summary="high 32 bits of layout modifier"/>
<arg name="modifier_lo" type="uint"
summary="low 32 bits of layout modifier"/>
</event>
</interface>
<interface name="zwp_linux_buffer_params_v1" version="3">
<description summary="parameters for creating a dmabuf-based wl_buffer">
This temporary object is a collection of dmabufs and other
parameters that together form a single logical buffer. The temporary
object may eventually create one wl_buffer unless cancelled by
destroying it before requesting 'create'.
Single-planar formats only require one dmabuf, however
multi-planar formats may require more than one dmabuf. For all
formats, an 'add' request must be called once per plane (even if the
underlying dmabuf fd is identical).
You must use consecutive plane indices ('plane_idx' argument for 'add')
from zero to the number of planes used by the drm_fourcc format code.
All planes required by the format must be given exactly once, but can
be given in any order. Each plane index can be set only once.
</description>
<enum name="error">
<entry name="already_used" value="0"
summary="the dmabuf_batch object has already been used to create a wl_buffer"/>
<entry name="plane_idx" value="1"
summary="plane index out of bounds"/>
<entry name="plane_set" value="2"
summary="the plane index was already set"/>
<entry name="incomplete" value="3"
summary="missing or too many planes to create a buffer"/>
<entry name="invalid_format" value="4"
summary="format not supported"/>
<entry name="invalid_dimensions" value="5"
summary="invalid width or height"/>
<entry name="out_of_bounds" value="6"
summary="offset + stride * height goes out of dmabuf bounds"/>
<entry name="invalid_wl_buffer" value="7"
summary="invalid wl_buffer resulted from importing dmabufs via
the create_immed request on given buffer_params"/>
</enum>
<request name="destroy" type="destructor">
<description summary="delete this object, used or not">
Cleans up the temporary data sent to the server for dmabuf-based
wl_buffer creation.
</description>
</request>
<request name="add">
<description summary="add a dmabuf to the temporary set">
This request adds one dmabuf to the set in this
zwp_linux_buffer_params_v1.
The 64-bit unsigned value combined from modifier_hi and modifier_lo
is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the
fb modifier, which is defined in drm_mode.h of Linux UAPI.
This is an opaque token. Drivers use this token to express tiling,
compression, etc. driver-specific modifications to the base format
defined by the DRM fourcc code.
This request raises the PLANE_IDX error if plane_idx is too large.
The error PLANE_SET is raised if attempting to set a plane that
was already set.
</description>
<arg name="fd" type="fd" summary="dmabuf fd"/>
<arg name="plane_idx" type="uint" summary="plane index"/>
<arg name="offset" type="uint" summary="offset in bytes"/>
<arg name="stride" type="uint" summary="stride in bytes"/>
<arg name="modifier_hi" type="uint"
summary="high 32 bits of layout modifier"/>
<arg name="modifier_lo" type="uint"
summary="low 32 bits of layout modifier"/>
</request>
<enum name="flags">
<entry name="y_invert" value="1" summary="contents are y-inverted"/>
<entry name="interlaced" value="2" summary="content is interlaced"/>
<entry name="bottom_first" value="4" summary="bottom field first"/>
</enum>
<request name="create">
<description summary="create a wl_buffer from the given dmabufs">
This asks for creation of a wl_buffer from the added dmabuf
buffers. The wl_buffer is not created immediately but returned via
the 'created' event if the dmabuf sharing succeeds. The sharing
may fail at runtime for reasons a client cannot predict, in
which case the 'failed' event is triggered.
The 'format' argument is a DRM_FORMAT code, as defined by the
libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the
authoritative source on how the format codes should work.
The 'flags' is a bitfield of the flags defined in enum "flags".
'y_invert' means the that the image needs to be y-flipped.
Flag 'interlaced' means that the frame in the buffer is not
progressive as usual, but interlaced. An interlaced buffer as
supported here must always contain both top and bottom fields.
The top field always begins on the first pixel row. The temporal
ordering between the two fields is top field first, unless
'bottom_first' is specified. It is undefined whether 'bottom_first'
is ignored if 'interlaced' is not set.
This protocol does not convey any information about field rate,
duration, or timing, other than the relative ordering between the
two fields in one buffer. A compositor may have to estimate the
intended field rate from the incoming buffer rate. It is undefined
whether the time of receiving wl_surface.commit with a new buffer
attached, applying the wl_surface state, wl_surface.frame callback
trigger, presentation, or any other point in the compositor cycle
is used to measure the frame or field times. There is no support
for detecting missed or late frames/fields/buffers either, and
there is no support whatsoever for cooperating with interlaced
compositor output.
The composited image quality resulting from the use of interlaced
buffers is explicitly undefined. A compositor may use elaborate
hardware features or software to deinterlace and create progressive
output frames from a sequence of interlaced input buffers, or it
may produce substandard image quality. However, compositors that
cannot guarantee reasonable image quality in all cases are recommended
to just reject all interlaced buffers.
Any argument errors, including non-positive width or height,
mismatch between the number of planes and the format, bad
format, bad offset or stride, may be indicated by fatal protocol
errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS,
OUT_OF_BOUNDS.
Dmabuf import errors in the server that are not obvious client
bugs are returned via the 'failed' event as non-fatal. This
allows attempting dmabuf sharing and falling back in the client
if it fails.
This request can be sent only once in the object's lifetime, after
which the only legal request is destroy. This object should be
destroyed after issuing a 'create' request. Attempting to use this
object after issuing 'create' raises ALREADY_USED protocol error.
It is not mandatory to issue 'create'. If a client wants to
cancel the buffer creation, it can just destroy this object.
</description>
<arg name="width" type="int" summary="base plane width in pixels"/>
<arg name="height" type="int" summary="base plane height in pixels"/>
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
<arg name="flags" type="uint" summary="see enum flags"/>
</request>
<event name="created">
<description summary="buffer creation succeeded">
This event indicates that the attempted buffer creation was
successful. It provides the new wl_buffer referencing the dmabuf(s).
Upon receiving this event, the client should destroy the
zlinux_dmabuf_params object.
</description>
<arg name="buffer" type="new_id" interface="wl_buffer"
summary="the newly created wl_buffer"/>
</event>
<event name="failed">
<description summary="buffer creation failed">
This event indicates that the attempted buffer creation has
failed. It usually means that one of the dmabuf constraints
has not been fulfilled.
Upon receiving this event, the client should destroy the
zlinux_buffer_params object.
</description>
</event>
<request name="create_immed" since="2">
<description summary="immediately create a wl_buffer from the given
dmabufs">
This asks for immediate creation of a wl_buffer by importing the
added dmabufs.
In case of import success, no event is sent from the server, and the
wl_buffer is ready to be used by the client.
Upon import failure, either of the following may happen, as seen fit
by the implementation:
- the client is terminated with one of the following fatal protocol
errors:
- INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS,
in case of argument errors such as mismatch between the number
of planes and the format, bad format, non-positive width or
height, or bad offset or stride.
- INVALID_WL_BUFFER, in case the cause for failure is unknown or
plaform specific.
- the server creates an invalid wl_buffer, marks it as failed and
sends a 'failed' event to the client. The result of using this
invalid wl_buffer as an argument in any request by the client is
defined by the compositor implementation.
This takes the same arguments as a 'create' request, and obeys the
same restrictions.
</description>
<arg name="buffer_id" type="new_id" interface="wl_buffer"
summary="id for the newly created wl_buffer"/>
<arg name="width" type="int" summary="base plane width in pixels"/>
<arg name="height" type="int" summary="base plane height in pixels"/>
<arg name="format" type="uint" summary="DRM_FORMAT code"/>
<arg name="flags" type="uint" summary="see enum flags"/>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="relative_pointer_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
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.
</copyright>
<description summary="protocol for relative pointer motion events">
This protocol specifies a set of interfaces used for making clients able to
receive relative pointer events not obstructed by barriers (such as the
monitor edge or other pointer barriers).
To start receiving relative pointer events, a client must first bind the
global interface "wp_relative_pointer_manager" which, if a compositor
supports relative pointer motion events, is exposed by the registry. After
having created the relative pointer manager proxy object, the client uses
it to create the actual relative pointer object using the
"get_relative_pointer" request given a wl_pointer. The relative pointer
motion events will then, when applicable, be transmitted via the proxy of
the newly created relative pointer object. See the documentation of the
relative pointer interface for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_relative_pointer_manager_v1" version="1">
<description summary="get relative pointer objects">
A global interface used for getting the relative pointer object for a
given pointer.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the relative pointer manager object">
Used by the client to notify the server that it will no longer use this
relative pointer manager object.
</description>
</request>
<request name="get_relative_pointer">
<description summary="get a relative pointer object">
Create a relative pointer interface given a wl_pointer object. See the
wp_relative_pointer interface for more details.
</description>
<arg name="id" type="new_id" interface="zwp_relative_pointer_v1"/>
<arg name="pointer" type="object" interface="wl_pointer"/>
</request>
</interface>
<interface name="zwp_relative_pointer_v1" version="1">
<description summary="relative pointer object">
A wp_relative_pointer object is an extension to the wl_pointer interface
used for emitting relative pointer events. It shares the same focus as
wl_pointer objects of the same seat and will only emit events when it has
focus.
</description>
<request name="destroy" type="destructor">
<description summary="release the relative pointer object"/>
</request>
<event name="relative_motion">
<description summary="relative pointer motion">
Relative x/y pointer motion from the pointer of the seat associated with
this object.
A relative motion is in the same dimension as regular wl_pointer motion
events, except they do not represent an absolute position. For example,
moving a pointer from (x, y) to (x', y') would have the equivalent
relative motion (x' - x, y' - y). If a pointer motion caused the
absolute pointer position to be clipped by for example the edge of the
monitor, the relative motion is unaffected by the clipping and will
represent the unclipped motion.
This event also contains non-accelerated motion deltas. The
non-accelerated delta is, when applicable, the regular pointer motion
delta as it was before having applied motion acceleration and other
transformations such as normalization.
Note that the non-accelerated delta does not represent 'raw' events as
they were read from some device. Pointer motion acceleration is device-
and configuration-specific and non-accelerated deltas and accelerated
deltas may have the same value on some devices.
Relative motions are not coupled to wl_pointer.motion events, and can be
sent in combination with such events, but also independently. There may
also be scenarios where wl_pointer.motion is sent, but there is no
relative motion. The order of an absolute and relative motion event
originating from the same physical motion is not guaranteed.
If the client needs button events or focus state, it can receive them
from a wl_pointer object of the same seat that the wp_relative_pointer
object is associated with.
</description>
<arg name="utime_hi" type="uint"
summary="high 32 bits of a 64 bit timestamp with microsecond granularity"/>
<arg name="utime_lo" type="uint"
summary="low 32 bits of a 64 bit timestamp with microsecond granularity"/>
<arg name="dx" type="fixed"
summary="the x component of the motion vector"/>
<arg name="dy" type="fixed"
summary="the y component of the motion vector"/>
<arg name="dx_unaccel" type="fixed"
summary="the x component of the unaccelerated motion vector"/>
<arg name="dy_unaccel" type="fixed"
summary="the y component of the unaccelerated motion vector"/>
</event>
</interface>
</protocol>

View File

@ -0,0 +1,385 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="text_input_unstable_v1">
<copyright>
Copyright © 2012, 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.
</copyright>
<interface name="zwp_text_input_v1" version="1">
<description summary="text input">
An object used for text input. Adds support for text input and input
methods to applications. A text_input object is created from a
wl_text_input_manager and corresponds typically to a text entry in an
application.
Requests are used to activate/deactivate the text_input object and set
state information like surrounding and selected text or the content type.
The information about entered text is sent to the text_input object via
the pre-edit and commit events. Using this interface removes the need
for applications to directly process hardware key events and compose text
out of them.
Text is generally UTF-8 encoded, indices and lengths are in bytes.
Serials are used to synchronize the state between the text input and
an input method. New serials are sent by the text input in the
commit_state request and are used by the input method to indicate
the known text input state in events like preedit_string, commit_string,
and keysym. The text input can then ignore events from the input method
which are based on an outdated state (for example after a reset).
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<request name="activate">
<description summary="request activation">
Requests the text_input object to be activated (typically when the
text entry gets focus).
The seat argument is a wl_seat which maintains the focus for this
activation. The surface argument is a wl_surface assigned to the
text_input object and tracked for focus lost. The enter event
is emitted on successful activation.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="deactivate">
<description summary="request deactivation">
Requests the text_input object to be deactivated (typically when the
text entry lost focus). The seat argument is a wl_seat which was used
for activation.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
</request>
<request name="show_input_panel">
<description summary="show input panels">
Requests input panels (virtual keyboard) to show.
</description>
</request>
<request name="hide_input_panel">
<description summary="hide input panels">
Requests input panels (virtual keyboard) to hide.
</description>
</request>
<request name="reset">
<description summary="reset">
Should be called by an editor widget when the input state should be
reset, for example after the text was changed outside of the normal
input method flow.
</description>
</request>
<request name="set_surrounding_text">
<description summary="sets the surrounding text">
Sets the plain surrounding text around the input position. Text is
UTF-8 encoded. Cursor is the byte offset within the
surrounding text. Anchor is the byte offset of the
selection anchor within the surrounding text. If there is no selected
text anchor, then it is the same as cursor.
</description>
<arg name="text" type="string"/>
<arg name="cursor" type="uint"/>
<arg name="anchor" type="uint"/>
</request>
<enum name="content_hint">
<description summary="content hint">
Content hint is a bitmask to allow to modify the behavior of the text
input.
</description>
<entry name="none" value="0x0" summary="no special behaviour"/>
<entry name="default" value="0x7" summary="auto completion, correction and capitalization"/>
<entry name="password" value="0xc0" summary="hidden and sensitive text"/>
<entry name="auto_completion" value="0x1" summary="suggest word completions"/>
<entry name="auto_correction" value="0x2" summary="suggest word corrections"/>
<entry name="auto_capitalization" value="0x4" summary="switch to uppercase letters at the start of a sentence"/>
<entry name="lowercase" value="0x8" summary="prefer lowercase letters"/>
<entry name="uppercase" value="0x10" summary="prefer uppercase letters"/>
<entry name="titlecase" value="0x20" summary="prefer casing for titles and headings (can be language dependent)"/>
<entry name="hidden_text" value="0x40" summary="characters should be hidden"/>
<entry name="sensitive_data" value="0x80" summary="typed text should not be stored"/>
<entry name="latin" value="0x100" summary="just latin characters should be entered"/>
<entry name="multiline" value="0x200" summary="the text input is multiline"/>
</enum>
<enum name="content_purpose">
<description summary="content purpose">
The content purpose allows to specify the primary purpose of a text
input.
This allows an input method to show special purpose input panels with
extra characters or to disallow some characters.
</description>
<entry name="normal" value="0" summary="default input, allowing all characters"/>
<entry name="alpha" value="1" summary="allow only alphabetic characters"/>
<entry name="digits" value="2" summary="allow only digits"/>
<entry name="number" value="3" summary="input a number (including decimal separator and sign)"/>
<entry name="phone" value="4" summary="input a phone number"/>
<entry name="url" value="5" summary="input an URL"/>
<entry name="email" value="6" summary="input an email address"/>
<entry name="name" value="7" summary="input a name of a person"/>
<entry name="password" value="8" summary="input a password (combine with password or sensitive_data hint)"/>
<entry name="date" value="9" summary="input a date"/>
<entry name="time" value="10" summary="input a time"/>
<entry name="datetime" value="11" summary="input a date and time"/>
<entry name="terminal" value="12" summary="input for a terminal"/>
</enum>
<request name="set_content_type">
<description summary="set content purpose and hint">
Sets the content purpose and content hint. While the purpose is the
basic purpose of an input field, the hint flags allow to modify some
of the behavior.
When no content type is explicitly set, a normal content purpose with
default hints (auto completion, auto correction, auto capitalization)
should be assumed.
</description>
<arg name="hint" type="uint"/>
<arg name="purpose" type="uint"/>
</request>
<request name="set_cursor_rectangle">
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</request>
<request name="set_preferred_language">
<description summary="sets preferred language">
Sets a specific language. This allows for example a virtual keyboard to
show a language specific layout. The "language" argument is an RFC-3066
format language tag.
It could be used for example in a word processor to indicate the
language of the currently edited document or in an instant message
application which tracks languages of contacts.
</description>
<arg name="language" type="string"/>
</request>
<request name="commit_state">
<arg name="serial" type="uint" summary="used to identify the known state"/>
</request>
<request name="invoke_action">
<arg name="button" type="uint"/>
<arg name="index" type="uint"/>
</request>
<event name="enter">
<description summary="enter event">
Notify the text_input object when it received focus. Typically in
response to an activate request.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
</event>
<event name="leave">
<description summary="leave event">
Notify the text_input object when it lost focus. Either in response
to a deactivate request or when the assigned surface lost focus or was
destroyed.
</description>
</event>
<event name="modifiers_map">
<description summary="modifiers map">
Transfer an array of 0-terminated modifier names. The position in
the array is the index of the modifier as used in the modifiers
bitmask in the keysym event.
</description>
<arg name="map" type="array"/>
</event>
<event name="input_panel_state">
<description summary="state of the input panel">
Notify when the visibility state of the input panel changed.
</description>
<arg name="state" type="uint"/>
</event>
<event name="preedit_string">
<description summary="pre-edit">
Notify when a new composing text (pre-edit) should be set around the
current cursor position. Any previously set composing text should
be removed.
The commit text can be used to replace the preedit text on reset
(for example on unfocus).
The text input should also handle all preedit_style and preedit_cursor
events occurring directly before preedit_string.
</description>
<arg name="serial" type="uint" summary="serial of the latest known text input state"/>
<arg name="text" type="string"/>
<arg name="commit" type="string"/>
</event>
<enum name="preedit_style">
<entry name="default" value="0" summary="default style for composing text"/>
<entry name="none" value="1" summary="style should be the same as in non-composing text"/>
<entry name="active" value="2"/>
<entry name="inactive" value="3"/>
<entry name="highlight" value="4"/>
<entry name="underline" value="5"/>
<entry name="selection" value="6"/>
<entry name="incorrect" value="7"/>
</enum>
<event name="preedit_styling">
<description summary="pre-edit styling">
Sets styling information on composing text. The style is applied for
length bytes from index relative to the beginning of the composing
text (as byte offset). Multiple styles can
be applied to a composing text by sending multiple preedit_styling
events.
This event is handled as part of a following preedit_string event.
</description>
<arg name="index" type="uint"/>
<arg name="length" type="uint"/>
<arg name="style" type="uint"/>
</event>
<event name="preedit_cursor">
<description summary="pre-edit cursor">
Sets the cursor position inside the composing text (as byte
offset) relative to the start of the composing text. When index is a
negative number no cursor is shown.
This event is handled as part of a following preedit_string event.
</description>
<arg name="index" type="int"/>
</event>
<event name="commit_string">
<description summary="commit">
Notify when text should be inserted into the editor widget. The text to
commit could be either just a single character after a key press or the
result of some composing (pre-edit). It could also be an empty text
when some text should be removed (see delete_surrounding_text) or when
the input cursor should be moved (see cursor_position).
Any previously set composing text should be removed.
</description>
<arg name="serial" type="uint" summary="serial of the latest known text input state"/>
<arg name="text" type="string"/>
</event>
<event name="cursor_position">
<description summary="set cursor to new position">
Notify when the cursor or anchor position should be modified.
This event should be handled as part of a following commit_string
event.
</description>
<arg name="index" type="int"/>
<arg name="anchor" type="int"/>
</event>
<event name="delete_surrounding_text">
<description summary="delete surrounding text">
Notify when the text around the current cursor position should be
deleted.
Index is relative to the current cursor (in bytes).
Length is the length of deleted text (in bytes).
This event should be handled as part of a following commit_string
event.
</description>
<arg name="index" type="int"/>
<arg name="length" type="uint"/>
</event>
<event name="keysym">
<description summary="keysym">
Notify when a key event was sent. Key events should not be used
for normal text input operations, which should be done with
commit_string, delete_surrounding_text, etc. The key event follows
the wl_keyboard key event convention. Sym is an XKB keysym, state a
wl_keyboard key_state. Modifiers are a mask for effective modifiers
(where the modifier indices are set by the modifiers_map event)
</description>
<arg name="serial" type="uint" summary="serial of the latest known text input state"/>
<arg name="time" type="uint"/>
<arg name="sym" type="uint"/>
<arg name="state" type="uint"/>
<arg name="modifiers" type="uint"/>
</event>
<event name="language">
<description summary="language">
Sets the language of the input text. The "language" argument is an
RFC-3066 format language tag.
</description>
<arg name="serial" type="uint" summary="serial of the latest known text input state"/>
<arg name="language" type="string"/>
</event>
<enum name="text_direction">
<entry name="auto" value="0" summary="automatic text direction based on text and language"/>
<entry name="ltr" value="1" summary="left-to-right"/>
<entry name="rtl" value="2" summary="right-to-left"/>
</enum>
<event name="text_direction">
<description summary="text direction">
Sets the text direction of input text.
It is mainly needed for showing an input cursor on the correct side of
the editor when there is no input done yet and making sure neutral
direction text is laid out properly.
</description>
<arg name="serial" type="uint" summary="serial of the latest known text input state"/>
<arg name="direction" type="uint"/>
</event>
</interface>
<interface name="zwp_text_input_manager_v1" version="1">
<description summary="text input manager">
A factory for text_input objects. This object is a global singleton.
</description>
<request name="create_text_input">
<description summary="create text input">
Creates a new text_input object.
</description>
<arg name="id" type="new_id" interface="zwp_text_input_v1"/>
</request>
</interface>
</protocol>

View File

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="viewporter">
<copyright>
Copyright © 2013-2016 Collabora, Ltd.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<interface name="wp_viewporter" version="1">
<description summary="surface cropping and scaling">
The global interface exposing surface cropping and scaling
capabilities is used to instantiate an interface extension for a
wl_surface object. This extended interface will then allow
cropping and scaling the surface contents, effectively
disconnecting the direct relationship between the buffer and the
surface size.
</description>
<request name="destroy" type="destructor">
<description summary="unbind from the cropping and scaling interface">
Informs the server that the client will not be using this
protocol object anymore. This does not affect any other objects,
wp_viewport objects included.
</description>
</request>
<enum name="error">
<entry name="viewport_exists" value="0"
summary="the surface already has a viewport object associated"/>
</enum>
<request name="get_viewport">
<description summary="extend surface interface for crop and scale">
Instantiate an interface extension for the given wl_surface to
crop and scale its content. If the given wl_surface already has
a wp_viewport object associated, the viewport_exists
protocol error is raised.
</description>
<arg name="id" type="new_id" interface="wp_viewport"
summary="the new viewport interface id"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface"/>
</request>
</interface>
<interface name="wp_viewport" version="1">
<description summary="crop and scale interface to a wl_surface">
An additional interface to a wl_surface object, which allows the
client to specify the cropping and scaling of the surface
contents.
This interface works with two concepts: the source rectangle (src_x,
src_y, src_width, src_height), and the destination size (dst_width,
dst_height). The contents of the source rectangle are scaled to the
destination size, and content outside the source rectangle is ignored.
This state is double-buffered, and is applied on the next
wl_surface.commit.
The two parts of crop and scale state are independent: the source
rectangle, and the destination size. Initially both are unset, that
is, no scaling is applied. The whole of the current wl_buffer is
used as the source, and the surface size is as defined in
wl_surface.attach.
If the destination size is set, it causes the surface size to become
dst_width, dst_height. The source (rectangle) is scaled to exactly
this size. This overrides whatever the attached wl_buffer size is,
unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
has no content and therefore no size. Otherwise, the size is always
at least 1x1 in surface local coordinates.
If the source rectangle is set, it defines what area of the wl_buffer is
taken as the source. If the source rectangle is set and the destination
size is not set, then src_width and src_height must be integers, and the
surface size becomes the source rectangle size. This results in cropping
without scaling. If src_width or src_height are not integers and
destination size is not set, the bad_size protocol error is raised when
the surface state is applied.
The coordinate transformations from buffer pixel coordinates up to
the surface-local coordinates happen in the following order:
1. buffer_transform (wl_surface.set_buffer_transform)
2. buffer_scale (wl_surface.set_buffer_scale)
3. crop and scale (wp_viewport.set*)
This means, that the source rectangle coordinates of crop and scale
are given in the coordinates after the buffer transform and scale,
i.e. in the coordinates that would be the surface-local coordinates
if the crop and scale was not applied.
If src_x or src_y are negative, the bad_value protocol error is raised.
Otherwise, if the source rectangle is partially or completely outside of
the non-NULL wl_buffer, then the out_of_buffer protocol error is raised
when the surface state is applied. A NULL wl_buffer does not raise the
out_of_buffer error.
The x, y arguments of wl_surface.attach are applied as normal to
the surface. They indicate how many pixels to remove from the
surface size from the left and the top. In other words, they are
still in the surface-local coordinate system, just like dst_width
and dst_height are.
If the wl_surface associated with the wp_viewport is destroyed,
all wp_viewport requests except 'destroy' raise the protocol error
no_surface.
If the wp_viewport object is destroyed, the crop and scale
state is removed from the wl_surface. The change will be applied
on the next wl_surface.commit.
</description>
<request name="destroy" type="destructor">
<description summary="remove scaling and cropping from the surface">
The associated wl_surface's crop and scale state is removed.
The change is applied on the next wl_surface.commit.
</description>
</request>
<enum name="error">
<entry name="bad_value" value="0"
summary="negative or zero values in width or height"/>
<entry name="bad_size" value="1"
summary="destination size is not integer"/>
<entry name="out_of_buffer" value="2"
summary="source rectangle extends outside of the content area"/>
<entry name="no_surface" value="3"
summary="the wl_surface was destroyed"/>
</enum>
<request name="set_source">
<description summary="set the source rectangle for cropping">
Set the source rectangle of the associated wl_surface. See
wp_viewport for the description, and relation to the wl_buffer
size.
If all of x, y, width and height are -1.0, the source rectangle is
unset instead. Any other set of values where width or height are zero
or negative, or x or y are negative, raise the bad_value protocol
error.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
</description>
<arg name="x" type="fixed" summary="source rectangle x"/>
<arg name="y" type="fixed" summary="source rectangle y"/>
<arg name="width" type="fixed" summary="source rectangle width"/>
<arg name="height" type="fixed" summary="source rectangle height"/>
</request>
<request name="set_destination">
<description summary="set the surface size for scaling">
Set the destination size of the associated wl_surface. See
wp_viewport for the description, and relation to the wl_buffer
size.
If width is -1 and height is -1, the destination size is unset
instead. Any other pair of values for width and height that
contains zero or negative values raises the bad_value protocol
error.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
</description>
<arg name="width" type="int" summary="surface width"/>
<arg name="height" type="int" summary="surface height"/>
</request>
</interface>
</protocol>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,894 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <errno.h>
#include <gbm.h>
#include <limits.h>
#include <linux/virtwl.h>
#include <pixman.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-util.h>
#include "drm-server-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h"
#define MIN_SIZE (INT_MIN / 10)
#define MAX_SIZE (INT_MAX / 10)
#define DMA_BUF_SYNC_READ (1 << 0)
#define DMA_BUF_SYNC_WRITE (2 << 0)
#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE)
#define DMA_BUF_SYNC_START (0 << 2)
#define DMA_BUF_SYNC_END (1 << 2)
#define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
struct sl_host_region {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_region* proxy;
};
struct sl_host_compositor {
struct sl_compositor* compositor;
struct wl_resource* resource;
struct wl_compositor* proxy;
};
struct sl_output_buffer {
struct wl_list link;
uint32_t width;
uint32_t height;
uint32_t format;
struct wl_buffer* internal;
struct sl_mmap* mmap;
struct pixman_region32 damage;
struct sl_host_surface* surface;
};
struct dma_buf_sync {
__u64 flags;
};
static void sl_dmabuf_sync(int fd, __u64 flags) {
struct dma_buf_sync sync = {0};
int rv;
sync.flags = flags;
do {
rv = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
} while (rv == -1 && errno == EINTR);
}
static void sl_dmabuf_begin_write(int fd) {
sl_dmabuf_sync(fd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE);
}
static void sl_dmabuf_end_write(int fd) {
sl_dmabuf_sync(fd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE);
}
static void sl_virtwl_dmabuf_sync(int fd, __u32 flags) {
struct virtwl_ioctl_dmabuf_sync sync = {0};
int rv;
sync.flags = flags;
rv = ioctl(fd, VIRTWL_IOCTL_DMABUF_SYNC, &sync);
assert(!rv);
UNUSED(rv);
}
static void sl_virtwl_dmabuf_begin_write(int fd) {
sl_virtwl_dmabuf_sync(fd, DMA_BUF_SYNC_START | DMA_BUF_SYNC_WRITE);
}
static void sl_virtwl_dmabuf_end_write(int fd) {
sl_virtwl_dmabuf_sync(fd, DMA_BUF_SYNC_END | DMA_BUF_SYNC_WRITE);
}
static uint32_t sl_gbm_format_for_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_NV12:
return GBM_FORMAT_NV12;
case WL_SHM_FORMAT_RGB565:
return GBM_FORMAT_RGB565;
case WL_SHM_FORMAT_ARGB8888:
return GBM_FORMAT_ARGB8888;
case WL_SHM_FORMAT_ABGR8888:
return GBM_FORMAT_ABGR8888;
case WL_SHM_FORMAT_XRGB8888:
return GBM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_XBGR8888:
return GBM_FORMAT_XBGR8888;
}
assert(0);
return 0;
}
static uint32_t sl_drm_format_for_shm_format(int format) {
switch (format) {
case WL_SHM_FORMAT_NV12:
return WL_DRM_FORMAT_NV12;
case WL_SHM_FORMAT_RGB565:
return WL_DRM_FORMAT_RGB565;
case WL_SHM_FORMAT_ARGB8888:
return WL_DRM_FORMAT_ARGB8888;
case WL_SHM_FORMAT_ABGR8888:
return WL_DRM_FORMAT_ABGR8888;
case WL_SHM_FORMAT_XRGB8888:
return WL_DRM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_XBGR8888:
return WL_DRM_FORMAT_XBGR8888;
}
assert(0);
return 0;
}
static void sl_output_buffer_destroy(struct sl_output_buffer* buffer) {
wl_buffer_destroy(buffer->internal);
sl_mmap_unref(buffer->mmap);
pixman_region32_fini(&buffer->damage);
wl_list_remove(&buffer->link);
free(buffer);
}
static void sl_output_buffer_release(void* data, struct wl_buffer* buffer) {
struct sl_output_buffer* output_buffer = wl_buffer_get_user_data(buffer);
struct sl_host_surface* host_surface = output_buffer->surface;
wl_list_remove(&output_buffer->link);
wl_list_insert(&host_surface->released_buffers, &output_buffer->link);
}
static const struct wl_buffer_listener sl_output_buffer_listener = {
sl_output_buffer_release};
static void sl_host_surface_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_host_surface_attach(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* buffer_resource,
int32_t x,
int32_t y) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
struct sl_host_buffer* host_buffer =
buffer_resource ? wl_resource_get_user_data(buffer_resource) : NULL;
struct wl_buffer* buffer_proxy = NULL;
struct sl_window* window;
double scale = host->ctx->scale;
host->current_buffer = NULL;
if (host->contents_shm_mmap) {
sl_mmap_unref(host->contents_shm_mmap);
host->contents_shm_mmap = NULL;
}
if (host_buffer) {
host->contents_width = host_buffer->width;
host->contents_height = host_buffer->height;
buffer_proxy = host_buffer->proxy;
if (host_buffer->shm_mmap)
host->contents_shm_mmap = sl_mmap_ref(host_buffer->shm_mmap);
}
if (host->contents_shm_mmap) {
while (!wl_list_empty(&host->released_buffers)) {
host->current_buffer = wl_container_of(host->released_buffers.next,
host->current_buffer, link);
if (host->current_buffer->width == host_buffer->width &&
host->current_buffer->height == host_buffer->height &&
host->current_buffer->format == host_buffer->shm_format) {
break;
}
sl_output_buffer_destroy(host->current_buffer);
host->current_buffer = NULL;
}
// Allocate new output buffer.
if (!host->current_buffer) {
size_t width = host_buffer->width;
size_t height = host_buffer->height;
uint32_t shm_format = host_buffer->shm_format;
size_t bpp = sl_shm_bpp_for_shm_format(shm_format);
size_t num_planes = sl_shm_num_planes_for_shm_format(shm_format);
host->current_buffer = malloc(sizeof(struct sl_output_buffer));
assert(host->current_buffer);
wl_list_insert(&host->released_buffers, &host->current_buffer->link);
host->current_buffer->width = width;
host->current_buffer->height = height;
host->current_buffer->format = shm_format;
host->current_buffer->surface = host;
pixman_region32_init_rect(&host->current_buffer->damage, 0, 0, MAX_SIZE,
MAX_SIZE);
switch (host->ctx->shm_driver) {
case SHM_DRIVER_DMABUF: {
struct zwp_linux_buffer_params_v1* buffer_params;
struct gbm_bo* bo;
int stride0;
int fd;
bo = gbm_bo_create(host->ctx->gbm, width, height,
sl_gbm_format_for_shm_format(shm_format),
GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
stride0 = gbm_bo_get_stride(bo);
fd = gbm_bo_get_fd(bo);
buffer_params = zwp_linux_dmabuf_v1_create_params(
host->ctx->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, fd, 0, 0, stride0, 0,
0);
host->current_buffer->internal =
zwp_linux_buffer_params_v1_create_immed(
buffer_params, width, height,
sl_drm_format_for_shm_format(shm_format), 0);
zwp_linux_buffer_params_v1_destroy(buffer_params);
host->current_buffer->mmap = sl_mmap_create(
fd, height * stride0, bpp, 1, 0, stride0, 0, 0, 1, 0);
host->current_buffer->mmap->begin_write = sl_dmabuf_begin_write;
host->current_buffer->mmap->end_write = sl_dmabuf_end_write;
gbm_bo_destroy(bo);
} break;
case SHM_DRIVER_VIRTWL: {
size_t size = host_buffer->shm_mmap->size;
struct virtwl_ioctl_new ioctl_new = {.type = VIRTWL_IOCTL_NEW_ALLOC,
.fd = -1,
.flags = 0,
.size = size};
struct wl_shm_pool* pool;
int rv;
rv = ioctl(host->ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &ioctl_new);
assert(rv == 0);
UNUSED(rv);
pool =
wl_shm_create_pool(host->ctx->shm->internal, ioctl_new.fd, size);
host->current_buffer->internal = wl_shm_pool_create_buffer(
pool, 0, width, height, host_buffer->shm_mmap->stride[0],
shm_format);
wl_shm_pool_destroy(pool);
host->current_buffer->mmap = sl_mmap_create(
ioctl_new.fd, size, bpp, num_planes, 0,
host_buffer->shm_mmap->stride[0],
host_buffer->shm_mmap->offset[1] -
host_buffer->shm_mmap->offset[0],
host_buffer->shm_mmap->stride[1], host_buffer->shm_mmap->y_ss[0],
host_buffer->shm_mmap->y_ss[1]);
} break;
case SHM_DRIVER_VIRTWL_DMABUF: {
uint32_t drm_format = sl_drm_format_for_shm_format(shm_format);
struct virtwl_ioctl_new ioctl_new = {
.type = VIRTWL_IOCTL_NEW_DMABUF,
.fd = -1,
.flags = 0,
.dmabuf = {
.width = width, .height = height, .format = drm_format}};
struct zwp_linux_buffer_params_v1* buffer_params;
size_t size;
int rv;
rv = ioctl(host->ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &ioctl_new);
if (rv) {
fprintf(stderr, "error: virtwl dmabuf allocation failed: %s\n",
strerror(errno));
_exit(EXIT_FAILURE);
}
size = ioctl_new.dmabuf.stride0 * height;
buffer_params = zwp_linux_dmabuf_v1_create_params(
host->ctx->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, ioctl_new.fd, 0,
ioctl_new.dmabuf.offset0,
ioctl_new.dmabuf.stride0, 0, 0);
if (num_planes > 1) {
zwp_linux_buffer_params_v1_add(buffer_params, ioctl_new.fd, 1,
ioctl_new.dmabuf.offset1,
ioctl_new.dmabuf.stride1, 0, 0);
size = MAX(size, ioctl_new.dmabuf.offset1 +
ioctl_new.dmabuf.stride1 * height /
host_buffer->shm_mmap->y_ss[1]);
}
host->current_buffer->internal =
zwp_linux_buffer_params_v1_create_immed(buffer_params, width,
height, drm_format, 0);
zwp_linux_buffer_params_v1_destroy(buffer_params);
host->current_buffer->mmap = sl_mmap_create(
ioctl_new.fd, size, bpp, num_planes, ioctl_new.dmabuf.offset0,
ioctl_new.dmabuf.stride0, ioctl_new.dmabuf.offset1,
ioctl_new.dmabuf.stride1, host_buffer->shm_mmap->y_ss[0],
host_buffer->shm_mmap->y_ss[1]);
host->current_buffer->mmap->begin_write =
sl_virtwl_dmabuf_begin_write;
host->current_buffer->mmap->end_write = sl_virtwl_dmabuf_end_write;
} break;
}
assert(host->current_buffer->internal);
assert(host->current_buffer->mmap);
wl_buffer_set_user_data(host->current_buffer->internal,
host->current_buffer);
wl_buffer_add_listener(host->current_buffer->internal,
&sl_output_buffer_listener, host->current_buffer);
}
}
x /= scale;
y /= scale;
// TODO(davidriley): This should be done in the commit.
if (host_buffer && host_buffer->sync_point) {
host_buffer->sync_point->sync(host->ctx, host_buffer->sync_point);
}
if (host->current_buffer) {
assert(host->current_buffer->internal);
wl_surface_attach(host->proxy, host->current_buffer->internal, x, y);
} else {
wl_surface_attach(host->proxy, buffer_proxy, x, y);
}
wl_list_for_each(window, &host->ctx->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
while (sl_process_pending_configure_acks(window, host))
continue;
break;
}
}
}
static void sl_host_surface_damage(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
struct sl_output_buffer* buffer;
int64_t x1, y1, x2, y2;
wl_list_for_each(buffer, &host->busy_buffers, link) {
pixman_region32_union_rect(&buffer->damage, &buffer->damage, x, y, width,
height);
}
wl_list_for_each(buffer, &host->released_buffers, link) {
pixman_region32_union_rect(&buffer->damage, &buffer->damage, x, y, width,
height);
}
x1 = x;
y1 = y;
x2 = x1 + width;
y2 = y1 + height;
// Enclosing rect after scaling and outset by one pixel to account for
// potential filtering.
x1 = MAX(MIN_SIZE, x1 - 1) / scale;
y1 = MAX(MIN_SIZE, y1 - 1) / scale;
x2 = ceil(MIN(x2 + 1, MAX_SIZE) / scale);
y2 = ceil(MIN(y2 + 1, MAX_SIZE) / scale);
wl_surface_damage(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void sl_frame_callback_done(void* data,
struct wl_callback* callback,
uint32_t time) {
struct sl_host_callback* host = wl_callback_get_user_data(callback);
wl_callback_send_done(host->resource, time);
wl_resource_destroy(host->resource);
}
static const struct wl_callback_listener sl_frame_callback_listener = {
sl_frame_callback_done};
static void sl_host_callback_destroy(struct wl_resource* resource) {
struct sl_host_callback* host = wl_resource_get_user_data(resource);
wl_callback_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_host_surface_frame(struct wl_client* client,
struct wl_resource* resource,
uint32_t callback) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
struct sl_host_callback* host_callback;
host_callback = malloc(sizeof(*host_callback));
assert(host_callback);
host_callback->resource =
wl_resource_create(client, &wl_callback_interface, 1, callback);
wl_resource_set_implementation(host_callback->resource, NULL, host_callback,
sl_host_callback_destroy);
host_callback->proxy = wl_surface_frame(host->proxy);
wl_callback_set_user_data(host_callback->proxy, host_callback);
wl_callback_add_listener(host_callback->proxy, &sl_frame_callback_listener,
host_callback);
}
static void sl_host_surface_set_opaque_region(
struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* region_resource) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
struct sl_host_region* host_region =
region_resource ? wl_resource_get_user_data(region_resource) : NULL;
wl_surface_set_opaque_region(host->proxy,
host_region ? host_region->proxy : NULL);
}
static void sl_host_surface_set_input_region(
struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* region_resource) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
struct sl_host_region* host_region =
region_resource ? wl_resource_get_user_data(region_resource) : NULL;
wl_surface_set_input_region(host->proxy,
host_region ? host_region->proxy : NULL);
}
static void sl_host_surface_commit(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
struct sl_viewport* viewport = NULL;
struct sl_window* window;
if (!wl_list_empty(&host->contents_viewport))
viewport = wl_container_of(host->contents_viewport.next, viewport, link);
if (host->contents_shm_mmap) {
uint8_t* src_addr = host->contents_shm_mmap->addr;
uint8_t* dst_addr = host->current_buffer->mmap->addr;
size_t* src_offset = host->contents_shm_mmap->offset;
size_t* dst_offset = host->current_buffer->mmap->offset;
size_t* src_stride = host->contents_shm_mmap->stride;
size_t* dst_stride = host->current_buffer->mmap->stride;
size_t* y_ss = host->contents_shm_mmap->y_ss;
size_t bpp = host->contents_shm_mmap->bpp;
size_t num_planes = host->contents_shm_mmap->num_planes;
double contents_scale_x = host->contents_scale;
double contents_scale_y = host->contents_scale;
double contents_offset_x = 0.0;
double contents_offset_y = 0.0;
pixman_box32_t* rect;
int n;
// Determine scale and offset for damage based on current viewport.
if (viewport) {
double contents_width = host->contents_width;
double contents_height = host->contents_height;
if (viewport->src_x >= 0 && viewport->src_y >= 0) {
contents_offset_x = wl_fixed_to_double(viewport->src_x);
contents_offset_y = wl_fixed_to_double(viewport->src_y);
}
if (viewport->dst_width > 0 && viewport->dst_height > 0) {
contents_scale_x *= contents_width / viewport->dst_width;
contents_scale_y *= contents_height / viewport->dst_height;
// Take source rectangle into account when both destionation size and
// source rectangle are set. If only source rectangle is set, then
// it determines the surface size so it can be ignored.
if (viewport->src_width >= 0 && viewport->src_height >= 0) {
contents_scale_x *=
wl_fixed_to_double(viewport->src_width) / contents_width;
contents_scale_y *=
wl_fixed_to_double(viewport->src_height) / contents_height;
}
}
}
if (host->current_buffer->mmap->begin_write)
host->current_buffer->mmap->begin_write(host->current_buffer->mmap->fd);
rect = pixman_region32_rectangles(&host->current_buffer->damage, &n);
while (n--) {
int32_t x1, y1, x2, y2;
// Enclosing rect after applying scale and offset.
x1 = rect->x1 * contents_scale_x + contents_offset_x;
y1 = rect->y1 * contents_scale_y + contents_offset_y;
x2 = rect->x2 * contents_scale_x + contents_offset_x + 0.5;
y2 = rect->y2 * contents_scale_y + contents_offset_y + 0.5;
x1 = MAX(0, x1);
y1 = MAX(0, y1);
x2 = MIN(host->contents_width, x2);
y2 = MIN(host->contents_height, y2);
if (x1 < x2 && y1 < y2) {
size_t i;
for (i = 0; i < num_planes; ++i) {
uint8_t* src_base = src_addr + src_offset[i];
uint8_t* dst_base = dst_addr + dst_offset[i];
uint8_t* src = src_base + y1 * src_stride[i] + x1 * bpp;
uint8_t* dst = dst_base + y1 * dst_stride[i] + x1 * bpp;
int32_t width = x2 - x1;
int32_t height = (y2 - y1) / y_ss[i];
size_t bytes = width * bpp;
while (height--) {
memcpy(dst, src, bytes);
dst += dst_stride[i];
src += src_stride[i];
}
}
}
++rect;
}
if (host->current_buffer->mmap->end_write)
host->current_buffer->mmap->end_write(host->current_buffer->mmap->fd);
pixman_region32_clear(&host->current_buffer->damage);
wl_list_remove(&host->current_buffer->link);
wl_list_insert(&host->busy_buffers, &host->current_buffer->link);
}
if (host->contents_width && host->contents_height) {
double scale = host->ctx->scale * host->contents_scale;
if (host->viewport) {
int width = host->contents_width;
int height = host->contents_height;
// We need to take the client's viewport into account while still
// making sure our scale is accounted for.
if (viewport) {
if (viewport->src_x >= 0 && viewport->src_y >= 0 &&
viewport->src_width >= 0 && viewport->src_height >= 0) {
wp_viewport_set_source(host->viewport, viewport->src_x,
viewport->src_y, viewport->src_width,
viewport->src_height);
// If the source rectangle is set and the destination size is not
// set, then src_width and src_height should be integers, and the
// surface size becomes the source rectangle size.
width = wl_fixed_to_int(viewport->src_width);
height = wl_fixed_to_int(viewport->src_height);
}
// Use destination size as surface size when set.
if (viewport->dst_width >= 0 && viewport->dst_height >= 0) {
width = viewport->dst_width;
height = viewport->dst_height;
}
}
wp_viewport_set_destination(host->viewport, ceil(width / scale),
ceil(height / scale));
} else {
wl_surface_set_buffer_scale(host->proxy, scale);
}
}
// No need to defer client commits if surface has a role. E.g. is a cursor
// or shell surface.
if (host->has_role) {
wl_surface_commit(host->proxy);
// GTK determines the scale based on the output the surface has entered.
// If the surface has not entered any output, then have it enter the
// internal output. TODO(reveman): Remove this when surface-output tracking
// has been implemented in Chrome.
if (!host->has_output) {
struct sl_host_output* output;
wl_list_for_each(output, &host->ctx->host_outputs, link) {
if (output->internal) {
wl_surface_send_enter(host->resource, output->resource);
host->has_output = 1;
break;
}
}
}
} else {
// Commit if surface is associated with a window. Otherwise, defer
// commit until window is created.
wl_list_for_each(window, &host->ctx->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
if (window->xdg_surface) {
wl_surface_commit(host->proxy);
if (host->contents_width && host->contents_height)
window->realized = 1;
}
break;
}
}
}
if (host->contents_shm_mmap) {
if (host->contents_shm_mmap->buffer_resource)
wl_buffer_send_release(host->contents_shm_mmap->buffer_resource);
sl_mmap_unref(host->contents_shm_mmap);
host->contents_shm_mmap = NULL;
}
}
static void sl_host_surface_set_buffer_transform(struct wl_client* client,
struct wl_resource* resource,
int32_t transform) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
wl_surface_set_buffer_transform(host->proxy, transform);
}
static void sl_host_surface_set_buffer_scale(struct wl_client* client,
struct wl_resource* resource,
int32_t scale) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
host->contents_scale = scale;
}
static void sl_host_surface_damage_buffer(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
assert(0);
}
static const struct wl_surface_interface sl_surface_implementation = {
sl_host_surface_destroy,
sl_host_surface_attach,
sl_host_surface_damage,
sl_host_surface_frame,
sl_host_surface_set_opaque_region,
sl_host_surface_set_input_region,
sl_host_surface_commit,
sl_host_surface_set_buffer_transform,
sl_host_surface_set_buffer_scale,
sl_host_surface_damage_buffer};
static void sl_destroy_host_surface(struct wl_resource* resource) {
struct sl_host_surface* host = wl_resource_get_user_data(resource);
struct sl_window *window, *surface_window = NULL;
struct sl_output_buffer* buffer;
wl_list_for_each(window, &host->ctx->windows, link) {
if (window->host_surface_id == wl_resource_get_id(resource)) {
surface_window = window;
break;
}
}
if (surface_window) {
surface_window->host_surface_id = 0;
sl_window_update(surface_window);
}
if (host->contents_shm_mmap)
sl_mmap_unref(host->contents_shm_mmap);
while (!wl_list_empty(&host->released_buffers)) {
buffer = wl_container_of(host->released_buffers.next, buffer, link);
sl_output_buffer_destroy(buffer);
}
while (!wl_list_empty(&host->busy_buffers)) {
buffer = wl_container_of(host->busy_buffers.next, buffer, link);
sl_output_buffer_destroy(buffer);
}
while (!wl_list_empty(&host->contents_viewport))
wl_list_remove(host->contents_viewport.next);
if (host->viewport)
wp_viewport_destroy(host->viewport);
wl_surface_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_surface_enter(void* data,
struct wl_surface* surface,
struct wl_output* output) {
struct sl_host_surface* host = wl_surface_get_user_data(surface);
struct sl_host_output* host_output = wl_output_get_user_data(output);
wl_surface_send_enter(host->resource, host_output->resource);
host->has_output = 1;
}
static void sl_surface_leave(void* data,
struct wl_surface* surface,
struct wl_output* output) {
struct sl_host_surface* host = wl_surface_get_user_data(surface);
struct sl_host_output* host_output = wl_output_get_user_data(output);
wl_surface_send_leave(host->resource, host_output->resource);
}
static const struct wl_surface_listener sl_surface_listener = {
sl_surface_enter, sl_surface_leave};
static void sl_region_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_region_add(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_region* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
wl_region_add(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void sl_region_subtract(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_region* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
wl_region_subtract(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static const struct wl_region_interface sl_region_implementation = {
sl_region_destroy, sl_region_add, sl_region_subtract};
static void sl_destroy_host_region(struct wl_resource* resource) {
struct sl_host_region* host = wl_resource_get_user_data(resource);
wl_region_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_compositor_create_host_surface(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_compositor* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_surface;
struct sl_window *window, *unpaired_window = NULL;
host_surface = malloc(sizeof(*host_surface));
assert(host_surface);
host_surface->ctx = host->compositor->ctx;
host_surface->contents_width = 0;
host_surface->contents_height = 0;
host_surface->contents_scale = 1;
wl_list_init(&host_surface->contents_viewport);
host_surface->contents_shm_mmap = NULL;
host_surface->has_role = 0;
host_surface->has_output = 0;
host_surface->last_event_serial = 0;
host_surface->current_buffer = NULL;
wl_list_init(&host_surface->released_buffers);
wl_list_init(&host_surface->busy_buffers);
host_surface->resource = wl_resource_create(
client, &wl_surface_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_surface->resource,
&sl_surface_implementation, host_surface,
sl_destroy_host_surface);
host_surface->proxy = wl_compositor_create_surface(host->proxy);
wl_surface_set_user_data(host_surface->proxy, host_surface);
wl_surface_add_listener(host_surface->proxy, &sl_surface_listener,
host_surface);
host_surface->viewport = NULL;
if (host_surface->ctx->viewporter) {
host_surface->viewport = wp_viewporter_get_viewport(
host_surface->ctx->viewporter->internal, host_surface->proxy);
}
wl_list_for_each(window, &host->compositor->ctx->unpaired_windows, link) {
if (window->host_surface_id == id) {
unpaired_window = window;
break;
}
}
if (unpaired_window)
sl_window_update(window);
}
static void sl_compositor_create_host_region(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_compositor* host = wl_resource_get_user_data(resource);
struct sl_host_region* host_region;
host_region = malloc(sizeof(*host_region));
assert(host_region);
host_region->ctx = host->compositor->ctx;
host_region->resource = wl_resource_create(
client, &wl_region_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_region->resource,
&sl_region_implementation, host_region,
sl_destroy_host_region);
host_region->proxy = wl_compositor_create_region(host->proxy);
wl_region_set_user_data(host_region->proxy, host_region);
}
static const struct wl_compositor_interface sl_compositor_implementation = {
sl_compositor_create_host_surface, sl_compositor_create_host_region};
static void sl_destroy_host_compositor(struct wl_resource* resource) {
struct sl_host_compositor* host = wl_resource_get_user_data(resource);
wl_compositor_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_compositor(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_compositor* host;
host = malloc(sizeof(*host));
assert(host);
host->compositor = ctx->compositor;
host->resource =
wl_resource_create(client, &wl_compositor_interface,
MIN(version, ctx->compositor->version), id);
wl_resource_set_implementation(host->resource, &sl_compositor_implementation,
host, sl_destroy_host_compositor);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
ctx->compositor->id, &wl_compositor_interface,
ctx->compositor->version);
wl_compositor_set_user_data(host->proxy, host);
}
struct sl_global* sl_compositor_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wl_compositor_interface,
ctx->compositor->version, ctx,
sl_bind_host_compositor);
}

View File

@ -0,0 +1,580 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/virtwl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <wayland-client.h>
struct sl_host_data_device_manager {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_data_device_manager* proxy;
};
struct sl_host_data_device {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_data_device* proxy;
};
struct sl_host_data_source {
struct wl_resource* resource;
struct wl_data_source* proxy;
};
struct sl_host_data_offer {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_data_offer* proxy;
};
struct sl_data_transfer {
int read_fd;
int write_fd;
size_t offset;
size_t bytes_left;
uint8_t data[4096];
struct wl_event_source* read_event_source;
struct wl_event_source* write_event_source;
};
static void sl_data_transfer_destroy(struct sl_data_transfer* transfer) {
assert(transfer->read_event_source);
wl_event_source_remove(transfer->read_event_source);
assert(transfer->write_event_source);
wl_event_source_remove(transfer->write_event_source);
close(transfer->read_fd);
close(transfer->write_fd);
free(transfer);
}
static int sl_handle_data_transfer_read(int fd, uint32_t mask, void* data) {
struct sl_data_transfer* transfer = (struct sl_data_transfer*)data;
if ((mask & WL_EVENT_READABLE) == 0) {
assert(mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR));
// Epoll (and therefore wl_event_loop) will notify listeners of errors and
// hangups even if all other events are disabled. Therefore, at this point,
// we don't know whether we didn't get a readable event because the fd has
// been exhausted, or because we aren't in the reading state and weren't
// listening for one. We can make the distinction be checking if there's any
// data left in the buffer. If there is, then we are just waiting for the
// writing to finish, otherwise end the transfer.
// In the case of an error, where there is not likely to be any more data to
// read, we still want to wait for any data we did get to be written out.
if (!transfer->bytes_left) {
sl_data_transfer_destroy(transfer);
}
return 0;
}
// At this point we must be in the reading state.
assert(!transfer->bytes_left);
transfer->bytes_left =
read(transfer->read_fd, transfer->data, sizeof(transfer->data));
if (transfer->bytes_left > 0) {
transfer->offset = 0;
// There may still be data to read from the event source, but we have no
// room in our buffer so move to the writing state.
wl_event_source_fd_update(transfer->read_event_source, 0);
wl_event_source_fd_update(transfer->write_event_source, WL_EVENT_WRITABLE);
} else {
// On a read error or EOF, end the transfer.
sl_data_transfer_destroy(transfer);
}
return 0;
}
static int sl_handle_data_transfer_write(int fd, uint32_t mask, void* data) {
struct sl_data_transfer* transfer = (struct sl_data_transfer*)data;
int rv;
// If we receive a HANGUP or ERROR event on the write source then there is no
// point in continuing the transfer. We could still read more data, but we
// couldn't send it to the recipient, so just destroy the transfer now.
if ((mask & WL_EVENT_WRITABLE) == 0) {
assert(mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR));
sl_data_transfer_destroy(transfer);
return 0;
}
// At this point we must be in the writing state.
assert(transfer->bytes_left);
rv = write(transfer->write_fd, transfer->data + transfer->offset,
transfer->bytes_left);
if (rv < 0) {
// On a write error, end the transfer.
sl_data_transfer_destroy(transfer);
} else {
assert(rv <= transfer->bytes_left);
transfer->bytes_left -= rv;
transfer->offset += rv;
}
if (!transfer->bytes_left) {
// If all data has been written, move back to the reading state.
wl_event_source_fd_update(transfer->write_event_source, 0);
wl_event_source_fd_update(transfer->read_event_source, WL_EVENT_READABLE);
}
// If there is still data left, continue in the writing state.
return 0;
}
static void sl_data_transfer_create(struct wl_event_loop* event_loop,
int read_fd,
int write_fd) {
struct sl_data_transfer* transfer;
int flags;
int rv;
flags = fcntl(write_fd, F_GETFL, 0);
rv = fcntl(write_fd, F_SETFL, flags | O_NONBLOCK);
assert(!rv);
UNUSED(rv);
// Start out the transfer in the reading state.
transfer = malloc(sizeof(*transfer));
assert(transfer);
transfer->read_fd = read_fd;
transfer->write_fd = write_fd;
transfer->offset = 0;
transfer->bytes_left = 0;
transfer->read_event_source =
wl_event_loop_add_fd(event_loop, read_fd, WL_EVENT_READABLE,
sl_handle_data_transfer_read, transfer);
transfer->write_event_source = wl_event_loop_add_fd(
event_loop, write_fd, 0, sl_handle_data_transfer_write, transfer);
}
static void sl_data_offer_accept(struct wl_client* client,
struct wl_resource* resource,
uint32_t serial,
const char* mime_type) {
struct sl_host_data_offer* host = wl_resource_get_user_data(resource);
wl_data_offer_accept(host->proxy, serial, mime_type);
}
static void sl_data_offer_receive(struct wl_client* client,
struct wl_resource* resource,
const char* mime_type,
int32_t fd) {
struct sl_host_data_offer* host = wl_resource_get_user_data(resource);
switch (host->ctx->data_driver) {
case DATA_DRIVER_VIRTWL: {
struct virtwl_ioctl_new new_pipe = {
.type = VIRTWL_IOCTL_NEW_PIPE_READ,
.fd = -1,
.flags = 0,
.size = 0,
};
int rv;
rv = ioctl(host->ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &new_pipe);
if (rv) {
fprintf(stderr, "error: failed to create virtwl pipe: %s\n",
strerror(errno));
close(fd);
return;
}
sl_data_transfer_create(
wl_display_get_event_loop(host->ctx->host_display), new_pipe.fd, fd);
wl_data_offer_receive(host->proxy, mime_type, new_pipe.fd);
} break;
case DATA_DRIVER_NOOP:
wl_data_offer_receive(host->proxy, mime_type, fd);
close(fd);
break;
}
}
static void sl_data_offer_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_data_offer_finish(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_data_offer* host = wl_resource_get_user_data(resource);
wl_data_offer_finish(host->proxy);
}
static void sl_data_offer_set_actions(struct wl_client* client,
struct wl_resource* resource,
uint32_t dnd_actions,
uint32_t preferred_action) {
struct sl_host_data_offer* host = wl_resource_get_user_data(resource);
wl_data_offer_set_actions(host->proxy, dnd_actions, preferred_action);
}
static const struct wl_data_offer_interface sl_data_offer_implementation = {
sl_data_offer_accept, sl_data_offer_receive, sl_data_offer_destroy,
sl_data_offer_finish, sl_data_offer_set_actions};
static void sl_data_offer_offer(void* data,
struct wl_data_offer* data_offer,
const char* mime_type) {
struct sl_host_data_offer* host = wl_data_offer_get_user_data(data_offer);
wl_data_offer_send_offer(host->resource, mime_type);
}
static void sl_data_offer_source_actions(void* data,
struct wl_data_offer* data_offer,
uint32_t source_actions) {
struct sl_host_data_offer* host = wl_data_offer_get_user_data(data_offer);
wl_data_offer_send_source_actions(host->resource, source_actions);
}
static void sl_data_offer_action(void* data,
struct wl_data_offer* data_offer,
uint32_t dnd_action) {
struct sl_host_data_offer* host = wl_data_offer_get_user_data(data_offer);
wl_data_offer_send_action(host->resource, dnd_action);
}
static const struct wl_data_offer_listener sl_data_offer_listener = {
sl_data_offer_offer, sl_data_offer_source_actions, sl_data_offer_action};
static void sl_destroy_host_data_offer(struct wl_resource* resource) {
struct sl_host_data_offer* host = wl_resource_get_user_data(resource);
wl_data_offer_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_data_source_offer(struct wl_client* client,
struct wl_resource* resource,
const char* mime_type) {
struct sl_host_data_source* host = wl_resource_get_user_data(resource);
wl_data_source_offer(host->proxy, mime_type);
}
static void sl_data_source_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_data_source_set_actions(struct wl_client* client,
struct wl_resource* resource,
uint32_t dnd_actions) {
struct sl_host_data_source* host = wl_resource_get_user_data(resource);
wl_data_source_set_actions(host->proxy, dnd_actions);
}
static const struct wl_data_source_interface sl_data_source_implementation = {
sl_data_source_offer, sl_data_source_destroy, sl_data_source_set_actions};
static void sl_data_source_target(void* data,
struct wl_data_source* data_source,
const char* mime_type) {
struct sl_host_data_source* host = wl_data_source_get_user_data(data_source);
wl_data_source_send_target(host->resource, mime_type);
}
static void sl_data_source_send(void* data,
struct wl_data_source* data_source,
const char* mime_type,
int32_t fd) {
struct sl_host_data_source* host = wl_data_source_get_user_data(data_source);
wl_data_source_send_send(host->resource, mime_type, fd);
close(fd);
}
static void sl_data_source_cancelled(void* data,
struct wl_data_source* data_source) {
struct sl_host_data_source* host = wl_data_source_get_user_data(data_source);
wl_data_source_send_cancelled(host->resource);
}
void sl_data_source_dnd_drop_performed(void* data,
struct wl_data_source* data_source) {
struct sl_host_data_source* host = wl_data_source_get_user_data(data_source);
wl_data_source_send_dnd_drop_performed(host->resource);
}
void sl_data_source_dnd_finished(void* data,
struct wl_data_source* data_source) {
struct sl_host_data_source* host = wl_data_source_get_user_data(data_source);
wl_data_source_send_dnd_finished(host->resource);
}
void sl_data_source_actions(void* data,
struct wl_data_source* data_source,
uint32_t dnd_action) {
struct sl_host_data_source* host = wl_data_source_get_user_data(data_source);
wl_data_source_send_action(host->resource, dnd_action);
}
static const struct wl_data_source_listener sl_data_source_listener = {
sl_data_source_target, sl_data_source_send,
sl_data_source_cancelled, sl_data_source_dnd_drop_performed,
sl_data_source_dnd_finished, sl_data_source_actions};
static void sl_destroy_host_data_source(struct wl_resource* resource) {
struct sl_host_data_source* host = wl_resource_get_user_data(resource);
wl_data_source_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_data_device_start_drag(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* source_resource,
struct wl_resource* origin_resource,
struct wl_resource* icon_resource,
uint32_t serial) {
struct sl_host_data_device* host = wl_resource_get_user_data(resource);
struct sl_host_data_source* host_source =
source_resource ? wl_resource_get_user_data(source_resource) : NULL;
struct sl_host_surface* host_origin =
origin_resource ? wl_resource_get_user_data(origin_resource) : NULL;
struct sl_host_surface* host_icon =
icon_resource ? wl_resource_get_user_data(icon_resource) : NULL;
host_icon->has_role = 1;
wl_data_device_start_drag(host->proxy,
host_source ? host_source->proxy : NULL,
host_origin ? host_origin->proxy : NULL,
host_icon ? host_icon->proxy : NULL, serial);
}
static void sl_data_device_set_selection(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* source_resource,
uint32_t serial) {
struct sl_host_data_device* host = wl_resource_get_user_data(resource);
struct sl_host_data_source* host_source =
source_resource ? wl_resource_get_user_data(source_resource) : NULL;
wl_data_device_set_selection(host->proxy,
host_source ? host_source->proxy : NULL, serial);
}
static void sl_data_device_release(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct wl_data_device_interface sl_data_device_implementation = {
sl_data_device_start_drag, sl_data_device_set_selection,
sl_data_device_release};
static void sl_data_device_data_offer(void* data,
struct wl_data_device* data_device,
struct wl_data_offer* data_offer) {
struct sl_host_data_device* host = wl_data_device_get_user_data(data_device);
struct sl_host_data_offer* host_data_offer;
host_data_offer = malloc(sizeof(*host_data_offer));
assert(host_data_offer);
host_data_offer->ctx = host->ctx;
host_data_offer->resource = wl_resource_create(
wl_resource_get_client(host->resource), &wl_data_offer_interface,
wl_resource_get_version(host->resource), 0);
wl_resource_set_implementation(host_data_offer->resource,
&sl_data_offer_implementation, host_data_offer,
sl_destroy_host_data_offer);
host_data_offer->proxy = data_offer;
wl_data_offer_set_user_data(host_data_offer->proxy, host_data_offer);
wl_data_offer_add_listener(host_data_offer->proxy, &sl_data_offer_listener,
host_data_offer);
wl_data_device_send_data_offer(host->resource, host_data_offer->resource);
}
static void sl_data_device_enter(void* data,
struct wl_data_device* data_device,
uint32_t serial,
struct wl_surface* surface,
wl_fixed_t x,
wl_fixed_t y,
struct wl_data_offer* data_offer) {
struct sl_host_data_device* host = wl_data_device_get_user_data(data_device);
struct sl_host_surface* host_surface = wl_surface_get_user_data(surface);
struct sl_host_data_offer* host_data_offer =
wl_data_offer_get_user_data(data_offer);
double scale = host->ctx->scale;
wl_data_device_send_enter(host->resource, serial, host_surface->resource,
wl_fixed_from_double(wl_fixed_to_double(x) * scale),
wl_fixed_from_double(wl_fixed_to_double(y) * scale),
host_data_offer->resource);
}
static void sl_data_device_leave(void* data,
struct wl_data_device* data_device) {
struct sl_host_data_device* host = wl_data_device_get_user_data(data_device);
wl_data_device_send_leave(host->resource);
}
static void sl_data_device_motion(void* data,
struct wl_data_device* data_device,
uint32_t time,
wl_fixed_t x,
wl_fixed_t y) {
struct sl_host_data_device* host = wl_data_device_get_user_data(data_device);
double scale = host->ctx->scale;
wl_data_device_send_motion(
host->resource, time, wl_fixed_from_double(wl_fixed_to_double(x) * scale),
wl_fixed_from_double(wl_fixed_to_double(y) * scale));
}
static void sl_data_device_drop(void* data,
struct wl_data_device* data_device) {
struct sl_host_data_device* host = wl_data_device_get_user_data(data_device);
wl_data_device_send_drop(host->resource);
}
static void sl_data_device_selection(void* data,
struct wl_data_device* data_device,
struct wl_data_offer* data_offer) {
struct sl_host_data_device* host = wl_data_device_get_user_data(data_device);
struct sl_host_data_offer* host_data_offer =
wl_data_offer_get_user_data(data_offer);
wl_data_device_send_selection(host->resource, host_data_offer->resource);
}
static const struct wl_data_device_listener sl_data_device_listener = {
sl_data_device_data_offer, sl_data_device_enter, sl_data_device_leave,
sl_data_device_motion, sl_data_device_drop, sl_data_device_selection};
static void sl_destroy_host_data_device(struct wl_resource* resource) {
struct sl_host_data_device* host = wl_resource_get_user_data(resource);
if (wl_data_device_get_version(host->proxy) >=
WL_DATA_DEVICE_RELEASE_SINCE_VERSION) {
wl_data_device_release(host->proxy);
} else {
wl_data_device_destroy(host->proxy);
}
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_data_device_manager_create_data_source(
struct wl_client* client, struct wl_resource* resource, uint32_t id) {
struct sl_host_data_device_manager* host =
wl_resource_get_user_data(resource);
struct sl_host_data_source* host_data_source;
host_data_source = malloc(sizeof(*host_data_source));
assert(host_data_source);
host_data_source->resource = wl_resource_create(
client, &wl_data_source_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_data_source->resource,
&sl_data_source_implementation,
host_data_source, sl_destroy_host_data_source);
host_data_source->proxy =
wl_data_device_manager_create_data_source(host->proxy);
wl_data_source_set_user_data(host_data_source->proxy, host_data_source);
wl_data_source_add_listener(host_data_source->proxy, &sl_data_source_listener,
host_data_source);
}
static void sl_data_device_manager_get_data_device(
struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* seat_resource) {
struct sl_host_data_device_manager* host =
wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat_resource);
struct sl_host_data_device* host_data_device;
host_data_device = malloc(sizeof(*host_data_device));
assert(host_data_device);
host_data_device->ctx = host->ctx;
host_data_device->resource = wl_resource_create(
client, &wl_data_device_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_data_device->resource,
&sl_data_device_implementation,
host_data_device, sl_destroy_host_data_device);
host_data_device->proxy =
wl_data_device_manager_get_data_device(host->proxy, host_seat->proxy);
wl_data_device_set_user_data(host_data_device->proxy, host_data_device);
wl_data_device_add_listener(host_data_device->proxy, &sl_data_device_listener,
host_data_device);
}
static const struct wl_data_device_manager_interface
sl_data_device_manager_implementation = {
sl_data_device_manager_create_data_source,
sl_data_device_manager_get_data_device};
static void sl_destroy_host_data_device_manager(struct wl_resource* resource) {
struct sl_host_data_device_manager* host =
wl_resource_get_user_data(resource);
wl_data_device_manager_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_data_device_manager(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_data_device_manager* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->resource =
wl_resource_create(client, &wl_data_device_manager_interface,
MIN(version, ctx->data_device_manager->version), id);
wl_resource_set_implementation(host->resource,
&sl_data_device_manager_implementation, host,
sl_destroy_host_data_device_manager);
host->proxy = wl_registry_bind(
wl_display_get_registry(ctx->display), ctx->data_device_manager->id,
&wl_data_device_manager_interface, ctx->data_device_manager->version);
wl_data_device_manager_set_user_data(host->proxy, host);
}
struct sl_global* sl_data_device_manager_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wl_data_device_manager_interface,
ctx->data_device_manager->version, ctx,
sl_bind_host_data_device_manager);
}

View File

@ -0,0 +1,126 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
static void sl_registry_bind(struct wl_client* client,
struct wl_resource* resource,
uint32_t name,
const char* interface,
uint32_t version,
uint32_t id) {
struct sl_host_registry* host = wl_resource_get_user_data(resource);
struct sl_global* global;
wl_list_for_each(global, &host->ctx->globals, link) {
if (global->name == name)
break;
}
assert(&global->link != &host->ctx->globals);
assert(version != 0);
assert(global->version >= version);
global->bind(client, global->data, version, id);
}
static const struct wl_registry_interface sl_registry_implementation = {
sl_registry_bind};
static void sl_sync_callback_done(void* data,
struct wl_callback* callback,
uint32_t serial) {
struct sl_host_callback* host = wl_callback_get_user_data(callback);
wl_callback_send_done(host->resource, serial);
wl_resource_destroy(host->resource);
}
static const struct wl_callback_listener sl_sync_callback_listener = {
sl_sync_callback_done};
static void sl_host_callback_destroy(struct wl_resource* resource) {
struct sl_host_callback* host = wl_resource_get_user_data(resource);
wl_callback_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_display_sync(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_context* ctx = wl_resource_get_user_data(resource);
struct sl_host_callback* host_callback;
host_callback = malloc(sizeof(*host_callback));
assert(host_callback);
host_callback->resource =
wl_resource_create(client, &wl_callback_interface, 1, id);
wl_resource_set_implementation(host_callback->resource, NULL, host_callback,
sl_host_callback_destroy);
host_callback->proxy = wl_display_sync(ctx->display);
wl_callback_set_user_data(host_callback->proxy, host_callback);
wl_callback_add_listener(host_callback->proxy, &sl_sync_callback_listener,
host_callback);
}
static void sl_destroy_host_registry(struct wl_resource* resource) {
struct sl_host_registry* host = wl_resource_get_user_data(resource);
wl_list_remove(&host->link);
free(host);
}
static void sl_display_get_registry(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_context* ctx = wl_resource_get_user_data(resource);
struct sl_host_registry* host_registry;
struct sl_global* global;
host_registry = malloc(sizeof(*host_registry));
assert(host_registry);
host_registry->ctx = ctx;
host_registry->resource =
wl_resource_create(client, &wl_registry_interface, 1, id);
wl_list_insert(&ctx->registries, &host_registry->link);
wl_resource_set_implementation(host_registry->resource,
&sl_registry_implementation, host_registry,
sl_destroy_host_registry);
wl_list_for_each(global, &ctx->globals, link) {
wl_resource_post_event(host_registry->resource, WL_REGISTRY_GLOBAL,
global->name, global->interface->name,
global->version);
}
}
static const struct wl_display_interface sl_display_implementation = {
sl_display_sync, sl_display_get_registry};
static enum wl_iterator_result sl_set_implementation(
struct wl_resource* resource, void* user_data) {
struct sl_context* ctx = (struct sl_context*)user_data;
if (strcmp(wl_resource_get_class(resource), "wl_display") == 0) {
wl_resource_set_implementation(resource, &sl_display_implementation, ctx,
NULL);
return WL_ITERATOR_STOP;
}
return WL_ITERATOR_CONTINUE;
}
void sl_set_display_implementation(struct sl_context* ctx) {
// Find display resource and set implementation.
wl_client_for_each_resource(ctx->client, sl_set_implementation, ctx);
}

259
sommelier/sommelier-drm.c Normal file
View File

@ -0,0 +1,259 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <gbm.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <xf86drm.h>
#include "virtgpu_drm.h"
#include "drm-server-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
struct sl_host_drm {
struct sl_context* ctx;
uint32_t version;
struct wl_resource* resource;
struct zwp_linux_dmabuf_v1* linux_dmabuf_proxy;
struct wl_callback* callback;
};
static void sl_drm_authenticate(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
wl_drm_send_authenticated(resource);
}
static void sl_drm_create_buffer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
uint32_t name,
int32_t width,
int32_t height,
uint32_t stride,
uint32_t format) {
assert(0);
}
static void sl_drm_create_planar_buffer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
uint32_t name,
int32_t width,
int32_t height,
uint32_t format,
int32_t offset0,
int32_t stride0,
int32_t offset1,
int32_t stride1,
int32_t offset2,
int32_t stride2) {
assert(0);
}
static void sl_drm_sync(struct sl_context *ctx,
struct sl_sync_point* sync_point)
{
int drm_fd = gbm_device_get_fd(ctx->gbm);
struct drm_prime_handle prime_handle;
int ret;
// First imports the prime fd to a gem handle. This will fail if this
// function was not passed a prime handle that can be imported by the drm
// device given to sommelier.
memset(&prime_handle, 0, sizeof(prime_handle));
prime_handle.fd = sync_point->fd;
ret = drmIoctl(drm_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime_handle);
if (!ret) {
struct drm_virtgpu_3d_wait wait_arg;
struct drm_gem_close gem_close;
// Then attempts to wait for GPU operations to complete. This will fail
// silently if the drm device passed to sommelier is not a virtio-gpu
// device.
memset(&wait_arg, 0, sizeof(wait_arg));
wait_arg.handle = prime_handle.handle;
drmIoctl(drm_fd, DRM_IOCTL_VIRTGPU_WAIT, &wait_arg);
// Always close the handle we imported.
memset(&gem_close, 0, sizeof(gem_close));
gem_close.handle = prime_handle.handle;
drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
}
}
static void sl_drm_create_prime_buffer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
int32_t name,
int32_t width,
int32_t height,
uint32_t format,
int32_t offset0,
int32_t stride0,
int32_t offset1,
int32_t stride1,
int32_t offset2,
int32_t stride2) {
struct sl_host_drm* host = wl_resource_get_user_data(resource);
struct zwp_linux_buffer_params_v1* buffer_params;
assert(name >= 0);
assert(!offset1);
assert(!stride1);
assert(!offset2);
assert(!stride2);
// Attempts to correct stride0 with virtio-gpu specific resource information,
// if available. Ideally mesa/gbm should have the correct stride. Remove
// after crbug.com/892242 is resolved in mesa.
int is_gpu_buffer = 0;
if (host->ctx->gbm) {
int drm_fd = gbm_device_get_fd(host->ctx->gbm);
struct drm_prime_handle prime_handle;
int ret;
// First imports the prime fd to a gem handle. This will fail if this
// function was not passed a prime handle that can be imported by the drm
// device given to sommelier.
memset(&prime_handle, 0, sizeof(prime_handle));
prime_handle.fd = name;
ret = drmIoctl(drm_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime_handle);
if (!ret) {
struct drm_virtgpu_resource_info info_arg;
struct drm_gem_close gem_close;
// Then attempts to get resource information. This will fail silently if
// the drm device passed to sommelier is not a virtio-gpu device.
memset(&info_arg, 0, sizeof(info_arg));
info_arg.bo_handle = prime_handle.handle;
ret = drmIoctl(drm_fd, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, &info_arg);
// Correct stride0 if we are able to get proper resource info.
if (!ret) {
stride0 = info_arg.stride;
is_gpu_buffer = 1;
}
// Always close the handle we imported.
memset(&gem_close, 0, sizeof(gem_close));
gem_close.handle = prime_handle.handle;
drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
}
}
buffer_params =
zwp_linux_dmabuf_v1_create_params(host->ctx->linux_dmabuf->internal);
zwp_linux_buffer_params_v1_add(buffer_params, name, 0, offset0, stride0, 0,
0);
struct sl_host_buffer* host_buffer =
sl_create_host_buffer(client, id,
zwp_linux_buffer_params_v1_create_immed(
buffer_params, width, height, format, 0),
width, height);
if (is_gpu_buffer) {
host_buffer->sync_point = sl_sync_point_create(name);
host_buffer->sync_point->sync = sl_drm_sync;
} else {
close(name);
}
zwp_linux_buffer_params_v1_destroy(buffer_params);
}
static const struct wl_drm_interface sl_drm_implementation = {
sl_drm_authenticate, sl_drm_create_buffer, sl_drm_create_planar_buffer,
sl_drm_create_prime_buffer};
static void sl_destroy_host_drm(struct wl_resource* resource) {
struct sl_host_drm* host = wl_resource_get_user_data(resource);
zwp_linux_dmabuf_v1_destroy(host->linux_dmabuf_proxy);
wl_callback_destroy(host->callback);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_drm_format(void* data,
struct zwp_linux_dmabuf_v1* linux_dmabuf,
uint32_t format) {
struct sl_host_drm* host = zwp_linux_dmabuf_v1_get_user_data(linux_dmabuf);
switch (format) {
case WL_DRM_FORMAT_RGB565:
case WL_DRM_FORMAT_ARGB8888:
case WL_DRM_FORMAT_ABGR8888:
case WL_DRM_FORMAT_XRGB8888:
case WL_DRM_FORMAT_XBGR8888:
wl_drm_send_format(host->resource, format);
default:
break;
}
}
static void sl_drm_modifier(void* data,
struct zwp_linux_dmabuf_v1* linux_dmabuf,
uint32_t format,
uint32_t modifier_hi,
uint32_t modifier_lo) {}
static const struct zwp_linux_dmabuf_v1_listener sl_linux_dmabuf_listener = {
sl_drm_format, sl_drm_modifier};
static void sl_drm_callback_done(void* data,
struct wl_callback* callback,
uint32_t serial) {
struct sl_host_drm* host = wl_callback_get_user_data(callback);
if (host->ctx->drm_device)
wl_drm_send_device(host->resource, host->ctx->drm_device);
if (host->version >= WL_DRM_CREATE_PRIME_BUFFER_SINCE_VERSION)
wl_drm_send_capabilities(host->resource, WL_DRM_CAPABILITY_PRIME);
}
static const struct wl_callback_listener sl_drm_callback_listener = {
sl_drm_callback_done};
static void sl_bind_host_drm(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_drm* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->version = MIN(version, 2);
host->resource =
wl_resource_create(client, &wl_drm_interface, host->version, id);
wl_resource_set_implementation(host->resource, &sl_drm_implementation, host,
sl_destroy_host_drm);
host->linux_dmabuf_proxy = wl_registry_bind(
wl_display_get_registry(ctx->display), ctx->linux_dmabuf->id,
&zwp_linux_dmabuf_v1_interface, ctx->linux_dmabuf->version);
zwp_linux_dmabuf_v1_set_user_data(host->linux_dmabuf_proxy, host);
zwp_linux_dmabuf_v1_add_listener(host->linux_dmabuf_proxy,
&sl_linux_dmabuf_listener, host);
host->callback = wl_display_sync(ctx->display);
wl_callback_set_user_data(host->callback, host);
wl_callback_add_listener(host->callback, &sl_drm_callback_listener, host);
}
struct sl_global* sl_drm_global_create(struct sl_context* ctx) {
assert(ctx->linux_dmabuf);
// Early out if DMABuf protocol version is not sufficient.
if (ctx->linux_dmabuf->version < 2)
return NULL;
return sl_global_create(ctx, &wl_drm_interface, 2, ctx, sl_bind_host_drm);
}

View File

@ -0,0 +1,162 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "aura-shell-client-protocol.h"
#include "gtk-shell-server-protocol.h"
struct sl_host_gtk_shell {
struct sl_aura_shell* aura_shell;
struct wl_resource* resource;
struct zaura_shell* proxy;
struct wl_callback* callback;
char* startup_id;
struct wl_list surfaces;
};
struct sl_host_gtk_surface {
struct wl_resource* resource;
struct zaura_surface* proxy;
struct wl_list link;
struct sl_aura_shell* aura_shell;
};
static void sl_gtk_surface_set_dbus_properties(
struct wl_client* client,
struct wl_resource* resource,
const char* application_id,
const char* app_menu_path,
const char* menubar_path,
const char* window_object_path,
const char* application_object_path,
const char* unique_bus_name) {
struct sl_host_gtk_surface* host = wl_resource_get_user_data(resource);
zaura_surface_set_application_id(host->proxy, application_id);
}
static void sl_gtk_surface_set_modal(struct wl_client* client,
struct wl_resource* resource) {}
static void sl_gtk_surface_unset_modal(struct wl_client* client,
struct wl_resource* resource) {}
static void sl_gtk_surface_present(struct wl_client* client,
struct wl_resource* resource,
uint32_t time) {}
static const struct gtk_surface1_interface sl_gtk_surface_implementation = {
sl_gtk_surface_set_dbus_properties, sl_gtk_surface_set_modal,
sl_gtk_surface_unset_modal, sl_gtk_surface_present};
static void sl_destroy_host_gtk_surface(struct wl_resource* resource) {
struct sl_host_gtk_surface* host = wl_resource_get_user_data(resource);
zaura_surface_destroy(host->proxy);
wl_list_remove(&host->link);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_gtk_shell_get_gtk_surface(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface_resource) {
struct sl_host_gtk_shell* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_surface =
wl_resource_get_user_data(surface_resource);
struct sl_host_gtk_surface* host_gtk_surface;
host_gtk_surface = malloc(sizeof(*host_gtk_surface));
assert(host_gtk_surface);
wl_list_insert(&host->surfaces, &host_gtk_surface->link);
host_gtk_surface->aura_shell = host->aura_shell;
host_gtk_surface->resource =
wl_resource_create(client, &gtk_surface1_interface, 1, id);
wl_resource_set_implementation(host_gtk_surface->resource,
&sl_gtk_surface_implementation,
host_gtk_surface, sl_destroy_host_gtk_surface);
host_gtk_surface->proxy =
zaura_shell_get_aura_surface(host->proxy, host_surface->proxy);
zaura_surface_set_startup_id(host_gtk_surface->proxy, host->startup_id);
}
static void sl_gtk_shell_set_startup_id(struct wl_client* client,
struct wl_resource* resource,
const char* startup_id) {
struct sl_host_gtk_shell* host = wl_resource_get_user_data(resource);
struct sl_host_gtk_surface* surface;
free(host->startup_id);
host->startup_id = startup_id ? strdup(startup_id) : NULL;
wl_list_for_each(surface, &host->surfaces, link)
zaura_surface_set_startup_id(surface->proxy, host->startup_id);
}
static void sl_gtk_shell_system_bell(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* surface_resource) {}
static const struct gtk_shell1_interface sl_gtk_shell_implementation = {
sl_gtk_shell_get_gtk_surface, sl_gtk_shell_set_startup_id,
sl_gtk_shell_system_bell};
static void sl_destroy_host_gtk_shell(struct wl_resource* resource) {
struct sl_host_gtk_shell* host = wl_resource_get_user_data(resource);
free(host->startup_id);
wl_callback_destroy(host->callback);
zaura_shell_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_gtk_shell_callback_done(void* data,
struct wl_callback* callback,
uint32_t serial) {
struct sl_host_gtk_shell* host = wl_callback_get_user_data(callback);
gtk_shell1_send_capabilities(host->resource, 0);
}
static const struct wl_callback_listener sl_gtk_shell_callback_listener = {
sl_gtk_shell_callback_done};
static void sl_bind_host_gtk_shell(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_gtk_shell* host;
host = malloc(sizeof(*host));
assert(host);
host->aura_shell = ctx->aura_shell;
host->startup_id = NULL;
wl_list_init(&host->surfaces);
host->resource = wl_resource_create(client, &gtk_shell1_interface, 1, id);
wl_resource_set_implementation(host->resource, &sl_gtk_shell_implementation,
host, sl_destroy_host_gtk_shell);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
ctx->aura_shell->id, &zaura_shell_interface,
ctx->aura_shell->version);
zaura_shell_set_user_data(host->proxy, host);
host->callback = wl_display_sync(ctx->aura_shell->ctx->display);
wl_callback_set_user_data(host->callback, host);
wl_callback_add_listener(host->callback, &sl_gtk_shell_callback_listener,
host);
}
struct sl_global* sl_gtk_shell_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &gtk_shell1_interface, 1, ctx,
sl_bind_host_gtk_shell);
}

View File

@ -0,0 +1,377 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include "aura-shell-client-protocol.h"
#define MAX_OUTPUT_SCALE 2
#define INCH_IN_MM 25.4
// The ergonomic advice for monitor distance is 50-75cm away, with laptops
// expected to be closer. This magic number is designed to correct that for the
// purpose of calculating a "useful" DPI.
//
// TODO(crbug.com/988325) Fix sommelier's scaling logic s.t. this ratio is
// unnecessary.
#define LAPTOP_TO_DESKTOP_DISTANCE_RATIO (2.0 / 3.0)
double sl_output_aura_scale_factor_to_double(int scale_factor) {
// Aura scale factor is an enum that for all currently know values
// is a scale value multipled by 1000. For example, enum value for
// 1.25 scale factor is 1250.
return scale_factor / 1000.0;
}
void sl_output_get_host_output_state(struct sl_host_output* host,
int* scale,
int* physical_width,
int* physical_height,
int* width,
int* height) {
double preferred_scale =
sl_output_aura_scale_factor_to_double(host->preferred_scale);
double current_scale =
sl_output_aura_scale_factor_to_double(host->current_scale);
// "Ideal" means the scale factor you would need in order to make a pixel in
// the buffer map 1:1 with a physical pixel. In the absence of any better
// information, we assume a device whose display density maps faithfully to
// true pixels (i.e. 1.0).
double ideal_scale_factor = 1.0;
double scale_factor = host->scale_factor;
// Use the scale factor we received from aura shell protocol when available.
if (host->ctx->aura_shell) {
double device_scale_factor =
sl_output_aura_scale_factor_to_double(host->device_scale_factor);
ideal_scale_factor = device_scale_factor * preferred_scale;
scale_factor = device_scale_factor * current_scale;
}
// Always use scale=1 and adjust geometry and mode based on ideal
// scale factor for Xwayland client. For other clients, pick an optimal
// scale and adjust geometry and mode based on it.
if (host->ctx->xwayland) {
if (scale)
*scale = 1;
*physical_width = host->physical_width * ideal_scale_factor / scale_factor;
*physical_height =
host->physical_height * ideal_scale_factor / scale_factor;
*width = host->width * host->ctx->scale / scale_factor;
*height = host->height * host->ctx->scale / scale_factor;
// Historically, X applications use DPI to decide their scale (which is not
// ideal). The main problem is that in order to facilitate this, many X
// utilities lie about the DPI of the device in order to achieve the desired
// scaling, e.g. most laptops report a dpi of 96 even if that is inaccurate.
//
// The reason they have to lie is because laptop screens are typically
// closer to your eye than desktop monitors (by a factor of roughly 2/3),
// meaning they have to have proportionally higher DPI in order to "look" as
// high-def as the monitor.
//
// Since sommelier is in the business of lying about the screen's
// dimensions, we will also lie a bit more when we are dealing with the
// internal display, to make its dpi scale like a desktop monitor's would.
if (host->internal) {
*physical_width /= LAPTOP_TO_DESKTOP_DISTANCE_RATIO;
*physical_height /= LAPTOP_TO_DESKTOP_DISTANCE_RATIO;
}
} else {
int s = MIN(ceil(scale_factor / host->ctx->scale), MAX_OUTPUT_SCALE);
if (scale)
*scale = s;
*physical_width = host->physical_width;
*physical_height = host->physical_height;
*width = host->width * host->ctx->scale * s / scale_factor;
*height = host->height * host->ctx->scale * s / scale_factor;
}
if (host->ctx->dpi.size) {
int dpi = (*width * INCH_IN_MM) / *physical_width;
int adjusted_dpi = *((int*)host->ctx->dpi.data);
double mmpd;
int* p;
// Choose the DPI bucket which is closest to the apparent DPI which we
// calculated above.
wl_array_for_each(p, &host->ctx->dpi) {
if (abs(*p - dpi) < abs(adjusted_dpi - dpi))
adjusted_dpi = *p;
}
mmpd = INCH_IN_MM / adjusted_dpi;
*physical_width = *width * mmpd + 0.5;
*physical_height = *height * mmpd + 0.5;
}
}
void sl_output_send_host_output_state(struct sl_host_output* host) {
int scale;
int physical_width;
int physical_height;
int width;
int height;
sl_output_get_host_output_state(host, &scale, &physical_width,
&physical_height, &width, &height);
// Use density of internal display for all Xwayland outputs. X11 clients
// typically lack support for dynamically changing density so it's
// preferred to always use the density of the internal display.
if (host->ctx->xwayland) {
struct sl_host_output* output;
wl_list_for_each(output, &host->ctx->host_outputs, link) {
if (output->internal) {
int internal_width;
int internal_height;
sl_output_get_host_output_state(output, NULL, &physical_width,
&physical_height, &internal_width,
&internal_height);
physical_width = (physical_width * width) / internal_width;
physical_height = (physical_height * height) / internal_height;
break;
}
}
}
// X/Y are best left at origin as managed X windows are kept centered on
// the root window. The result is that all outputs are overlapping and
// pointer events can always be dispatched to the visible region of the
// window.
wl_output_send_geometry(host->resource, 0, 0, physical_width, physical_height,
host->subpixel, host->make, host->model,
host->transform);
wl_output_send_mode(host->resource, host->flags | WL_OUTPUT_MODE_CURRENT,
width, height, host->refresh);
if (wl_resource_get_version(host->resource) >= WL_OUTPUT_SCALE_SINCE_VERSION)
wl_output_send_scale(host->resource, scale);
if (wl_resource_get_version(host->resource) >= WL_OUTPUT_DONE_SINCE_VERSION)
wl_output_send_done(host->resource);
}
static void sl_output_geometry(void* data,
struct wl_output* output,
int x,
int y,
int physical_width,
int physical_height,
int subpixel,
const char* make,
const char* model,
int transform) {
struct sl_host_output* host = wl_output_get_user_data(output);
host->x = x;
host->y = y;
host->physical_width = physical_width;
host->physical_height = physical_height;
host->subpixel = subpixel;
free(host->model);
host->model = strdup(model);
free(host->make);
host->make = strdup(make);
host->transform = transform;
}
static void sl_output_mode(void* data,
struct wl_output* output,
uint32_t flags,
int width,
int height,
int refresh) {
struct sl_host_output* host = wl_output_get_user_data(output);
host->flags = flags;
host->width = width;
host->height = height;
host->refresh = refresh;
}
static void sl_output_done(void* data, struct wl_output* output) {
struct sl_host_output* host = wl_output_get_user_data(output);
// Early out if scale is expected but not yet know.
if (host->expecting_scale)
return;
sl_output_send_host_output_state(host);
// Expect scale if aura output exists.
if (host->aura_output)
host->expecting_scale = 1;
}
static void sl_output_scale(void* data,
struct wl_output* output,
int32_t scale_factor) {
struct sl_host_output* host = wl_output_get_user_data(output);
host->scale_factor = scale_factor;
}
static const struct wl_output_listener sl_output_listener = {
sl_output_geometry, sl_output_mode, sl_output_done, sl_output_scale};
static void sl_aura_output_scale(void* data,
struct zaura_output* output,
uint32_t flags,
uint32_t scale) {
struct sl_host_output* host = zaura_output_get_user_data(output);
switch (scale) {
case ZAURA_OUTPUT_SCALE_FACTOR_0400:
case ZAURA_OUTPUT_SCALE_FACTOR_0500:
case ZAURA_OUTPUT_SCALE_FACTOR_0550:
case ZAURA_OUTPUT_SCALE_FACTOR_0600:
case ZAURA_OUTPUT_SCALE_FACTOR_0625:
case ZAURA_OUTPUT_SCALE_FACTOR_0650:
case ZAURA_OUTPUT_SCALE_FACTOR_0700:
case ZAURA_OUTPUT_SCALE_FACTOR_0750:
case ZAURA_OUTPUT_SCALE_FACTOR_0800:
case ZAURA_OUTPUT_SCALE_FACTOR_0850:
case ZAURA_OUTPUT_SCALE_FACTOR_0900:
case ZAURA_OUTPUT_SCALE_FACTOR_0950:
case ZAURA_OUTPUT_SCALE_FACTOR_1000:
case ZAURA_OUTPUT_SCALE_FACTOR_1050:
case ZAURA_OUTPUT_SCALE_FACTOR_1100:
case ZAURA_OUTPUT_SCALE_FACTOR_1150:
case ZAURA_OUTPUT_SCALE_FACTOR_1125:
case ZAURA_OUTPUT_SCALE_FACTOR_1200:
case ZAURA_OUTPUT_SCALE_FACTOR_1250:
case ZAURA_OUTPUT_SCALE_FACTOR_1300:
case ZAURA_OUTPUT_SCALE_FACTOR_1400:
case ZAURA_OUTPUT_SCALE_FACTOR_1450:
case ZAURA_OUTPUT_SCALE_FACTOR_1500:
case ZAURA_OUTPUT_SCALE_FACTOR_1600:
case ZAURA_OUTPUT_SCALE_FACTOR_1750:
case ZAURA_OUTPUT_SCALE_FACTOR_1800:
case ZAURA_OUTPUT_SCALE_FACTOR_2000:
case ZAURA_OUTPUT_SCALE_FACTOR_2200:
case ZAURA_OUTPUT_SCALE_FACTOR_2250:
case ZAURA_OUTPUT_SCALE_FACTOR_2500:
case ZAURA_OUTPUT_SCALE_FACTOR_2750:
case ZAURA_OUTPUT_SCALE_FACTOR_3000:
case ZAURA_OUTPUT_SCALE_FACTOR_3500:
case ZAURA_OUTPUT_SCALE_FACTOR_4000:
case ZAURA_OUTPUT_SCALE_FACTOR_4500:
case ZAURA_OUTPUT_SCALE_FACTOR_5000:
break;
default:
fprintf(stderr, "warning: unknown scale factor: %d\n", scale);
break;
}
if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT)
host->current_scale = scale;
if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED)
host->preferred_scale = scale;
host->expecting_scale = 0;
}
static void sl_aura_output_connection(void* data,
struct zaura_output* output,
uint32_t connection) {
struct sl_host_output* host = zaura_output_get_user_data(output);
host->internal = connection == ZAURA_OUTPUT_CONNECTION_TYPE_INTERNAL;
}
static void sl_aura_output_device_scale_factor(void* data,
struct zaura_output* output,
uint32_t device_scale_factor) {
struct sl_host_output* host = zaura_output_get_user_data(output);
host->device_scale_factor = device_scale_factor;
}
static const struct zaura_output_listener sl_aura_output_listener = {
sl_aura_output_scale, sl_aura_output_connection,
sl_aura_output_device_scale_factor};
static void sl_destroy_host_output(struct wl_resource* resource) {
struct sl_host_output* host = wl_resource_get_user_data(resource);
if (host->aura_output)
zaura_output_destroy(host->aura_output);
if (wl_output_get_version(host->proxy) >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
wl_output_release(host->proxy);
} else {
wl_output_destroy(host->proxy);
}
wl_resource_set_user_data(resource, NULL);
wl_list_remove(&host->link);
free(host->make);
free(host->model);
free(host);
}
static void sl_bind_host_output(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_output* output = (struct sl_output*)data;
struct sl_context* ctx = output->ctx;
struct sl_host_output* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->resource = wl_resource_create(client, &wl_output_interface,
MIN(version, output->version), id);
wl_resource_set_implementation(host->resource, NULL, host,
sl_destroy_host_output);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
output->id, &wl_output_interface,
wl_resource_get_version(host->resource));
wl_output_set_user_data(host->proxy, host);
wl_output_add_listener(host->proxy, &sl_output_listener, host);
host->aura_output = NULL;
// We assume that first output is internal by default.
host->internal = wl_list_empty(&ctx->host_outputs);
host->x = 0;
host->y = 0;
host->physical_width = 0;
host->physical_height = 0;
host->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
host->make = strdup("unknown");
host->model = strdup("unknown");
host->transform = WL_OUTPUT_TRANSFORM_NORMAL;
host->flags = 0;
host->width = 1024;
host->height = 768;
host->refresh = 60000;
host->scale_factor = 1;
host->current_scale = 1000;
host->preferred_scale = 1000;
host->device_scale_factor = 1000;
host->expecting_scale = 0;
wl_list_insert(ctx->host_outputs.prev, &host->link);
if (ctx->aura_shell) {
host->expecting_scale = 1;
host->internal = 0;
host->aura_output =
zaura_shell_get_aura_output(ctx->aura_shell->internal, host->proxy);
zaura_output_set_user_data(host->aura_output, host);
zaura_output_add_listener(host->aura_output, &sl_aura_output_listener,
host);
}
}
struct sl_global* sl_output_global_create(struct sl_output* output) {
return sl_global_create(output->ctx, &wl_output_interface, output->version,
output, sl_bind_host_output);
}

View File

@ -0,0 +1,146 @@
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-server-core.h>
#include "relative-pointer-unstable-v1-server-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
struct sl_host_relative_pointer_manager {
struct sl_context* ctx;
struct wl_resource* resource;
struct zwp_relative_pointer_manager_v1* proxy;
};
struct sl_host_relative_pointer {
struct sl_context* ctx;
struct wl_resource* resource;
struct zwp_relative_pointer_v1* proxy;
};
static void sl_relative_pointer_relative_motion(
void* data,
struct zwp_relative_pointer_v1* relative_pointer,
uint32_t utime_hi,
uint32_t utime_lo,
wl_fixed_t dx,
wl_fixed_t dy,
wl_fixed_t dx_unaccel,
wl_fixed_t dy_unaccel) {
struct sl_host_relative_pointer* host =
zwp_relative_pointer_v1_get_user_data(relative_pointer);
zwp_relative_pointer_v1_send_relative_motion(
host->resource, utime_hi, utime_lo, dx, dy, dx_unaccel, dy_unaccel);
}
static void sl_destroy_host_relative_pointer(struct wl_resource* resource) {
struct sl_host_relative_pointer* host = wl_resource_get_user_data(resource);
zwp_relative_pointer_v1_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_relative_pointer_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static struct zwp_relative_pointer_v1_listener sl_relative_pointer_listener = {
sl_relative_pointer_relative_motion,
};
static struct zwp_relative_pointer_v1_interface
sl_relative_pointer_implementation = {
sl_relative_pointer_destroy,
};
static void sl_destroy_host_relative_pointer_manager(
struct wl_resource* resource) {
struct sl_host_relative_pointer_manager* host =
wl_resource_get_user_data(resource);
zwp_relative_pointer_manager_v1_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_relative_pointer_manager_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_relative_pointer_manager_get_relative_pointer(
struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* pointer) {
struct sl_host_relative_pointer_manager* host =
wl_resource_get_user_data(resource);
struct sl_host_pointer* host_pointer = wl_resource_get_user_data(pointer);
struct wl_resource* relative_pointer_resource =
wl_resource_create(client, &zwp_relative_pointer_v1_interface, 1, id);
struct sl_host_relative_pointer* relative_pointer_host;
relative_pointer_host = malloc(sizeof(*relative_pointer_host));
assert(relative_pointer_host);
relative_pointer_host->resource = relative_pointer_resource;
relative_pointer_host->ctx = host->ctx;
relative_pointer_host->proxy =
zwp_relative_pointer_manager_v1_get_relative_pointer(
host->ctx->relative_pointer_manager->internal, host_pointer->proxy);
wl_resource_set_implementation(
relative_pointer_resource, &sl_relative_pointer_implementation,
relative_pointer_host, sl_destroy_host_relative_pointer);
zwp_relative_pointer_v1_set_user_data(relative_pointer_host->proxy,
relative_pointer_host);
zwp_relative_pointer_v1_add_listener(relative_pointer_host->proxy,
&sl_relative_pointer_listener,
relative_pointer_host);
}
static struct zwp_relative_pointer_manager_v1_interface
sl_relative_pointer_manager_implementation = {
sl_relative_pointer_manager_destroy,
sl_relative_pointer_manager_get_relative_pointer,
};
static void sl_bind_host_relative_pointer_manager(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_relative_pointer_manager* relative_pointer_manager =
ctx->relative_pointer_manager;
struct sl_host_relative_pointer_manager* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->resource = wl_resource_create(
client, &zwp_relative_pointer_manager_v1_interface, 1, id);
wl_resource_set_implementation(
host->resource, &sl_relative_pointer_manager_implementation, host,
sl_destroy_host_relative_pointer_manager);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
relative_pointer_manager->id,
&zwp_relative_pointer_manager_v1_interface,
wl_resource_get_version(host->resource));
zwp_relative_pointer_manager_v1_set_user_data(host->proxy, host);
}
struct sl_global* sl_relative_pointer_manager_global_create(
struct sl_context* ctx) {
return sl_global_create(ctx, &zwp_relative_pointer_manager_v1_interface, 1,
ctx, sl_bind_host_relative_pointer_manager);
}

778
sommelier/sommelier-seat.c Normal file
View File

@ -0,0 +1,778 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client.h>
#include "keyboard-extension-unstable-v1-client-protocol.h"
struct sl_host_keyboard {
struct sl_seat* seat;
struct wl_resource* resource;
struct wl_keyboard* proxy;
struct zcr_extended_keyboard_v1* extended_keyboard_proxy;
struct wl_resource* focus_resource;
struct wl_listener focus_resource_listener;
uint32_t focus_serial;
struct xkb_keymap* keymap;
struct xkb_state* state;
xkb_mod_mask_t control_mask;
xkb_mod_mask_t alt_mask;
xkb_mod_mask_t shift_mask;
uint32_t modifiers;
struct wl_array pressed_keys;
};
struct sl_host_touch {
struct sl_seat* seat;
struct wl_resource* resource;
struct wl_touch* proxy;
struct wl_resource* focus_resource;
struct wl_listener focus_resource_listener;
};
static void sl_host_pointer_set_cursor(struct wl_client* client,
struct wl_resource* resource,
uint32_t serial,
struct wl_resource* surface_resource,
int32_t hotspot_x,
int32_t hotspot_y) {
struct sl_host_pointer* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_surface = NULL;
double scale = host->seat->ctx->scale;
if (surface_resource) {
host_surface = wl_resource_get_user_data(surface_resource);
host_surface->has_role = 1;
if (host_surface->contents_width && host_surface->contents_height)
wl_surface_commit(host_surface->proxy);
}
wl_pointer_set_cursor(host->proxy, serial,
host_surface ? host_surface->proxy : NULL,
hotspot_x / scale, hotspot_y / scale);
}
static void sl_host_pointer_release(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct wl_pointer_interface sl_pointer_implementation = {
sl_host_pointer_set_cursor, sl_host_pointer_release};
static void sl_set_last_event_serial(struct wl_resource* surface_resource,
uint32_t serial) {
struct sl_host_surface* host_surface =
wl_resource_get_user_data(surface_resource);
host_surface->last_event_serial = serial;
}
static void sl_pointer_set_focus(struct sl_host_pointer* host,
uint32_t serial,
struct sl_host_surface* host_surface,
wl_fixed_t x,
wl_fixed_t y) {
struct wl_resource* surface_resource =
host_surface ? host_surface->resource : NULL;
if (surface_resource == host->focus_resource)
return;
if (host->focus_resource)
wl_pointer_send_leave(host->resource, serial, host->focus_resource);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = surface_resource;
host->focus_serial = serial;
if (surface_resource) {
double scale = host->seat->ctx->scale;
if (host->seat->ctx->xwayland) {
// Make sure focus surface is on top before sending enter event.
sl_restack_windows(host->seat->ctx, wl_resource_get_id(surface_resource));
sl_roundtrip(host->seat->ctx);
}
wl_resource_add_destroy_listener(surface_resource,
&host->focus_resource_listener);
wl_pointer_send_enter(host->resource, serial, surface_resource, x * scale,
y * scale);
}
}
static void sl_pointer_enter(void* data,
struct wl_pointer* pointer,
uint32_t serial,
struct wl_surface* surface,
wl_fixed_t x,
wl_fixed_t y) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
struct sl_host_surface* host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
if (!host_surface)
return;
sl_pointer_set_focus(host, serial, host_surface, x, y);
if (host->focus_resource)
sl_set_last_event_serial(host->focus_resource, serial);
host->seat->last_serial = serial;
}
static void sl_pointer_leave(void* data,
struct wl_pointer* pointer,
uint32_t serial,
struct wl_surface* surface) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
sl_pointer_set_focus(host, serial, NULL, 0, 0);
}
static void sl_pointer_motion(void* data,
struct wl_pointer* pointer,
uint32_t time,
wl_fixed_t x,
wl_fixed_t y) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
double scale = host->seat->ctx->scale;
wl_pointer_send_motion(host->resource, time, x * scale, y * scale);
}
static void sl_pointer_button(void* data,
struct wl_pointer* pointer,
uint32_t serial,
uint32_t time,
uint32_t button,
uint32_t state) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
wl_pointer_send_button(host->resource, serial, time, button, state);
if (host->focus_resource)
sl_set_last_event_serial(host->focus_resource, serial);
host->seat->last_serial = serial;
}
static void sl_pointer_axis(void* data,
struct wl_pointer* pointer,
uint32_t time,
uint32_t axis,
wl_fixed_t value) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
double scale = host->seat->ctx->scale;
wl_pointer_send_axis(host->resource, time, axis, value * scale);
}
static void sl_pointer_frame(void* data, struct wl_pointer* pointer) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
wl_pointer_send_frame(host->resource);
}
void sl_pointer_axis_source(void* data,
struct wl_pointer* pointer,
uint32_t axis_source) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
wl_pointer_send_axis_source(host->resource, axis_source);
}
static void sl_pointer_axis_stop(void* data,
struct wl_pointer* pointer,
uint32_t time,
uint32_t axis) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
wl_pointer_send_axis_stop(host->resource, time, axis);
}
static void sl_pointer_axis_discrete(void* data,
struct wl_pointer* pointer,
uint32_t axis,
int32_t discrete) {
struct sl_host_pointer* host = wl_pointer_get_user_data(pointer);
wl_pointer_send_axis_discrete(host->resource, axis, discrete);
}
static const struct wl_pointer_listener sl_pointer_listener = {
sl_pointer_enter, sl_pointer_leave, sl_pointer_motion,
sl_pointer_button, sl_pointer_axis, sl_pointer_frame,
sl_pointer_axis_source, sl_pointer_axis_stop, sl_pointer_axis_discrete};
static void sl_host_keyboard_release(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct wl_keyboard_interface sl_keyboard_implementation = {
sl_host_keyboard_release};
static void sl_keyboard_keymap(void* data,
struct wl_keyboard* keyboard,
uint32_t format,
int32_t fd,
uint32_t size) {
struct sl_host_keyboard* host = wl_keyboard_get_user_data(keyboard);
wl_keyboard_send_keymap(host->resource, format, fd, size);
if (format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
void* data = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
assert(data != MAP_FAILED);
if (host->keymap)
xkb_keymap_unref(host->keymap);
host->keymap = xkb_keymap_new_from_string(
host->seat->ctx->xkb_context, data, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
assert(host->keymap);
munmap(data, size);
if (host->state)
xkb_state_unref(host->state);
host->state = xkb_state_new(host->keymap);
assert(host->state);
host->control_mask = 1 << xkb_keymap_mod_get_index(host->keymap, "Control");
host->alt_mask = 1 << xkb_keymap_mod_get_index(host->keymap, "Mod1");
host->shift_mask = 1 << xkb_keymap_mod_get_index(host->keymap, "Shift");
}
close(fd);
}
static void sl_keyboard_set_focus(struct sl_host_keyboard* host,
uint32_t serial,
struct sl_host_surface* host_surface,
struct wl_array* keys) {
struct wl_resource* surface_resource =
host_surface ? host_surface->resource : NULL;
if (surface_resource == host->focus_resource)
return;
if (host->focus_resource)
wl_keyboard_send_leave(host->resource, serial, host->focus_resource);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = surface_resource;
host->focus_serial = serial;
if (surface_resource) {
wl_resource_add_destroy_listener(surface_resource,
&host->focus_resource_listener);
wl_keyboard_send_enter(host->resource, serial, surface_resource, keys);
}
host->seat->last_serial = serial;
}
static void sl_keyboard_enter(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
struct wl_surface* surface,
struct wl_array* keys) {
struct sl_host_keyboard* host = wl_keyboard_get_user_data(keyboard);
struct sl_host_surface* host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
if (!host_surface)
return;
wl_array_copy(&host->pressed_keys, keys);
sl_keyboard_set_focus(host, serial, host_surface, keys);
host->seat->last_serial = serial;
}
static void sl_keyboard_leave(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
struct wl_surface* surface) {
struct sl_host_keyboard* host = wl_keyboard_get_user_data(keyboard);
struct wl_array array;
wl_array_init(&array);
sl_keyboard_set_focus(host, serial, NULL, &array);
}
static int sl_array_set_add(struct wl_array* array, uint32_t key) {
uint32_t* k;
wl_array_for_each(k, array) {
if (*k == key)
return 0;
}
k = wl_array_add(array, sizeof(key));
assert(k);
*k = key;
return 1;
}
static int sl_array_set_remove(struct wl_array* array, uint32_t key) {
uint32_t* k;
wl_array_for_each(k, array) {
if (*k == key) {
uint32_t* end = (uint32_t*)((char*)array->data + array->size);
*k = *(end - 1);
array->size -= sizeof(*k);
return 1;
}
}
return 0;
}
static void sl_keyboard_key(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
uint32_t time,
uint32_t key,
uint32_t state) {
struct sl_host_keyboard* host = wl_keyboard_get_user_data(keyboard);
int handled = 1;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (host->state) {
const xkb_keysym_t* symbols;
uint32_t num_symbols;
xkb_keysym_t symbol = XKB_KEY_NoSymbol;
uint32_t code = key + 8;
struct sl_accelerator* accelerator;
num_symbols = xkb_state_key_get_syms(host->state, code, &symbols);
if (num_symbols == 1)
symbol = symbols[0];
wl_list_for_each(accelerator, &host->seat->ctx->accelerators, link) {
if (host->modifiers != accelerator->modifiers)
continue;
if (symbol != accelerator->symbol)
continue;
handled = 0;
break;
}
}
// Forward key pressed event if it should be handled and not
// already pressed.
if (handled) {
if (sl_array_set_add(&host->pressed_keys, key))
wl_keyboard_send_key(host->resource, serial, time, key, state);
}
} else {
// Forward key release event if currently pressed.
handled = sl_array_set_remove(&host->pressed_keys, key);
if (handled)
wl_keyboard_send_key(host->resource, serial, time, key, state);
}
if (host->focus_resource)
sl_set_last_event_serial(host->focus_resource, serial);
host->seat->last_serial = serial;
if (host->extended_keyboard_proxy) {
zcr_extended_keyboard_v1_ack_key(
host->extended_keyboard_proxy, serial,
handled ? ZCR_EXTENDED_KEYBOARD_V1_HANDLED_STATE_HANDLED
: ZCR_EXTENDED_KEYBOARD_V1_HANDLED_STATE_NOT_HANDLED);
}
}
static void sl_keyboard_modifiers(void* data,
struct wl_keyboard* keyboard,
uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group) {
struct sl_host_keyboard* host = wl_keyboard_get_user_data(keyboard);
xkb_mod_mask_t mask;
wl_keyboard_send_modifiers(host->resource, serial, mods_depressed,
mods_latched, mods_locked, group);
if (host->focus_resource)
sl_set_last_event_serial(host->focus_resource, serial);
host->seat->last_serial = serial;
if (!host->keymap)
return;
xkb_state_update_mask(host->state, mods_depressed, mods_latched, mods_locked,
0, 0, group);
mask = xkb_state_serialize_mods(
host->state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED);
host->modifiers = 0;
if (mask & host->control_mask)
host->modifiers |= CONTROL_MASK;
if (mask & host->alt_mask)
host->modifiers |= ALT_MASK;
if (mask & host->shift_mask)
host->modifiers |= SHIFT_MASK;
}
static void sl_keyboard_repeat_info(void* data,
struct wl_keyboard* keyboard,
int32_t rate,
int32_t delay) {
struct sl_host_keyboard* host = wl_keyboard_get_user_data(keyboard);
wl_keyboard_send_repeat_info(host->resource, rate, delay);
}
static const struct wl_keyboard_listener sl_keyboard_listener = {
sl_keyboard_keymap, sl_keyboard_enter, sl_keyboard_leave,
sl_keyboard_key, sl_keyboard_modifiers, sl_keyboard_repeat_info};
static void sl_host_touch_release(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct wl_touch_interface sl_touch_implementation = {
sl_host_touch_release};
static void sl_host_touch_down(void* data,
struct wl_touch* touch,
uint32_t serial,
uint32_t time,
struct wl_surface* surface,
int32_t id,
wl_fixed_t x,
wl_fixed_t y) {
struct sl_host_touch* host = wl_touch_get_user_data(touch);
struct sl_host_surface* host_surface =
surface ? wl_surface_get_user_data(surface) : NULL;
double scale = host->seat->ctx->scale;
if (!host_surface)
return;
if (host_surface->resource != host->focus_resource) {
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = host_surface->resource;
wl_resource_add_destroy_listener(host_surface->resource,
&host->focus_resource_listener);
}
if (host->seat->ctx->xwayland) {
// Make sure focus surface is on top before sending down event.
sl_restack_windows(host->seat->ctx,
wl_resource_get_id(host_surface->resource));
sl_roundtrip(host->seat->ctx);
}
wl_touch_send_down(host->resource, serial, time, host_surface->resource, id,
x * scale, y * scale);
if (host->focus_resource)
sl_set_last_event_serial(host->focus_resource, serial);
host->seat->last_serial = serial;
}
static void sl_host_touch_up(void* data,
struct wl_touch* touch,
uint32_t serial,
uint32_t time,
int32_t id) {
struct sl_host_touch* host = wl_touch_get_user_data(touch);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = NULL;
wl_touch_send_up(host->resource, serial, time, id);
if (host->focus_resource)
sl_set_last_event_serial(host->focus_resource, serial);
host->seat->last_serial = serial;
}
static void sl_host_touch_motion(void* data,
struct wl_touch* touch,
uint32_t time,
int32_t id,
wl_fixed_t x,
wl_fixed_t y) {
struct sl_host_touch* host = wl_touch_get_user_data(touch);
double scale = host->seat->ctx->scale;
wl_touch_send_motion(host->resource, time, id, x * scale, y * scale);
}
static void sl_host_touch_frame(void* data, struct wl_touch* touch) {
struct sl_host_touch* host = wl_touch_get_user_data(touch);
wl_touch_send_frame(host->resource);
}
static void sl_host_touch_cancel(void* data, struct wl_touch* touch) {
struct sl_host_touch* host = wl_touch_get_user_data(touch);
wl_touch_send_cancel(host->resource);
}
static const struct wl_touch_listener sl_touch_listener = {
sl_host_touch_down, sl_host_touch_up, sl_host_touch_motion,
sl_host_touch_frame, sl_host_touch_cancel};
static void sl_destroy_host_pointer(struct wl_resource* resource) {
struct sl_host_pointer* host = wl_resource_get_user_data(resource);
if (wl_pointer_get_version(host->proxy) >= WL_POINTER_RELEASE_SINCE_VERSION) {
wl_pointer_release(host->proxy);
} else {
wl_pointer_destroy(host->proxy);
}
wl_list_remove(&host->focus_resource_listener.link);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_pointer_focus_resource_destroyed(struct wl_listener* listener,
void* data) {
struct sl_host_pointer* host;
host = wl_container_of(listener, host, focus_resource_listener);
sl_pointer_set_focus(host, host->focus_serial, NULL, 0, 0);
}
static void sl_host_seat_get_host_pointer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_seat* host = wl_resource_get_user_data(resource);
struct sl_host_pointer* host_pointer;
host_pointer = malloc(sizeof(*host_pointer));
assert(host_pointer);
host_pointer->seat = host->seat;
host_pointer->resource = wl_resource_create(
client, &wl_pointer_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_pointer->resource,
&sl_pointer_implementation, host_pointer,
sl_destroy_host_pointer);
host_pointer->proxy = wl_seat_get_pointer(host->proxy);
wl_pointer_set_user_data(host_pointer->proxy, host_pointer);
wl_pointer_add_listener(host_pointer->proxy, &sl_pointer_listener,
host_pointer);
wl_list_init(&host_pointer->focus_resource_listener.link);
host_pointer->focus_resource_listener.notify =
sl_pointer_focus_resource_destroyed;
host_pointer->focus_resource = NULL;
host_pointer->focus_serial = 0;
}
static void sl_destroy_host_keyboard(struct wl_resource* resource) {
struct sl_host_keyboard* host = wl_resource_get_user_data(resource);
if (host->extended_keyboard_proxy)
zcr_extended_keyboard_v1_destroy(host->extended_keyboard_proxy);
wl_array_release(&host->pressed_keys);
if (host->keymap)
xkb_keymap_unref(host->keymap);
if (host->state)
xkb_state_unref(host->state);
if (wl_keyboard_get_version(host->proxy) >=
WL_KEYBOARD_RELEASE_SINCE_VERSION) {
wl_keyboard_release(host->proxy);
} else {
wl_keyboard_destroy(host->proxy);
}
wl_list_remove(&host->focus_resource_listener.link);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_keyboard_focus_resource_destroyed(struct wl_listener* listener,
void* data) {
struct sl_host_keyboard* host;
struct wl_array array;
host = wl_container_of(listener, host, focus_resource_listener);
wl_array_init(&array);
sl_keyboard_set_focus(host, host->focus_serial, NULL, &array);
}
static void sl_host_seat_get_host_keyboard(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_seat* host = wl_resource_get_user_data(resource);
struct sl_host_keyboard* host_keyboard;
host_keyboard = malloc(sizeof(*host_keyboard));
assert(host_keyboard);
host_keyboard->seat = host->seat;
host_keyboard->resource = wl_resource_create(
client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_keyboard->resource,
&sl_keyboard_implementation, host_keyboard,
sl_destroy_host_keyboard);
host_keyboard->proxy = wl_seat_get_keyboard(host->proxy);
wl_keyboard_set_user_data(host_keyboard->proxy, host_keyboard);
wl_keyboard_add_listener(host_keyboard->proxy, &sl_keyboard_listener,
host_keyboard);
wl_list_init(&host_keyboard->focus_resource_listener.link);
host_keyboard->focus_resource_listener.notify =
sl_keyboard_focus_resource_destroyed;
host_keyboard->focus_resource = NULL;
host_keyboard->focus_serial = 0;
host_keyboard->keymap = NULL;
host_keyboard->state = NULL;
host_keyboard->control_mask = 0;
host_keyboard->alt_mask = 0;
host_keyboard->shift_mask = 0;
host_keyboard->modifiers = 0;
wl_array_init(&host_keyboard->pressed_keys);
if (host->seat->ctx->keyboard_extension) {
host_keyboard->extended_keyboard_proxy =
zcr_keyboard_extension_v1_get_extended_keyboard(
host->seat->ctx->keyboard_extension->internal,
host_keyboard->proxy);
} else {
host_keyboard->extended_keyboard_proxy = NULL;
}
}
static void sl_destroy_host_touch(struct wl_resource* resource) {
struct sl_host_touch* host = wl_resource_get_user_data(resource);
if (wl_touch_get_version(host->proxy) >= WL_TOUCH_RELEASE_SINCE_VERSION) {
wl_touch_release(host->proxy);
} else {
wl_touch_destroy(host->proxy);
}
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_touch_focus_resource_destroyed(struct wl_listener* listener,
void* data) {
struct sl_host_touch* host;
host = wl_container_of(listener, host, focus_resource_listener);
wl_list_remove(&host->focus_resource_listener.link);
wl_list_init(&host->focus_resource_listener.link);
host->focus_resource = NULL;
}
static void sl_host_seat_get_host_touch(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_seat* host = wl_resource_get_user_data(resource);
struct sl_host_touch* host_touch;
host_touch = malloc(sizeof(*host_touch));
assert(host_touch);
host_touch->seat = host->seat;
host_touch->resource = wl_resource_create(
client, &wl_touch_interface, wl_resource_get_version(resource), id);
wl_resource_set_implementation(host_touch->resource, &sl_touch_implementation,
host_touch, sl_destroy_host_touch);
host_touch->proxy = wl_seat_get_touch(host->proxy);
wl_touch_set_user_data(host_touch->proxy, host_touch);
wl_touch_add_listener(host_touch->proxy, &sl_touch_listener, host_touch);
wl_list_init(&host_touch->focus_resource_listener.link);
host_touch->focus_resource_listener.notify =
sl_touch_focus_resource_destroyed;
host_touch->focus_resource = NULL;
}
static void sl_host_seat_release(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_seat* host = wl_resource_get_user_data(resource);
wl_seat_release(host->proxy);
}
static const struct wl_seat_interface sl_seat_implementation = {
sl_host_seat_get_host_pointer, sl_host_seat_get_host_keyboard,
sl_host_seat_get_host_touch, sl_host_seat_release};
static void sl_seat_capabilities(void* data,
struct wl_seat* seat,
uint32_t capabilities) {
struct sl_host_seat* host = wl_seat_get_user_data(seat);
wl_seat_send_capabilities(host->resource, capabilities);
}
static void sl_seat_name(void* data, struct wl_seat* seat, const char* name) {
struct sl_host_seat* host = wl_seat_get_user_data(seat);
if (wl_resource_get_version(host->resource) >= WL_SEAT_NAME_SINCE_VERSION)
wl_seat_send_name(host->resource, name);
}
static const struct wl_seat_listener sl_seat_listener = {sl_seat_capabilities,
sl_seat_name};
static void sl_destroy_host_seat(struct wl_resource* resource) {
struct sl_host_seat* host = wl_resource_get_user_data(resource);
sl_host_seat_removed(host);
if (wl_seat_get_version(host->proxy) >= WL_SEAT_RELEASE_SINCE_VERSION)
wl_seat_release(host->proxy);
else
wl_seat_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_seat(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_seat* seat = (struct sl_seat*)data;
struct sl_host_seat* host;
host = malloc(sizeof(*host));
assert(host);
host->seat = seat;
host->resource = wl_resource_create(client, &wl_seat_interface,
MIN(version, seat->version), id);
wl_resource_set_implementation(host->resource, &sl_seat_implementation, host,
sl_destroy_host_seat);
host->proxy = wl_registry_bind(wl_display_get_registry(seat->ctx->display),
seat->id, &wl_seat_interface,
wl_resource_get_version(host->resource));
wl_seat_set_user_data(host->proxy, host);
wl_seat_add_listener(host->proxy, &sl_seat_listener, host);
sl_host_seat_added(host);
}
struct sl_global* sl_seat_global_create(struct sl_seat* seat) {
return sl_global_create(seat->ctx, &wl_seat_interface, seat->version, seat,
sl_bind_host_seat);
}

234
sommelier/sommelier-shell.c Normal file
View File

@ -0,0 +1,234 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <wayland-client.h>
struct sl_host_shell_surface {
struct wl_resource* resource;
struct wl_shell_surface* proxy;
};
struct sl_host_shell {
struct sl_shell* shell;
struct wl_resource* resource;
struct wl_shell* proxy;
};
static void sl_shell_surface_pong(struct wl_client* client,
struct wl_resource* resource,
uint32_t serial) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
wl_shell_surface_pong(host->proxy, serial);
}
static void sl_shell_surface_move(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat_resource);
wl_shell_surface_move(host->proxy, host_seat->proxy, serial);
}
static void sl_shell_surface_resize(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial,
uint32_t edges) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat_resource);
wl_shell_surface_resize(host->proxy, host_seat->proxy, serial, edges);
}
static void sl_shell_surface_set_toplevel(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
wl_shell_surface_set_toplevel(host->proxy);
}
static void sl_shell_surface_set_transient(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* parent_resource,
int32_t x,
int32_t y,
uint32_t flags) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_parent =
wl_resource_get_user_data(parent_resource);
wl_shell_surface_set_transient(host->proxy, host_parent->proxy, x, y, flags);
}
static void sl_shell_surface_set_fullscreen(
struct wl_client* client,
struct wl_resource* resource,
uint32_t method,
uint32_t framerate,
struct wl_resource* output_resource) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
struct sl_host_output* host_output =
output_resource ? wl_resource_get_user_data(output_resource) : NULL;
wl_shell_surface_set_fullscreen(host->proxy, method, framerate,
host_output ? host_output->proxy : NULL);
}
static void sl_shell_surface_set_popup(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial,
struct wl_resource* parent_resource,
int32_t x,
int32_t y,
uint32_t flags) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat_resource);
struct sl_host_surface* host_parent =
wl_resource_get_user_data(parent_resource);
wl_shell_surface_set_popup(host->proxy, host_seat->proxy, serial,
host_parent->proxy, x, y, flags);
}
static void sl_shell_surface_set_maximized(
struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* output_resource) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
struct sl_host_output* host_output =
output_resource ? wl_resource_get_user_data(output_resource) : NULL;
wl_shell_surface_set_maximized(host->proxy,
host_output ? host_output->proxy : NULL);
}
static void sl_shell_surface_set_title(struct wl_client* client,
struct wl_resource* resource,
const char* title) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
wl_shell_surface_set_title(host->proxy, title);
}
static void sl_shell_surface_set_class(struct wl_client* client,
struct wl_resource* resource,
const char* clazz) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
wl_shell_surface_set_class(host->proxy, clazz);
}
static const struct wl_shell_surface_interface sl_shell_surface_implementation =
{sl_shell_surface_pong, sl_shell_surface_move,
sl_shell_surface_resize, sl_shell_surface_set_toplevel,
sl_shell_surface_set_transient, sl_shell_surface_set_fullscreen,
sl_shell_surface_set_popup, sl_shell_surface_set_maximized,
sl_shell_surface_set_title, sl_shell_surface_set_class};
static void sl_shell_surface_ping(void* data,
struct wl_shell_surface* shell_surface,
uint32_t serial) {
struct sl_host_shell_surface* host =
wl_shell_surface_get_user_data(shell_surface);
wl_shell_surface_send_ping(host->resource, serial);
}
static void sl_shell_surface_configure(void* data,
struct wl_shell_surface* shell_surface,
uint32_t edges,
int32_t width,
int32_t height) {
struct sl_host_shell_surface* host =
wl_shell_surface_get_user_data(shell_surface);
wl_shell_surface_send_configure(host->resource, edges, width, height);
}
static void sl_shell_surface_popup_done(
void* data, struct wl_shell_surface* shell_surface) {
struct sl_host_shell_surface* host =
wl_shell_surface_get_user_data(shell_surface);
wl_shell_surface_send_popup_done(host->resource);
}
static const struct wl_shell_surface_listener sl_shell_surface_listener = {
sl_shell_surface_ping, sl_shell_surface_configure,
sl_shell_surface_popup_done};
static void sl_destroy_host_shell_surface(struct wl_resource* resource) {
struct sl_host_shell_surface* host = wl_resource_get_user_data(resource);
wl_shell_surface_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_host_shell_get_shell_surface(
struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface_resource) {
struct sl_host_shell* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_surface =
wl_resource_get_user_data(surface_resource);
struct sl_host_shell_surface* host_shell_surface;
host_shell_surface = malloc(sizeof(*host_shell_surface));
assert(host_shell_surface);
host_shell_surface->resource =
wl_resource_create(client, &wl_shell_surface_interface, 1, id);
wl_resource_set_implementation(
host_shell_surface->resource, &sl_shell_surface_implementation,
host_shell_surface, sl_destroy_host_shell_surface);
host_shell_surface->proxy =
wl_shell_get_shell_surface(host->proxy, host_surface->proxy);
wl_shell_surface_set_user_data(host_shell_surface->proxy, host_shell_surface);
wl_shell_surface_add_listener(host_shell_surface->proxy,
&sl_shell_surface_listener, host_shell_surface);
host_surface->has_role = 1;
}
static const struct wl_shell_interface sl_shell_implementation = {
sl_host_shell_get_shell_surface};
static void sl_destroy_host_shell(struct wl_resource* resource) {
struct sl_host_shell* host = wl_resource_get_user_data(resource);
wl_shell_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_shell(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_shell* host;
host = malloc(sizeof(*host));
assert(host);
host->shell = ctx->shell;
host->resource = wl_resource_create(client, &wl_shell_interface, 1, id);
wl_resource_set_implementation(host->resource, &sl_shell_implementation, host,
sl_destroy_host_shell);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
ctx->shell->id, &wl_shell_interface,
wl_resource_get_version(host->resource));
wl_shell_set_user_data(host->proxy, host);
}
struct sl_global* sl_shell_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wl_shell_interface, 1, ctx, sl_bind_host_shell);
}

328
sommelier/sommelier-shm.c Normal file
View File

@ -0,0 +1,328 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-client.h>
#include "drm-server-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
struct sl_host_shm_pool {
struct sl_shm* shm;
struct wl_resource* resource;
struct wl_shm_pool* proxy;
int fd;
};
struct sl_host_shm {
struct sl_shm* shm;
struct wl_resource* resource;
struct wl_shm* shm_proxy;
struct zwp_linux_dmabuf_v1* linux_dmabuf_proxy;
};
size_t sl_shm_bpp_for_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_NV12:
return 1;
case WL_SHM_FORMAT_RGB565:
return 2;
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XBGR8888:
return 4;
}
assert(0);
return 0;
}
size_t sl_shm_num_planes_for_shm_format(uint32_t format) {
switch (format) {
case WL_SHM_FORMAT_NV12:
return 2;
case WL_SHM_FORMAT_RGB565:
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XBGR8888:
return 1;
}
assert(0);
return 0;
}
static size_t sl_y_subsampling_for_shm_format_plane(uint32_t format,
size_t plane) {
switch (format) {
case WL_SHM_FORMAT_NV12: {
const size_t subsampling[] = {1, 2};
assert(plane < ARRAY_SIZE(subsampling));
return subsampling[plane];
}
case WL_SHM_FORMAT_RGB565:
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XBGR8888:
return 1;
}
assert(0);
return 0;
}
static int sl_offset_for_shm_format_plane(uint32_t format,
size_t height,
size_t stride,
size_t plane) {
switch (format) {
case WL_SHM_FORMAT_NV12: {
const size_t offset[] = {0, 1};
assert(plane < ARRAY_SIZE(offset));
return offset[plane] * height * stride;
}
case WL_SHM_FORMAT_RGB565:
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XBGR8888:
return 0;
}
assert(0);
return 0;
}
static size_t sl_size_for_shm_format_plane(uint32_t format,
size_t height,
size_t stride,
size_t plane) {
return height / sl_y_subsampling_for_shm_format_plane(format, plane) * stride;
}
static size_t sl_size_for_shm_format(uint32_t format,
size_t height,
size_t stride) {
size_t i, num_planes = sl_shm_num_planes_for_shm_format(format);
size_t total_size = 0;
for (i = 0; i < num_planes; ++i) {
size_t size = sl_size_for_shm_format_plane(format, height, stride, i);
size_t offset = sl_offset_for_shm_format_plane(format, height, stride, i);
total_size = MAX(total_size, size + offset);
}
return total_size;
}
static void sl_host_shm_pool_create_host_buffer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
int32_t offset,
int32_t width,
int32_t height,
int32_t stride,
uint32_t format) {
struct sl_host_shm_pool* host = wl_resource_get_user_data(resource);
if (host->shm->ctx->shm_driver == SHM_DRIVER_NOOP) {
assert(host->proxy);
sl_create_host_buffer(client, id,
wl_shm_pool_create_buffer(host->proxy, offset, width,
height, stride, format),
width, height);
} else {
struct sl_host_buffer* host_buffer =
sl_create_host_buffer(client, id, NULL, width, height);
host_buffer->shm_format = format;
host_buffer->shm_mmap = sl_mmap_create(
dup(host->fd), sl_size_for_shm_format(format, height, stride),
sl_shm_bpp_for_shm_format(format),
sl_shm_num_planes_for_shm_format(format), offset, stride,
offset + sl_offset_for_shm_format_plane(format, height, stride, 1),
stride, sl_y_subsampling_for_shm_format_plane(format, 0),
sl_y_subsampling_for_shm_format_plane(format, 1));
host_buffer->shm_mmap->buffer_resource = host_buffer->resource;
}
}
static void sl_host_shm_pool_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_host_shm_pool_resize(struct wl_client* client,
struct wl_resource* resource,
int32_t size) {
struct sl_host_shm_pool* host = wl_resource_get_user_data(resource);
if (host->proxy)
wl_shm_pool_resize(host->proxy, size);
}
static const struct wl_shm_pool_interface sl_shm_pool_implementation = {
sl_host_shm_pool_create_host_buffer, sl_host_shm_pool_destroy,
sl_host_shm_pool_resize};
static void sl_destroy_host_shm_pool(struct wl_resource* resource) {
struct sl_host_shm_pool* host = wl_resource_get_user_data(resource);
if (host->fd >= 0)
close(host->fd);
if (host->proxy)
wl_shm_pool_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_shm_create_host_pool(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
int fd,
int32_t size) {
struct sl_host_shm* host = wl_resource_get_user_data(resource);
struct sl_host_shm_pool* host_shm_pool;
host_shm_pool = malloc(sizeof(*host_shm_pool));
assert(host_shm_pool);
host_shm_pool->shm = host->shm;
host_shm_pool->fd = -1;
host_shm_pool->proxy = NULL;
host_shm_pool->resource =
wl_resource_create(client, &wl_shm_pool_interface, 1, id);
wl_resource_set_implementation(host_shm_pool->resource,
&sl_shm_pool_implementation, host_shm_pool,
sl_destroy_host_shm_pool);
switch (host->shm->ctx->shm_driver) {
case SHM_DRIVER_NOOP:
host_shm_pool->proxy = wl_shm_create_pool(host->shm_proxy, fd, size);
wl_shm_pool_set_user_data(host_shm_pool->proxy, host_shm_pool);
close(fd);
break;
case SHM_DRIVER_DMABUF:
case SHM_DRIVER_VIRTWL:
case SHM_DRIVER_VIRTWL_DMABUF:
host_shm_pool->fd = fd;
break;
}
}
static const struct wl_shm_interface sl_shm_implementation = {
sl_shm_create_host_pool};
static void sl_shm_format(void* data, struct wl_shm* shm, uint32_t format) {
struct sl_host_shm* host = wl_shm_get_user_data(shm);
switch (format) {
case WL_SHM_FORMAT_RGB565:
case WL_SHM_FORMAT_ARGB8888:
case WL_SHM_FORMAT_ABGR8888:
case WL_SHM_FORMAT_XRGB8888:
case WL_SHM_FORMAT_XBGR8888:
wl_shm_send_format(host->resource, format);
default:
break;
}
}
static const struct wl_shm_listener sl_shm_listener = {sl_shm_format};
static void sl_drm_format(void* data,
struct zwp_linux_dmabuf_v1* linux_dmabuf,
uint32_t format) {
struct sl_host_shm* host = zwp_linux_dmabuf_v1_get_user_data(linux_dmabuf);
// Forward SHM versions of supported formats.
switch (format) {
case WL_DRM_FORMAT_NV12:
wl_shm_send_format(host->resource, WL_SHM_FORMAT_NV12);
break;
case WL_DRM_FORMAT_RGB565:
wl_shm_send_format(host->resource, WL_SHM_FORMAT_RGB565);
break;
case WL_DRM_FORMAT_ARGB8888:
wl_shm_send_format(host->resource, WL_SHM_FORMAT_ARGB8888);
break;
case WL_DRM_FORMAT_ABGR8888:
wl_shm_send_format(host->resource, WL_SHM_FORMAT_ABGR8888);
break;
case WL_DRM_FORMAT_XRGB8888:
wl_shm_send_format(host->resource, WL_SHM_FORMAT_XRGB8888);
break;
case WL_DRM_FORMAT_XBGR8888:
wl_shm_send_format(host->resource, WL_SHM_FORMAT_XBGR8888);
break;
}
}
static void sl_drm_modifier(void* data,
struct zwp_linux_dmabuf_v1* linux_dmabuf,
uint32_t format,
uint32_t modifier_hi,
uint32_t modifier_lo) {}
static const struct zwp_linux_dmabuf_v1_listener sl_linux_dmabuf_listener = {
sl_drm_format, sl_drm_modifier};
static void sl_destroy_host_shm(struct wl_resource* resource) {
struct sl_host_shm* host = wl_resource_get_user_data(resource);
if (host->shm_proxy)
wl_shm_destroy(host->shm_proxy);
if (host->linux_dmabuf_proxy)
zwp_linux_dmabuf_v1_destroy(host->linux_dmabuf_proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_shm(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_shm* host;
host = malloc(sizeof(*host));
assert(host);
host->shm = ctx->shm;
host->shm_proxy = NULL;
host->linux_dmabuf_proxy = NULL;
host->resource = wl_resource_create(client, &wl_shm_interface, 1, id);
wl_resource_set_implementation(host->resource, &sl_shm_implementation, host,
sl_destroy_host_shm);
switch (ctx->shm_driver) {
case SHM_DRIVER_NOOP:
case SHM_DRIVER_VIRTWL:
host->shm_proxy = wl_registry_bind(
wl_display_get_registry(ctx->display), ctx->shm->id,
&wl_shm_interface, wl_resource_get_version(host->resource));
wl_shm_set_user_data(host->shm_proxy, host);
wl_shm_add_listener(host->shm_proxy, &sl_shm_listener, host);
break;
case SHM_DRIVER_VIRTWL_DMABUF:
case SHM_DRIVER_DMABUF:
assert(ctx->linux_dmabuf);
host->linux_dmabuf_proxy = wl_registry_bind(
wl_display_get_registry(ctx->display), ctx->linux_dmabuf->id,
&zwp_linux_dmabuf_v1_interface,
wl_resource_get_version(host->resource));
zwp_linux_dmabuf_v1_set_user_data(host->linux_dmabuf_proxy, host);
zwp_linux_dmabuf_v1_add_listener(host->linux_dmabuf_proxy,
&sl_linux_dmabuf_listener, host);
break;
}
}
struct sl_global* sl_shm_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wl_shm_interface, 1, ctx, sl_bind_host_shm);
}

View File

@ -0,0 +1,153 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <wayland-client.h>
struct sl_host_subcompositor {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_subcompositor* proxy;
};
struct sl_host_subsurface {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_subsurface* proxy;
};
static void sl_subsurface_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_subsurface_set_position(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y) {
struct sl_host_subsurface* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
wl_subsurface_set_position(host->proxy, x / scale, y / scale);
}
static void sl_subsurface_place_above(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* sibling_resource) {
struct sl_host_subsurface* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_sibling =
wl_resource_get_user_data(sibling_resource);
wl_subsurface_place_above(host->proxy, host_sibling->proxy);
}
static void sl_subsurface_place_below(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* sibling_resource) {
struct sl_host_subsurface* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_sibling =
wl_resource_get_user_data(sibling_resource);
wl_subsurface_place_below(host->proxy, host_sibling->proxy);
}
static void sl_subsurface_set_sync(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_subsurface* host = wl_resource_get_user_data(resource);
wl_subsurface_set_sync(host->proxy);
}
static void sl_subsurface_set_desync(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_subsurface* host = wl_resource_get_user_data(resource);
wl_subsurface_set_desync(host->proxy);
}
static const struct wl_subsurface_interface sl_subsurface_implementation = {
sl_subsurface_destroy, sl_subsurface_set_position,
sl_subsurface_place_above, sl_subsurface_place_below,
sl_subsurface_set_sync, sl_subsurface_set_desync};
static void sl_destroy_host_subsurface(struct wl_resource* resource) {
struct sl_host_subsurface* host = wl_resource_get_user_data(resource);
wl_subsurface_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_subcompositor_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_subcompositor_get_subsurface(
struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface_resource,
struct wl_resource* parent_resource) {
struct sl_host_subcompositor* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_surface =
wl_resource_get_user_data(surface_resource);
struct sl_host_surface* host_parent =
wl_resource_get_user_data(parent_resource);
struct sl_host_subsurface* host_subsurface;
host_subsurface = malloc(sizeof(*host_subsurface));
assert(host_subsurface);
host_subsurface->ctx = host->ctx;
host_subsurface->resource =
wl_resource_create(client, &wl_subsurface_interface, 1, id);
wl_resource_set_implementation(host_subsurface->resource,
&sl_subsurface_implementation, host_subsurface,
sl_destroy_host_subsurface);
host_subsurface->proxy = wl_subcompositor_get_subsurface(
host->proxy, host_surface->proxy, host_parent->proxy);
wl_subsurface_set_user_data(host_subsurface->proxy, host_subsurface);
host_surface->has_role = 1;
}
static const struct wl_subcompositor_interface sl_subcompositor_implementation =
{sl_subcompositor_destroy, sl_subcompositor_get_subsurface};
static void sl_destroy_host_subcompositor(struct wl_resource* resource) {
struct sl_host_subcompositor* host = wl_resource_get_user_data(resource);
wl_subcompositor_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_subcompositor(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_subcompositor* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->resource =
wl_resource_create(client, &wl_subcompositor_interface, 1, id);
wl_resource_set_implementation(host->resource,
&sl_subcompositor_implementation, host,
sl_destroy_host_subcompositor);
host->proxy =
wl_registry_bind(wl_display_get_registry(ctx->display),
ctx->subcompositor->id, &wl_subcompositor_interface, 1);
wl_subcompositor_set_user_data(host->proxy, host);
}
struct sl_global* sl_subcompositor_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wl_subcompositor_interface, 1, ctx,
sl_bind_host_subcompositor);
}

View File

@ -0,0 +1,332 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "text-input-unstable-v1-client-protocol.h"
#include "text-input-unstable-v1-server-protocol.h"
struct sl_host_text_input_manager {
struct sl_context* ctx;
struct wl_resource* resource;
struct zwp_text_input_manager_v1* proxy;
};
struct sl_host_text_input {
struct sl_context* ctx;
struct wl_resource* resource;
struct zwp_text_input_v1* proxy;
};
static void sl_text_input_activate(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat,
struct wl_resource* surface) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat);
struct sl_host_surface* host_surface = wl_resource_get_user_data(surface);
zwp_text_input_v1_activate(host->proxy, host_seat->proxy,
host_surface->proxy);
}
static void sl_text_input_deactivate(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat);
zwp_text_input_v1_deactivate(host->proxy, host_seat->proxy);
}
static void sl_text_input_show_input_panel(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_show_input_panel(host->proxy);
}
static void sl_text_input_hide_input_panel(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_hide_input_panel(host->proxy);
}
static void sl_text_input_reset(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_reset(host->proxy);
}
static void sl_text_input_set_surrounding_text(struct wl_client* client,
struct wl_resource* resource,
const char* text,
uint32_t cursor,
uint32_t anchor) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_set_surrounding_text(host->proxy, text, cursor, anchor);
}
static void sl_text_input_set_content_type(struct wl_client* client,
struct wl_resource* resource,
uint32_t hint,
uint32_t purpose) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_set_content_type(host->proxy, hint, purpose);
}
static void sl_text_input_set_cursor_rectangle(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_set_cursor_rectangle(host->proxy, x, y, width, height);
}
static void sl_text_input_set_preferred_language(struct wl_client* client,
struct wl_resource* resource,
const char* language) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_set_preferred_language(host->proxy, language);
}
static void sl_text_input_commit_state(struct wl_client* client,
struct wl_resource* resource,
uint32_t serial) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_commit_state(host->proxy, serial);
}
static void sl_text_input_invoke_action(struct wl_client* client,
struct wl_resource* resource,
uint32_t button,
uint32_t index) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_invoke_action(host->proxy, button, index);
}
static const struct zwp_text_input_v1_interface sl_text_input_implementation = {
sl_text_input_activate,
sl_text_input_deactivate,
sl_text_input_show_input_panel,
sl_text_input_hide_input_panel,
sl_text_input_reset,
sl_text_input_set_surrounding_text,
sl_text_input_set_content_type,
sl_text_input_set_cursor_rectangle,
sl_text_input_set_preferred_language,
sl_text_input_commit_state,
sl_text_input_invoke_action,
};
static void sl_text_input_enter(void* data,
struct zwp_text_input_v1* text_input,
struct wl_surface* surface) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
struct sl_host_surface* host_surface = wl_surface_get_user_data(surface);
zwp_text_input_v1_send_enter(host->resource, host_surface->resource);
}
static void sl_text_input_leave(void* data,
struct zwp_text_input_v1* text_input) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_leave(host->resource);
}
static void sl_text_input_modifiers_map(void* data,
struct zwp_text_input_v1* text_input,
struct wl_array* map) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_modifiers_map(host->resource, map);
}
static void sl_text_input_input_panel_state(
void* data, struct zwp_text_input_v1* text_input, uint32_t state) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_input_panel_state(host->resource, state);
}
static void sl_text_input_preedit_string(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text,
const char* commit) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_preedit_string(host->resource, serial, text, commit);
}
static void sl_text_input_preedit_styling(void* data,
struct zwp_text_input_v1* text_input,
uint32_t index,
uint32_t length,
uint32_t style) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_preedit_styling(host->resource, index, length, style);
}
static void sl_text_input_preedit_cursor(void* data,
struct zwp_text_input_v1* text_input,
int32_t index) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_preedit_cursor(host->resource, index);
}
static void sl_text_input_commit_string(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* text) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_commit_string(host->resource, serial, text);
}
static void sl_text_input_cursor_position(void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
int32_t anchor) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_cursor_position(host->resource, index, anchor);
}
static void sl_text_input_delete_surrounding_text(
void* data,
struct zwp_text_input_v1* text_input,
int32_t index,
uint32_t length) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_delete_surrounding_text(host->resource, index, length);
}
static void sl_text_input_keysym(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t time,
uint32_t sym,
uint32_t state,
uint32_t modifiers) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_keysym(host->resource, serial, time, sym, state,
modifiers);
}
static void sl_text_input_language(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
const char* language) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_language(host->resource, serial, language);
}
static void sl_text_input_text_direction(void* data,
struct zwp_text_input_v1* text_input,
uint32_t serial,
uint32_t direction) {
struct sl_host_text_input* host = zwp_text_input_v1_get_user_data(text_input);
zwp_text_input_v1_send_text_direction(host->resource, serial, direction);
}
static const struct zwp_text_input_v1_listener sl_text_input_listener = {
sl_text_input_enter, sl_text_input_leave,
sl_text_input_modifiers_map, sl_text_input_input_panel_state,
sl_text_input_preedit_string, sl_text_input_preedit_styling,
sl_text_input_preedit_cursor, sl_text_input_commit_string,
sl_text_input_cursor_position, sl_text_input_delete_surrounding_text,
sl_text_input_keysym, sl_text_input_language,
sl_text_input_text_direction,
};
static void sl_destroy_host_text_input(struct wl_resource* resource) {
struct sl_host_text_input* host = wl_resource_get_user_data(resource);
zwp_text_input_v1_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_text_input_manager_create_text_input(
struct wl_client* client, struct wl_resource* resource, uint32_t id) {
struct sl_host_text_input_manager* host = wl_resource_get_user_data(resource);
struct wl_resource* text_input_resource =
wl_resource_create(client, &zwp_text_input_v1_interface, 1, id);
struct sl_host_text_input* text_input_host =
malloc(sizeof(struct sl_host_text_input));
text_input_host->resource = text_input_resource;
text_input_host->ctx = host->ctx;
text_input_host->proxy = zwp_text_input_manager_v1_create_text_input(
host->ctx->text_input_manager->internal);
wl_resource_set_implementation(text_input_resource,
&sl_text_input_implementation, text_input_host,
sl_destroy_host_text_input);
zwp_text_input_v1_set_user_data(text_input_host->proxy, text_input_host);
zwp_text_input_v1_add_listener(text_input_host->proxy,
&sl_text_input_listener, text_input_host);
}
static void sl_destroy_host_text_input_manager(struct wl_resource* resource) {
struct sl_host_text_input_manager* host = wl_resource_get_user_data(resource);
zwp_text_input_manager_v1_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static struct zwp_text_input_manager_v1_interface
sl_text_input_manager_implementation = {
sl_text_input_manager_create_text_input,
};
static void sl_bind_host_text_input_manager(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_text_input_manager* text_input_manager = ctx->text_input_manager;
struct sl_host_text_input_manager* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->resource =
wl_resource_create(client, &zwp_text_input_manager_v1_interface, 1, id);
wl_resource_set_implementation(host->resource,
&sl_text_input_manager_implementation, host,
sl_destroy_host_text_input_manager);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
text_input_manager->id,
&zwp_text_input_manager_v1_interface,
wl_resource_get_version(host->resource));
zwp_text_input_manager_v1_set_user_data(host->proxy, host);
}
struct sl_global* sl_text_input_manager_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &zwp_text_input_manager_v1_interface, 1, ctx,
sl_bind_host_text_input_manager);
}

View File

@ -0,0 +1,128 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include "viewporter-client-protocol.h"
#include "viewporter-server-protocol.h"
struct sl_host_viewporter {
struct sl_viewporter* viewporter;
struct wl_resource* resource;
struct wp_viewporter* proxy;
};
struct sl_host_viewport {
struct wl_resource* resource;
struct sl_viewport viewport;
};
static void sl_viewport_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_viewport_set_source(struct wl_client* client,
struct wl_resource* resource,
wl_fixed_t x,
wl_fixed_t y,
wl_fixed_t width,
wl_fixed_t height) {
struct sl_host_viewport* host = wl_resource_get_user_data(resource);
host->viewport.src_x = x;
host->viewport.src_y = y;
host->viewport.src_width = width;
host->viewport.src_height = height;
}
static void sl_viewport_set_destination(struct wl_client* client,
struct wl_resource* resource,
int32_t width,
int32_t height) {
struct sl_host_viewport* host = wl_resource_get_user_data(resource);
host->viewport.dst_width = width;
host->viewport.dst_height = height;
}
static const struct wp_viewport_interface sl_viewport_implementation = {
sl_viewport_destroy, sl_viewport_set_source, sl_viewport_set_destination};
static void sl_destroy_host_viewport(struct wl_resource* resource) {
struct sl_host_viewport* host = wl_resource_get_user_data(resource);
wl_resource_set_user_data(resource, NULL);
wl_list_remove(&host->viewport.link);
free(host);
}
static void sl_viewporter_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_viewporter_get_viewport(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface_resource) {
struct sl_host_surface* host_surface =
wl_resource_get_user_data(surface_resource);
struct sl_host_viewport* host_viewport;
host_viewport = malloc(sizeof(*host_viewport));
assert(host_viewport);
host_viewport->viewport.src_x = -1;
host_viewport->viewport.src_y = -1;
host_viewport->viewport.src_width = -1;
host_viewport->viewport.src_height = -1;
host_viewport->viewport.dst_width = -1;
host_viewport->viewport.dst_height = -1;
wl_list_insert(&host_surface->contents_viewport,
&host_viewport->viewport.link);
host_viewport->resource =
wl_resource_create(client, &wp_viewport_interface, 1, id);
wl_resource_set_implementation(host_viewport->resource,
&sl_viewport_implementation, host_viewport,
sl_destroy_host_viewport);
}
static const struct wp_viewporter_interface sl_viewporter_implementation = {
sl_viewporter_destroy, sl_viewporter_get_viewport};
static void sl_destroy_host_viewporter(struct wl_resource* resource) {
struct sl_host_viewporter* host = wl_resource_get_user_data(resource);
wp_viewporter_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_viewporter(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_viewporter* host;
host = malloc(sizeof(*host));
assert(host);
host->viewporter = ctx->viewporter;
host->resource = wl_resource_create(client, &wp_viewporter_interface, 1, id);
wl_resource_set_implementation(host->resource, &sl_viewporter_implementation,
host, sl_destroy_host_viewporter);
host->proxy =
wl_registry_bind(wl_display_get_registry(ctx->display),
ctx->viewporter->id, &wp_viewporter_interface, 1);
wp_viewporter_set_user_data(host->proxy, host);
}
struct sl_global* sl_viewporter_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &wp_viewporter_interface, 1, ctx,
sl_bind_host_viewporter);
}

View File

@ -0,0 +1,563 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include "xdg-shell-unstable-v6-client-protocol.h"
#include "xdg-shell-unstable-v6-server-protocol.h"
struct sl_host_xdg_shell {
struct sl_context* ctx;
struct wl_resource* resource;
struct zxdg_shell_v6* proxy;
};
struct sl_host_xdg_surface {
struct sl_context* ctx;
struct wl_resource* resource;
struct zxdg_surface_v6* proxy;
};
struct sl_host_xdg_toplevel {
struct sl_context* ctx;
struct wl_resource* resource;
struct zxdg_toplevel_v6* proxy;
};
struct sl_host_xdg_popup {
struct sl_context* ctx;
struct wl_resource* resource;
struct zxdg_popup_v6* proxy;
};
struct sl_host_xdg_positioner {
struct sl_context* ctx;
struct wl_resource* resource;
struct zxdg_positioner_v6* proxy;
};
static void sl_xdg_positioner_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_xdg_positioner_set_size(struct wl_client* client,
struct wl_resource* resource,
int32_t width,
int32_t height) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
zxdg_positioner_v6_set_size(host->proxy, width / scale, height / scale);
}
static void sl_xdg_positioner_set_anchor_rect(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
zxdg_positioner_v6_set_anchor_rect(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void sl_xdg_positioner_set_anchor(struct wl_client* client,
struct wl_resource* resource,
uint32_t anchor) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_set_anchor(host->proxy, anchor);
}
static void sl_xdg_positioner_set_gravity(struct wl_client* client,
struct wl_resource* resource,
uint32_t gravity) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_set_gravity(host->proxy, gravity);
}
static void sl_xdg_positioner_set_constraint_adjustment(
struct wl_client* client,
struct wl_resource* resource,
uint32_t constraint_adjustment) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_set_constraint_adjustment(host->proxy,
constraint_adjustment);
}
static void sl_xdg_positioner_set_offset(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
zxdg_positioner_v6_set_offset(host->proxy, x / scale, y / scale);
}
static const struct zxdg_positioner_v6_interface
sl_xdg_positioner_implementation = {
sl_xdg_positioner_destroy,
sl_xdg_positioner_set_size,
sl_xdg_positioner_set_anchor_rect,
sl_xdg_positioner_set_anchor,
sl_xdg_positioner_set_gravity,
sl_xdg_positioner_set_constraint_adjustment,
sl_xdg_positioner_set_offset};
static void sl_destroy_host_xdg_positioner(struct wl_resource* resource) {
struct sl_host_xdg_positioner* host = wl_resource_get_user_data(resource);
zxdg_positioner_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_xdg_popup_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_xdg_popup_grab(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial) {
struct sl_host_xdg_popup* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat = wl_resource_get_user_data(seat_resource);
zxdg_popup_v6_grab(host->proxy, host_seat->proxy, serial);
}
static const struct zxdg_popup_v6_interface sl_xdg_popup_implementation = {
sl_xdg_popup_destroy, sl_xdg_popup_grab};
static void sl_xdg_popup_configure(void* data,
struct zxdg_popup_v6* xdg_popup,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_xdg_popup* host = zxdg_popup_v6_get_user_data(xdg_popup);
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x * scale;
y1 = y * scale;
x2 = (x + width) * scale;
y2 = (y + height) * scale;
zxdg_popup_v6_send_configure(host->resource, x1, y1, x2 - x1, y2 - y1);
}
static void sl_xdg_popup_popup_done(void* data,
struct zxdg_popup_v6* xdg_popup) {
struct sl_host_xdg_popup* host = zxdg_popup_v6_get_user_data(xdg_popup);
zxdg_popup_v6_send_popup_done(host->resource);
}
static const struct zxdg_popup_v6_listener sl_xdg_popup_listener = {
sl_xdg_popup_configure, sl_xdg_popup_popup_done};
static void sl_destroy_host_xdg_popup(struct wl_resource* resource) {
struct sl_host_xdg_popup* host = wl_resource_get_user_data(resource);
zxdg_popup_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_xdg_toplevel_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_xdg_toplevel_set_parent(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* parent_resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
struct sl_host_xdg_toplevel* host_parent =
parent_resource ? wl_resource_get_user_data(parent_resource) : NULL;
zxdg_toplevel_v6_set_parent(host->proxy,
host_parent ? host_parent->proxy : NULL);
}
static void sl_xdg_toplevel_set_title(struct wl_client* client,
struct wl_resource* resource,
const char* title) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_title(host->proxy, title);
}
static void sl_xdg_toplevel_set_app_id(struct wl_client* client,
struct wl_resource* resource,
const char* app_id) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_app_id(host->proxy, app_id);
}
static void sl_xdg_toplevel_show_window_menu(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial,
int32_t x,
int32_t y) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat =
seat_resource ? wl_resource_get_user_data(seat_resource) : NULL;
zxdg_toplevel_v6_show_window_menu(
host->proxy, host_seat ? host_seat->proxy : NULL, serial, x, y);
}
static void sl_xdg_toplevel_move(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat =
seat_resource ? wl_resource_get_user_data(seat_resource) : NULL;
zxdg_toplevel_v6_move(host->proxy, host_seat ? host_seat->proxy : NULL,
serial);
}
static void sl_xdg_toplevel_resize(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* seat_resource,
uint32_t serial,
uint32_t edges) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
struct sl_host_seat* host_seat =
seat_resource ? wl_resource_get_user_data(seat_resource) : NULL;
zxdg_toplevel_v6_resize(host->proxy, host_seat ? host_seat->proxy : NULL,
serial, edges);
}
static void sl_xdg_toplevel_set_max_size(struct wl_client* client,
struct wl_resource* resource,
int32_t width,
int32_t height) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_max_size(host->proxy, width, height);
}
static void sl_xdg_toplevel_set_min_size(struct wl_client* client,
struct wl_resource* resource,
int32_t width,
int32_t height) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_min_size(host->proxy, width, height);
}
static void sl_xdg_toplevel_set_maximized(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_maximized(host->proxy);
}
static void sl_xdg_toplevel_unset_maximized(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_unset_maximized(host->proxy);
}
static void sl_xdg_toplevel_set_fullscreen(
struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* output_resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
struct sl_host_output* host_output =
output_resource ? wl_resource_get_user_data(resource) : NULL;
zxdg_toplevel_v6_set_fullscreen(host->proxy,
host_output ? host_output->proxy : NULL);
}
static void sl_xdg_toplevel_unset_fullscreen(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_unset_fullscreen(host->proxy);
}
static void sl_xdg_toplevel_set_minimized(struct wl_client* client,
struct wl_resource* resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_set_minimized(host->proxy);
}
static const struct zxdg_toplevel_v6_interface sl_xdg_toplevel_implementation =
{sl_xdg_toplevel_destroy, sl_xdg_toplevel_set_parent,
sl_xdg_toplevel_set_title, sl_xdg_toplevel_set_app_id,
sl_xdg_toplevel_show_window_menu, sl_xdg_toplevel_move,
sl_xdg_toplevel_resize, sl_xdg_toplevel_set_max_size,
sl_xdg_toplevel_set_min_size, sl_xdg_toplevel_set_maximized,
sl_xdg_toplevel_unset_maximized, sl_xdg_toplevel_set_fullscreen,
sl_xdg_toplevel_unset_fullscreen, sl_xdg_toplevel_set_minimized};
static void sl_xdg_toplevel_configure(void* data,
struct zxdg_toplevel_v6* xdg_toplevel,
int32_t width,
int32_t height,
struct wl_array* states) {
struct sl_host_xdg_toplevel* host =
zxdg_toplevel_v6_get_user_data(xdg_toplevel);
double scale = host->ctx->scale;
zxdg_toplevel_v6_send_configure(host->resource, width * scale, height * scale,
states);
}
static void sl_xdg_toplevel_close(void* data,
struct zxdg_toplevel_v6* xdg_toplevel) {
struct sl_host_xdg_toplevel* host =
zxdg_toplevel_v6_get_user_data(xdg_toplevel);
zxdg_toplevel_v6_send_close(host->resource);
}
static const struct zxdg_toplevel_v6_listener sl_xdg_toplevel_listener = {
sl_xdg_toplevel_configure, sl_xdg_toplevel_close};
static void sl_destroy_host_xdg_toplevel(struct wl_resource* resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
zxdg_toplevel_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_xdg_surface_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_xdg_surface_get_toplevel(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_xdg_surface* host = wl_resource_get_user_data(resource);
struct sl_host_xdg_toplevel* host_xdg_toplevel;
host_xdg_toplevel = malloc(sizeof(*host_xdg_toplevel));
assert(host_xdg_toplevel);
host_xdg_toplevel->ctx = host->ctx;
host_xdg_toplevel->resource =
wl_resource_create(client, &zxdg_toplevel_v6_interface, 1, id);
wl_resource_set_implementation(
host_xdg_toplevel->resource, &sl_xdg_toplevel_implementation,
host_xdg_toplevel, sl_destroy_host_xdg_toplevel);
host_xdg_toplevel->proxy = zxdg_surface_v6_get_toplevel(host->proxy);
zxdg_toplevel_v6_set_user_data(host_xdg_toplevel->proxy, host_xdg_toplevel);
zxdg_toplevel_v6_add_listener(host_xdg_toplevel->proxy,
&sl_xdg_toplevel_listener, host_xdg_toplevel);
}
static void sl_xdg_surface_get_popup(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* parent_resource,
struct wl_resource* positioner_resource) {
struct sl_host_xdg_surface* host = wl_resource_get_user_data(resource);
struct sl_host_xdg_surface* host_parent =
wl_resource_get_user_data(parent_resource);
struct sl_host_xdg_positioner* host_positioner =
wl_resource_get_user_data(positioner_resource);
struct sl_host_xdg_popup* host_xdg_popup;
host_xdg_popup = malloc(sizeof(*host_xdg_popup));
assert(host_xdg_popup);
host_xdg_popup->ctx = host->ctx;
host_xdg_popup->resource =
wl_resource_create(client, &zxdg_popup_v6_interface, 1, id);
wl_resource_set_implementation(host_xdg_popup->resource,
&sl_xdg_popup_implementation, host_xdg_popup,
sl_destroy_host_xdg_popup);
host_xdg_popup->proxy = zxdg_surface_v6_get_popup(
host->proxy, host_parent->proxy, host_positioner->proxy);
zxdg_popup_v6_set_user_data(host_xdg_popup->proxy, host_xdg_popup);
zxdg_popup_v6_add_listener(host_xdg_popup->proxy, &sl_xdg_popup_listener,
host_xdg_popup);
}
static void sl_xdg_surface_set_window_geometry(struct wl_client* client,
struct wl_resource* resource,
int32_t x,
int32_t y,
int32_t width,
int32_t height) {
struct sl_host_xdg_surface* host = wl_resource_get_user_data(resource);
double scale = host->ctx->scale;
int32_t x1, y1, x2, y2;
x1 = x / scale;
y1 = y / scale;
x2 = (x + width) / scale;
y2 = (y + height) / scale;
zxdg_surface_v6_set_window_geometry(host->proxy, x1, y1, x2 - x1, y2 - y1);
}
static void sl_xdg_surface_ack_configure(struct wl_client* client,
struct wl_resource* resource,
uint32_t serial) {
struct sl_host_xdg_surface* host = wl_resource_get_user_data(resource);
zxdg_surface_v6_ack_configure(host->proxy, serial);
}
static const struct zxdg_surface_v6_interface sl_xdg_surface_implementation = {
sl_xdg_surface_destroy, sl_xdg_surface_get_toplevel,
sl_xdg_surface_get_popup, sl_xdg_surface_set_window_geometry,
sl_xdg_surface_ack_configure};
static void sl_xdg_surface_configure(void* data,
struct zxdg_surface_v6* xdg_surface,
uint32_t serial) {
struct sl_host_xdg_surface* host = zxdg_surface_v6_get_user_data(xdg_surface);
zxdg_surface_v6_send_configure(host->resource, serial);
}
static const struct zxdg_surface_v6_listener sl_xdg_surface_listener = {
sl_xdg_surface_configure};
static void sl_destroy_host_xdg_surface(struct wl_resource* resource) {
struct sl_host_xdg_surface* host = wl_resource_get_user_data(resource);
zxdg_surface_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_xdg_shell_destroy(struct wl_client* client,
struct wl_resource* resource) {
wl_resource_destroy(resource);
}
static void sl_xdg_shell_create_positioner(struct wl_client* client,
struct wl_resource* resource,
uint32_t id) {
struct sl_host_xdg_shell* host = wl_resource_get_user_data(resource);
struct sl_host_xdg_positioner* host_xdg_positioner;
host_xdg_positioner = malloc(sizeof(*host_xdg_positioner));
assert(host_xdg_positioner);
host_xdg_positioner->ctx = host->ctx;
host_xdg_positioner->resource =
wl_resource_create(client, &zxdg_positioner_v6_interface, 1, id);
wl_resource_set_implementation(
host_xdg_positioner->resource, &sl_xdg_positioner_implementation,
host_xdg_positioner, sl_destroy_host_xdg_positioner);
host_xdg_positioner->proxy = zxdg_shell_v6_create_positioner(host->proxy);
zxdg_positioner_v6_set_user_data(host_xdg_positioner->proxy,
host_xdg_positioner);
}
static void sl_xdg_shell_get_xdg_surface(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface_resource) {
struct sl_host_xdg_shell* host = wl_resource_get_user_data(resource);
struct sl_host_surface* host_surface =
wl_resource_get_user_data(surface_resource);
struct sl_host_xdg_surface* host_xdg_surface;
host_xdg_surface = malloc(sizeof(*host_xdg_surface));
assert(host_xdg_surface);
host_xdg_surface->ctx = host->ctx;
host_xdg_surface->resource =
wl_resource_create(client, &zxdg_surface_v6_interface, 1, id);
wl_resource_set_implementation(host_xdg_surface->resource,
&sl_xdg_surface_implementation,
host_xdg_surface, sl_destroy_host_xdg_surface);
host_xdg_surface->proxy =
zxdg_shell_v6_get_xdg_surface(host->proxy, host_surface->proxy);
zxdg_surface_v6_set_user_data(host_xdg_surface->proxy, host_xdg_surface);
zxdg_surface_v6_add_listener(host_xdg_surface->proxy,
&sl_xdg_surface_listener, host_xdg_surface);
host_surface->has_role = 1;
}
static void sl_xdg_shell_pong(struct wl_client* client,
struct wl_resource* resource,
uint32_t serial) {
struct sl_host_xdg_shell* host = wl_resource_get_user_data(resource);
zxdg_shell_v6_pong(host->proxy, serial);
}
static const struct zxdg_shell_v6_interface sl_xdg_shell_implementation = {
sl_xdg_shell_destroy, sl_xdg_shell_create_positioner,
sl_xdg_shell_get_xdg_surface, sl_xdg_shell_pong};
static void sl_xdg_shell_ping(void* data,
struct zxdg_shell_v6* xdg_shell,
uint32_t serial) {
struct sl_host_xdg_shell* host = zxdg_shell_v6_get_user_data(xdg_shell);
zxdg_shell_v6_send_ping(host->resource, serial);
}
static const struct zxdg_shell_v6_listener sl_xdg_shell_listener = {
sl_xdg_shell_ping};
static void sl_destroy_host_xdg_shell(struct wl_resource* resource) {
struct sl_host_xdg_shell* host = wl_resource_get_user_data(resource);
zxdg_shell_v6_destroy(host->proxy);
wl_resource_set_user_data(resource, NULL);
free(host);
}
static void sl_bind_host_xdg_shell(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_host_xdg_shell* host;
host = malloc(sizeof(*host));
assert(host);
host->ctx = ctx;
host->resource = wl_resource_create(client, &zxdg_shell_v6_interface, 1, id);
wl_resource_set_implementation(host->resource, &sl_xdg_shell_implementation,
host, sl_destroy_host_xdg_shell);
host->proxy =
wl_registry_bind(wl_display_get_registry(ctx->display),
ctx->xdg_shell->id, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_set_user_data(host->proxy, host);
zxdg_shell_v6_add_listener(host->proxy, &sl_xdg_shell_listener, host);
}
struct sl_global* sl_xdg_shell_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &zxdg_shell_v6_interface, 1, ctx,
sl_bind_host_xdg_shell);
}

4146
sommelier/sommelier.c Normal file

File diff suppressed because it is too large Load Diff

547
sommelier/sommelier.h Normal file
View File

@ -0,0 +1,547 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef VM_TOOLS_SOMMELIER_SOMMELIER_H_
#define VM_TOOLS_SOMMELIER_SOMMELIER_H_
#include <sys/types.h>
#include <wayland-server.h>
#include <wayland-util.h>
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
#include "config.h"
#define SOMMELIER_VERSION "0.20"
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define UNUSED(x) ((void)(x))
#define CONTROL_MASK (1 << 0)
#define ALT_MASK (1 << 1)
#define SHIFT_MASK (1 << 2)
struct sl_global;
struct sl_compositor;
struct sl_shm;
struct sl_shell;
struct sl_data_device_manager;
struct sl_data_offer;
struct sl_data_source;
struct sl_xdg_shell;
struct sl_subcompositor;
struct sl_aura_shell;
struct sl_viewporter;
struct sl_linux_dmabuf;
struct sl_keyboard_extension;
struct sl_text_input_manager;
struct sl_relative_pointer_manager;
struct sl_window;
struct zaura_shell;
struct zcr_keyboard_extension_v1;
enum {
ATOM_WM_S0,
ATOM_WM_PROTOCOLS,
ATOM_WM_STATE,
ATOM_WM_CHANGE_STATE,
ATOM_WM_DELETE_WINDOW,
ATOM_WM_TAKE_FOCUS,
ATOM_WM_CLIENT_LEADER,
ATOM_WL_SURFACE_ID,
ATOM_UTF8_STRING,
ATOM_MOTIF_WM_HINTS,
ATOM_NET_FRAME_EXTENTS,
ATOM_NET_STARTUP_ID,
ATOM_NET_SUPPORTING_WM_CHECK,
ATOM_NET_WM_NAME,
ATOM_NET_WM_MOVERESIZE,
ATOM_NET_WM_STATE,
ATOM_NET_WM_STATE_FULLSCREEN,
ATOM_NET_WM_STATE_MAXIMIZED_VERT,
ATOM_NET_WM_STATE_MAXIMIZED_HORZ,
ATOM_CLIPBOARD,
ATOM_CLIPBOARD_MANAGER,
ATOM_TARGETS,
ATOM_TIMESTAMP,
ATOM_TEXT,
ATOM_INCR,
ATOM_WL_SELECTION,
ATOM_GTK_THEME_VARIANT,
ATOM_LAST = ATOM_GTK_THEME_VARIANT,
};
enum {
SHM_DRIVER_NOOP,
SHM_DRIVER_DMABUF,
SHM_DRIVER_VIRTWL,
SHM_DRIVER_VIRTWL_DMABUF,
};
enum {
DATA_DRIVER_NOOP,
DATA_DRIVER_VIRTWL,
};
struct sl_context {
char** runprog;
struct wl_display* display;
struct wl_display* host_display;
struct wl_client* client;
struct sl_compositor* compositor;
struct sl_subcompositor* subcompositor;
struct sl_shm* shm;
struct sl_shell* shell;
struct sl_data_device_manager* data_device_manager;
struct sl_xdg_shell* xdg_shell;
struct sl_aura_shell* aura_shell;
struct sl_viewporter* viewporter;
struct sl_linux_dmabuf* linux_dmabuf;
struct sl_keyboard_extension* keyboard_extension;
struct sl_text_input_manager* text_input_manager;
struct sl_relative_pointer_manager* relative_pointer_manager;
struct wl_list outputs;
struct wl_list seats;
struct wl_event_source* display_event_source;
struct wl_event_source* display_ready_event_source;
struct wl_event_source* sigchld_event_source;
struct wl_array dpi;
int shm_driver;
int data_driver;
int wm_fd;
int virtwl_fd;
int virtwl_ctx_fd;
int virtwl_socket_fd;
struct wl_event_source* virtwl_ctx_event_source;
struct wl_event_source* virtwl_socket_event_source;
const char* drm_device;
struct gbm_device* gbm;
int xwayland;
pid_t xwayland_pid;
pid_t child_pid;
pid_t peer_pid;
struct xkb_context* xkb_context;
struct wl_list accelerators;
struct wl_list registries;
struct wl_list globals;
struct wl_list host_outputs;
int next_global_id;
xcb_connection_t* connection;
struct wl_event_source* connection_event_source;
const xcb_query_extension_reply_t* xfixes_extension;
xcb_screen_t* screen;
xcb_window_t window;
struct wl_list windows, unpaired_windows;
struct sl_window* host_focus_window;
int needs_set_input_focus;
double desired_scale;
double scale;
const char* application_id;
int exit_with_child;
const char* sd_notify;
int clipboard_manager;
uint32_t frame_color;
uint32_t dark_frame_color;
struct sl_host_seat* default_seat;
xcb_window_t selection_window;
xcb_window_t selection_owner;
int selection_incremental_transfer;
xcb_selection_request_event_t selection_request;
xcb_timestamp_t selection_timestamp;
struct wl_data_device* selection_data_device;
struct sl_data_offer* selection_data_offer;
struct sl_data_source* selection_data_source;
int selection_data_source_send_fd;
struct wl_list selection_data_source_send_pending;
struct wl_event_source* selection_send_event_source;
xcb_get_property_reply_t* selection_property_reply;
int selection_property_offset;
struct wl_event_source* selection_event_source;
xcb_atom_t selection_data_type;
struct wl_array selection_data;
int selection_data_offer_receive_fd;
int selection_data_ack_pending;
union {
const char* name;
xcb_intern_atom_cookie_t cookie;
xcb_atom_t value;
} atoms[ATOM_LAST + 1];
xcb_visualid_t visual_ids[256];
xcb_colormap_t colormaps[256];
};
struct sl_compositor {
struct sl_context* ctx;
uint32_t id;
uint32_t version;
struct sl_global* host_global;
struct wl_compositor* internal;
};
struct sl_shm {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
struct wl_shm* internal;
};
struct sl_seat {
struct sl_context* ctx;
uint32_t id;
uint32_t version;
struct sl_global* host_global;
uint32_t last_serial;
struct wl_list link;
};
struct sl_host_pointer {
struct sl_seat* seat;
struct wl_resource* resource;
struct wl_pointer* proxy;
struct wl_resource* focus_resource;
struct wl_listener focus_resource_listener;
uint32_t focus_serial;
};
struct sl_relative_pointer_manager {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
struct zwp_relative_pointer_manager_v1* internal;
};
struct sl_viewport {
struct wl_list link;
wl_fixed_t src_x;
wl_fixed_t src_y;
wl_fixed_t src_width;
wl_fixed_t src_height;
int32_t dst_width;
int32_t dst_height;
};
struct sl_host_callback {
struct wl_resource* resource;
struct wl_callback* proxy;
};
struct sl_host_surface {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_surface* proxy;
struct wp_viewport* viewport;
uint32_t contents_width;
uint32_t contents_height;
int32_t contents_scale;
struct wl_list contents_viewport;
struct sl_mmap* contents_shm_mmap;
int has_role;
int has_output;
uint32_t last_event_serial;
struct sl_output_buffer* current_buffer;
struct wl_list released_buffers;
struct wl_list busy_buffers;
};
struct sl_host_buffer {
struct wl_resource* resource;
struct wl_buffer* proxy;
uint32_t width;
uint32_t height;
struct sl_mmap* shm_mmap;
uint32_t shm_format;
struct sl_sync_point* sync_point;
};
struct sl_data_source_send_request {
int fd;
xcb_intern_atom_cookie_t cookie;
struct sl_data_source* data_source;
struct wl_list link;
};
struct sl_subcompositor {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
};
struct sl_shell {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
};
struct sl_output {
struct sl_context* ctx;
uint32_t id;
uint32_t version;
struct sl_global* host_global;
struct wl_list link;
};
struct sl_host_output {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_output* proxy;
struct zaura_output* aura_output;
int internal;
int x;
int y;
int physical_width;
int physical_height;
int subpixel;
char* make;
char* model;
int transform;
uint32_t flags;
int width;
int height;
int refresh;
int scale_factor;
int current_scale;
int preferred_scale;
int device_scale_factor;
int expecting_scale;
struct wl_list link;
};
struct sl_host_seat {
struct sl_seat* seat;
struct wl_resource* resource;
struct wl_seat* proxy;
};
struct sl_accelerator {
struct wl_list link;
uint32_t modifiers;
xkb_keysym_t symbol;
};
struct sl_keyboard_extension {
struct sl_context* ctx;
uint32_t id;
struct zcr_keyboard_extension_v1* internal;
};
struct sl_data_device_manager {
struct sl_context* ctx;
uint32_t id;
uint32_t version;
struct sl_global* host_global;
struct wl_data_device_manager* internal;
};
struct sl_data_offer {
struct sl_context* ctx;
struct wl_data_offer* internal;
struct wl_array atoms; // Contains xcb_atom_t
struct wl_array cookies; // Contains xcb_intern_atom_cookie_t
};
struct sl_text_input_manager {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
struct zwp_text_input_manager_v1* internal;
};
struct sl_viewporter {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_viewporter_global;
struct wp_viewporter* internal;
};
struct sl_xdg_shell {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
struct zxdg_shell_v6* internal;
};
struct sl_aura_shell {
struct sl_context* ctx;
uint32_t id;
uint32_t version;
struct sl_global* host_gtk_shell_global;
struct zaura_shell* internal;
};
struct sl_linux_dmabuf {
struct sl_context* ctx;
uint32_t id;
uint32_t version;
struct sl_global* host_drm_global;
struct zwp_linux_dmabuf_v1* internal;
};
struct sl_global {
struct sl_context* ctx;
const struct wl_interface* interface;
uint32_t name;
uint32_t version;
void* data;
wl_global_bind_func_t bind;
struct wl_list link;
};
struct sl_host_registry {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_list link;
};
typedef void (*sl_begin_end_access_func_t)(int fd);
struct sl_mmap {
int refcount;
int fd;
void* addr;
size_t size;
size_t bpp;
size_t num_planes;
size_t offset[2];
size_t stride[2];
size_t y_ss[2];
sl_begin_end_access_func_t begin_write;
sl_begin_end_access_func_t end_write;
struct wl_resource* buffer_resource;
};
typedef void (*sl_sync_func_t)(struct sl_context *ctx,
struct sl_sync_point* sync_point);
struct sl_sync_point {
int fd;
sl_sync_func_t sync;
};
struct sl_config {
uint32_t serial;
uint32_t mask;
uint32_t values[5];
uint32_t states_length;
uint32_t states[3];
};
struct sl_window {
struct sl_context* ctx;
xcb_window_t id;
xcb_window_t frame_id;
uint32_t host_surface_id;
int unpaired;
int x;
int y;
int width;
int height;
int border_width;
int depth;
int managed;
int realized;
int activated;
int allow_resize;
xcb_window_t transient_for;
xcb_window_t client_leader;
int decorated;
char* name;
char* clazz;
char* startup_id;
int dark_frame;
uint32_t size_flags;
int min_width;
int min_height;
int max_width;
int max_height;
struct sl_config next_config;
struct sl_config pending_config;
struct zxdg_surface_v6* xdg_surface;
struct zxdg_toplevel_v6* xdg_toplevel;
struct zxdg_popup_v6* xdg_popup;
struct zaura_surface* aura_surface;
struct wl_list link;
};
struct sl_host_buffer* sl_create_host_buffer(struct wl_client* client,
uint32_t id,
struct wl_buffer* proxy,
int32_t width,
int32_t height);
struct sl_global* sl_global_create(struct sl_context* ctx,
const struct wl_interface* interface,
int version,
void* data,
wl_global_bind_func_t bind);
struct sl_global* sl_compositor_global_create(struct sl_context* ctx);
size_t sl_shm_bpp_for_shm_format(uint32_t format);
size_t sl_shm_num_planes_for_shm_format(uint32_t format);
struct sl_global* sl_shm_global_create(struct sl_context* ctx);
struct sl_global* sl_subcompositor_global_create(struct sl_context* ctx);
struct sl_global* sl_shell_global_create(struct sl_context* ctx);
double sl_output_aura_scale_factor_to_double(int scale_factor);
void sl_output_send_host_output_state(struct sl_host_output* host);
struct sl_global* sl_output_global_create(struct sl_output* output);
struct sl_global* sl_seat_global_create(struct sl_seat* seat);
struct sl_global* sl_relative_pointer_manager_global_create(
struct sl_context* ctx);
struct sl_global* sl_data_device_manager_global_create(struct sl_context* ctx);
struct sl_global* sl_viewporter_global_create(struct sl_context* ctx);
struct sl_global* sl_xdg_shell_global_create(struct sl_context* ctx);
struct sl_global* sl_gtk_shell_global_create(struct sl_context* ctx);
struct sl_global* sl_drm_global_create(struct sl_context* ctx);
struct sl_global* sl_text_input_manager_global_create(struct sl_context* ctx);
void sl_set_display_implementation(struct sl_context* ctx);
struct sl_mmap* sl_mmap_create(int fd,
size_t size,
size_t bpp,
size_t num_planes,
size_t offset0,
size_t stride0,
size_t offset1,
size_t stride1,
size_t y_ss0,
size_t y_ss1);
struct sl_mmap* sl_mmap_ref(struct sl_mmap* map);
void sl_mmap_unref(struct sl_mmap* map);
struct sl_sync_point* sl_sync_point_create(int fd);
void sl_sync_point_destroy(struct sl_sync_point* sync_point);
void sl_host_seat_added(struct sl_host_seat* host);
void sl_host_seat_removed(struct sl_host_seat* host);
void sl_restack_windows(struct sl_context* ctx, uint32_t focus_resource_id);
void sl_roundtrip(struct sl_context* ctx);
int sl_process_pending_configure_acks(struct sl_window* window,
struct sl_host_surface* host_surface);
void sl_window_update(struct sl_window* window);
#endif // VM_TOOLS_SOMMELIER_SOMMELIER_H_

View File

@ -0,0 +1,34 @@
# Caution!: GYP to GN migration is happening. If you update this file, please
# update vm_tools/sommelier/wayland-protocol.gni too accordingly.
{
'variables': {
'wayland_dir': '<(SHARED_INTERMEDIATE_DIR)/<(wayland_out_dir)',
'wayland_in_dir%': '.',
},
'rules': [
{
'rule_name': 'genwayland',
'extension': 'xml',
'outputs': [
'<(wayland_dir)/<(RULE_INPUT_ROOT)-protocol.c',
'<(wayland_dir)/<(RULE_INPUT_ROOT)-client-protocol.h',
'<(wayland_dir)/<(RULE_INPUT_ROOT)-server-protocol.h',
],
'action': [
'sh',
'-c',
'wayland-scanner code < <(wayland_in_dir)/<(RULE_INPUT_NAME) > <(wayland_dir)/<(RULE_INPUT_ROOT)-protocol.c; wayland-scanner client-header < <(wayland_in_dir)/<(RULE_INPUT_NAME) > <(wayland_dir)/<(RULE_INPUT_ROOT)-client-protocol.h; wayland-scanner server-header < <(wayland_in_dir)/<(RULE_INPUT_NAME) > <(wayland_dir)/<(RULE_INPUT_ROOT)-server-protocol.h',
],
'message': 'Generating Wayland C code from <(RULE_INPUT_PATH)',
'process_outputs_as_sources': 1,
},
],
# This target exports a hard dependency because it generates header
# files.
'hard_dependency': 1,
'direct_dependent_settings': {
'include_dirs': [
'<(wayland_dir)',
],
},
}

View File

@ -0,0 +1,65 @@
# Copyright 2019 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# GN template to generate static library for given Wayland protorol description files.
# Parameters:
# sources
# Wayland protocol description XML file paths.
# out_dir (optional)
# Directory to output generated source files. Relative to gen/ directory.
template("wayland_protocol_library") {
forward_variables_from(invoker, [ "out_dir" ])
if (!defined(out_dir)) {
out_dir = "."
}
wayland_dir = "${root_gen_dir}/${out_dir}"
generators = [
{
subcommand = "code"
output_suffix = "-protocol.c"
},
{
subcommand = "client-header"
output_suffix = "-client-protocol.h"
},
{
subcommand = "server-header"
output_suffix = "-server-protocol.h"
},
]
generator_actions = []
foreach(g, generators) {
action_name = "${target_name}_${g.subcommand}"
generator_actions += [ ":" + action_name ]
action_foreach(action_name) {
sources = invoker.sources
script = "//common-mk/file_generator_wrapper.py"
output_file = "${wayland_dir}/{{source_name_part}}${g.output_suffix}"
outputs = [
output_file,
]
args = [
"wayland-scanner",
g.subcommand,
"{{source}}",
output_file,
]
}
}
static_library(target_name) {
if (defined(invoker.configs)) {
configs += invoker.configs
}
deps = generator_actions
sources = []
foreach(t, deps) {
sources += get_target_outputs(t)
}
if (defined(invoker.deps)) {
deps += invoker.deps
}
}
}